Slide 1

Slide 1 text

w e l c o m e t o t h e n e x t l e v e l

Slide 2

Slide 2 text

Ember CLI: UP AND RUNNING! The stuff you need to build next-level native web apps

Slide 3

Slide 3 text

Brandon Hays @tehviking This one guy The Frontside

Slide 4

Slide 4 text

Welcome to training

Slide 5

Slide 5 text

WHAT WE’LL COVER: ¶ Ember CLI basics ¶ Building an app with CLI ¶ Migrating to Ember-CLI

Slide 6

Slide 6 text

Prerequisites ¶ git ¶ node ¶ admin access

Slide 7

Slide 7 text

WHY ember? It’s our favorite framework, sure. But why?

Slide 8

Slide 8 text

Ember lets me focus on our unique business problems, not boilerplate code i just wanna code!

Slide 9

Slide 9 text

Let the community tackle shared problems.

Slide 10

Slide 10 text

I don’t want to spend my limited time on earth shaving yaks. WHY ember?

Slide 11

Slide 11 text

how devs see their custom build pipeline

Slide 12

Slide 12 text

their actual custom build pipeline

Slide 13

Slide 13 text

Let’s see, we have a community, a shared problem, and Ember philosophy... so what do we do?

Slide 14

Slide 14 text

An opinionated suite of command line tools for building Ember apps. Enter ember cli.

Slide 15

Slide 15 text

I get to code, not shave yaks. WHY ember cli? >

Slide 16

Slide 16 text

“You should begin moving your app to Ember CLI as soon as possible.” - Tom Dale, Javascript Ombudsman WHY ember cli?

Slide 17

Slide 17 text

So let’s get up to speed. And get back to what we like: making stuff.

Slide 18

Slide 18 text

PART 1: CLI BASICS

Slide 19

Slide 19 text

the pleasure º Generators º Fast builds º Add-on ecosystem º Testing integration º ES6, Sass support

Slide 20

Slide 20 text

THE PAIN ‡ Learning ES6 modules ‡ Cryptic error messages like “cannot find file tmp/3n8glcv7cv6sgs ‡ Migrating existing apps to Ember-CLI

Slide 21

Slide 21 text

THE PAIN ‡ Migrating existing apps to Ember-CLI

Slide 22

Slide 22 text

basic concepts - Installing - Generating - Using ES6 modules - Folder structure - Assets - Testing - Deployment

Slide 23

Slide 23 text

Installing npm install -g ember-cli ember new cool-app

Slide 24

Slide 24 text

GENERATORS & BLUEPRINTS

Slide 25

Slide 25 text

LMGTFY ember generate route images ember generate component pixelate-image and my favorite... ember generate acceptance-test images (...let me generate that for you)

Slide 26

Slide 26 text

Ember GENERATORS Controller Model Route Resource Template Mixin Component Helper Initializer Adapter Serializer Service Transform Util View

Slide 27

Slide 27 text

Ember GENERATORS Acceptance-test* Adapter-test Component-test Controller-test Helper-test Initializer-test Mixin-test Model-test Route-test Serializer-test Service-test Transform-test Util-test View-test *(All but this are generated automatically)

Slide 28

Slide 28 text

Ember GENERATORS Addon In-repo-addon (& lib) App Blueprint Http-mock Http-proxy Server Test-helper Plus custom generators from installed addons ...Or create your own

Slide 29

Slide 29 text

ES6 Modules: getting out of dependency hell

Slide 30

Slide 30 text

game over, globals!

Slide 31

Slide 31 text

App.ImagesRoute = Ember.Route.extend({
 model: function() {
 return App.imageData();
 }
 }); app/routes/images-route.js From globals...

Slide 32

Slide 32 text

App.ImagesRoute = Ember.Route.extend({
 model: function() {
 return App.imageData();
 }
 }); app/routes/images-route.js From globals... Ember is global imageData comes from global App (at least, we hope)

Slide 33

Slide 33 text

import Ember from 'ember';
 import imageData from '../utils/image-data';
 
 var ImagesRoute = Ember.Route.extend({
 model: function() {
 return imageData();
 }
 }); export default ImagesRoute; app/routes/images.js TO ES6 Modules

Slide 34

Slide 34 text

import Ember from 'ember';
 import imageData from '../utils/image-data';
 
 var ImagesRoute = Ember.Route.extend({
 model: function() {
 return imageData();
 }
 }); export default ImagesRoute; app/routes/images.js to ES6 Modules Ember comes from ember.js

Slide 35

Slide 35 text

import Ember from 'ember';
 import imageData from '../utils/image-data';
 
 var ImagesRoute = Ember.Route.extend({
 model: function() {
 return imageData();
 }
 }); export default ImagesRoute; app/routes/images.js to ES6 Modules imageData comes from ../utils

Slide 36

Slide 36 text

import Ember from 'ember';
 import imageData from '../utils/image-data';
 
 var ImagesRoute = Ember.Route.extend({
 model: function() {
 return imageData();
 }
 }); export default ImagesRoute; app/routes/images.js to ES6 Modules export so others can import

Slide 37

Slide 37 text

Opinionated framework, opinionated structure Folder structure

Slide 38

Slide 38 text

app/routes/images.js app/routes/images/show.js app/controllers/images.js app/controllers/images/show.js app/templates/images.hbs app/templates/images/show.hbs old-school folders

Slide 39

Slide 39 text

app/images/route.js app/images/controller.js app/images/template.hbs app/images/show/route.js app/images/show/controller.js app/images/show/template.hbs New-school Pods

Slide 40

Slide 40 text

ember generate resource foo --pod and if you want, in your env: podModulePrefix: 'pixelfy-me/pods' Maybe you’ll like it

Slide 41

Slide 41 text

Asset compilation Where did your files go?

Slide 42

Slide 42 text

No content

Slide 43

Slide 43 text

- assets/pixelfy-me.js (app) - assets/vendor.js (vendored js) Asset compilation

Slide 44

Slide 44 text

app.import("vendor/my-stuff/cool-utility.js"); app.import("bower_components/pixelate/pixelate.js");
 app.import("bower_components/JavaScript-MD5/js/md5.js");
 Brocfile.js VENDORING ASSETS

Slide 45

Slide 45 text

TESTING

Slide 46

Slide 46 text

TESTING Awesome test helpers Awesome test generators QUnit & Mocha support

Slide 47

Slide 47 text

Deployment

Slide 48

Slide 48 text

Build & ship to S3/CloudFront Heroku Buildpack Existing asset pipeline Deployment

Slide 49

Slide 49 text

The smartest DevOps engineer is our collective community ember cli ends the build tool yak shave

Slide 50

Slide 50 text

EMBER ROUTER & NESTED ROUTES

Slide 51

Slide 51 text

DATA, UI, STATE, URLs, IN PERFECT HARMONY

Slide 52

Slide 52 text

ApplicationRoute (generated): ‘/’ ProductRoute: ‘/:id’ Rendered into products {{outlet}} URL: / products / 230 ProductsRoute: ‘/products’ Rendered into application {{outlet}}

Slide 53

Slide 53 text

ACTION HANDLING ProductController {{action “add”}} ProductRoute ProductsRoute ApplicationRoute

Slide 54

Slide 54 text

EMBER COMPONENTS

Slide 55

Slide 55 text

EMBER COMPONENTS OF THE NEAR FUTURE

Slide 56

Slide 56 text

{{each people as |person|}} {{person.name}} {{/each}} EMBER COMPONENTS OF THE NEAR FUTURE

Slide 57

Slide 57 text

COMPUTED PROPERTIES & COMPUTED MACROS

Slide 58

Slide 58 text

COMPUTED PROPERTIES app/controllers/cart.js export default Ember.Controller.extend({
 cartItemSubtotals: Ember.computed.mapBy("model.cartItems", "subtotal"),
 orderSubtotal: Ember.computed.sum("cartItemSubtotals"),
 shippingCost: Ember.computed(function() {
 return 0;
 }),
 orderTax: Ember.computed("orderSubtotal", function() {
 var taxCents = (this.get("orderSubtotal") * 100) * 0.0825;
 return taxCents / 100;
 }),
 orderTotal: Ember.computed("orderTax", "orderSubtotal", function() {
 return this.get("orderSubtotal") + this.get("orderTax");
 }),
 });

Slide 59

Slide 59 text

COMPUTED MACROS app/controllers/cart.js export default Ember.Controller.extend({
 cartItemSubtotals: Ember.computed.mapBy("model.cartItems", "subtotal"),
 orderSubtotal: Ember.computed.sum("cartItemSubtotals"),
 shippingCost: Ember.computed(function() {
 return 0;
 }),
 orderTax: Ember.computed("orderSubtotal", function() {
 var taxCents = (this.get("orderSubtotal") * 100) * 0.0825;
 return taxCents / 100;
 }),
 orderTotal: Ember.computed("orderTax", "orderSubtotal", function() {
 return this.get("orderSubtotal") + this.get("orderTax");
 }),
 });

Slide 60

Slide 60 text

IF YOU UNDERSTAND: • Generating with Ember CLI • Router & Routes • Components • Computed Properties YOU CAN BUILD AWESOME STUFF

Slide 61

Slide 61 text

Ember CLI lets me focus on our unique business problems, not janky build systems i just wanna code!

Slide 62

Slide 62 text

so let’s code.

Slide 63

Slide 63 text

Questions & break

Slide 64

Slide 64 text

Let’s build an app! Pixelfy Me

Slide 65

Slide 65 text

look out, batman! it’s the pixeler! Shut up, Robin! Worst. Villain. Ever.

Slide 66

Slide 66 text

Let’s build an app!

Slide 67

Slide 67 text

Let’s build an app! npm install -g ember-cli

Slide 68

Slide 68 text

Let’s build an app! ember new pixelfy-me cd pixelfy-me git remote add origin https://github.com/thefrontside/pixelfy-me.git git fetch --tags open the folder in your text editor of choice

Slide 69

Slide 69 text

Task 1: List Images What we’ll do: Generate Images resource Write Model hook for Images Route Install helper for image data Display images on Images page

Slide 70

Slide 70 text

git checkout task-1 npm install bower install (Stuck? You can git checkout at any task with git checkout task-) Task 1: List Images

Slide 71

Slide 71 text

Generate Images resource ember generate resource images (NOTE: don’t overwrite that hbs file!)

Slide 72

Slide 72 text

import Ember from 'ember';
 
 export default Ember.Object.extend({
 url: ""
 });
 app/models/image.js “downgrade” model

Slide 73

Slide 73 text

import Ember from 'ember';
 
 export default Ember.Route.extend({
 model: function() {
 return imageData();
 }
 });
 
 app/routes/images.js Load data in model hook KABOOOOOM! (doesn’t exist)

Slide 74

Slide 74 text

kinda magic custom addon ember install cowboyd/ember-cli-sample-image-data

Slide 75

Slide 75 text

import Ember from 'ember'; import imageData from 'ember-cli-sample-image-data';
 
 export default Ember.Route.extend({
 model: function() {
 return imageData();
 }
 });
 
 app/routes/images.js import it in the route

Slide 76

Slide 76 text

... "8": {
 id: "8",
 url: "/img/yeezus.jpg"
 }
 };
 if(arguments.length) {
 return Image.create(images[id]);
 } else {
 var imagesArray = Object.keys(images).map(function(k){
 return Image.create(images[k]);
 });
 return imagesArray;
 }
 inside the addon’s hidden volcano lair... P.S. it’s not really that magical

Slide 77

Slide 77 text

check it out ember server check localhost:4200

Slide 78

Slide 78 text

need to fast forward? git reset --hard task-2

Slide 79

Slide 79 text

Let’s talk about what just happened: - Generated a route & model - Tweaked the route & model slightly - Installed an addon - Used ES6 to include the addon helper That loads images!

Slide 80

Slide 80 text

task 2: show image What we’ll do: Generate images/show route Add dynamic segment to show route Write Model hook for Images Show Route Link to Image from Images page Display image on Image Show section

Slide 81

Slide 81 text

Generate Images show route ember generate route images/show (again, don’t overwrite that hbs file!)

Slide 82

Slide 82 text

Router.map(function() {
 this.resource('images', function() {
 this.route('show', {path: ":id"});
 });
 });
 app/router.js Add dynamic segment

Slide 83

Slide 83 text

import Ember from 'ember';
 import imageData from 'ember-cli-sample-image-data';
 
 export default Ember.Route.extend({
 model: function(params) {
 return imageData(params.id);
 }
 }); app/routes/images/show.js Add model hook & import statement

Slide 84

Slide 84 text

{{#each image in model}}

  • 
 {{#link-to "images.show" image}}
 
 {{/link-to}}

  • 
 {{/each}}
 app/templates/images.hbs link to image from images template

    Slide 85

    Slide 85 text

    
 
 ! Implement pixelate-image here !

    
 app/templates/images/show.hbs Display image in the show template

    Slide 86

    Slide 86 text

    To recap what we just built: - Generated a nested route with / - Used a dynamic segment - Re-used ES6 for our imageData helper - Edited template files to display stuff images/show done!

    Slide 87

    Slide 87 text

    task 3: pixelatE What we’ll do: Install pixelate-image component Wrap image in pixelate component Install {{emberx-slider}} component Drop in {{x-slider}} component

    Slide 88

    Slide 88 text

    install pixelate component addon ember install cowboyd/ember-cli-pixelate-image

    Slide 89

    Slide 89 text

    need to fast forward? git reset --hard task-3

    Slide 90

    Slide 90 text

    install x-slider component addon ember install emberx-slider

    Slide 91

    Slide 91 text

    
 {{pixelate-image src=model.url value=pixelatePercentage}}

    
 app/templates/images/show.hbs Drop in pixelate-image component

    Slide 92

    Slide 92 text

    
 {{x-slider value=pixelatePercentage}}

    
 app/templates/images/show.hbs Drop in emberx-slider component

    Slide 93

    Slide 93 text

    what? that worked?! Did we mention Ember Addons are pretty great?

    Slide 94

    Slide 94 text

    Let’s look at what we did: - Installed pixelate-image custom addon - Installed emberx-slider addon - Tied the two together to make MAGIC So yeah

    Slide 95

    Slide 95 text

    task 4: Style it What we’ll do: Install ember-cli-sass addon Install bootstrap-sass bower package rename app.css to app.scss include bootstrap include custom SCSS

    Slide 96

    Slide 96 text

    install sass addon ember install ember-cli-sass

    Slide 97

    Slide 97 text

    install bootstrap-sass bower install --save-dev bootstrap-sass

    Slide 98

    Slide 98 text

    rename app.css mv app/styles/app.css app/styles/app.scss

    Slide 99

    Slide 99 text

    COPY & PASTE CSS FILES Files are here (grab the tar file): http://bit.ly/cli-training-styles - app.scss - _bootswatch.scss - bootstrap_imports.scss - bootstrap_variables.scss Copy or move them to app/styles

    Slide 100

    Slide 100 text

    need to fast forward? git reset --hard task-5

    Slide 101

    Slide 101 text

    Now we have a fully styled app! - Installed ember-cli-sass to compile Sass - Installed bootstrap-sass - Used Sass @include for granular control so stylin’

    Slide 102

    Slide 102 text

    task 5: test it Add karma, karma-cil, karma-mocha, karma-chai- plugins, karma-chrome-launcher, karma-ember- preprocessor, karma-mocha, karma-phantomjs- launcher, karma-junit-reporter to package.json Compile and install PhantomJS npm install Install ember-mocha-adapter and chai-jquery via Bower Set up a karma.conf file In your karma.conf: Customize your frameworks section in karma.conf to include mocha, chai, sinon-chai, and chai-jquery Set up a Grunt task to build your Sass on each test run Include your vendor dependencies in karma.conf Include your code in files section Debug file load order issues Add & configure handlebars karma preprocessor Then, create test-helper.js & include it in karma.conf In your test-helper.js: Configure chai and mocha defaults Set Ember.testing to true Set App.setupForTesting to true Call App.injectTestHelpers() Set up app on each test run Tear down app after each test run Ensure beforeEach and afterEach use the run loop Use the .done() callback for async beforeEach What we’ll do:

    Slide 103

    Slide 103 text

    task 5: test it j/k lol ;) ember generate acceptance-test images ember test

    Slide 104

    Slide 104 text

    ha! ha! ha! But seriously, this was real http://bit.ly/karma-sad-lol

    Slide 105

    Slide 105 text

    task 5: test it What we’ll do: Generate “images” acceptance test Remove generated Ember Data test Write an assertion Run the tests

    Slide 106

    Slide 106 text

    generate the test ember generate acceptance-test images

    Slide 107

    Slide 107 text

    remove ember data test rm tests/unit/models/image-test.js (We’re not using Ember data)

    Slide 108

    Slide 108 text

    test('visiting /images', function(assert) {
 visit('/images');
 
 andThen(function() {
 assert.equal(Ember.$(".spec-gallery-image").length, 8);
 });
 });
 tests/acceptance/images-test.js basic acceptance test

    Slide 109

    Slide 109 text

    run tests $ ember test --serve 1..14 # tests 14 # pass 14 # fail 0 # ok

    Slide 110

    Slide 110 text

    WE DID IT!

    Slide 111

    Slide 111 text

    look at all the yaks i have to shave

    Slide 112

    Slide 112 text

    No content

    Slide 113

    Slide 113 text

    bonus: gravatars What we’ll do: git checkout bonus-stage Set up gravatar route & link Handle download action install JavaScript-md5 bower component Pixelate images in the gravatar template

    Slide 114

    Slide 114 text

    set up gravatar route ember generate route gravatar

    Slide 115

    Slide 115 text

    downloadImage: function() {
 var link = document.createElement("a");
 var uri = Ember.$("img.pixelated-image").attr("src");
 link.download = "pixelfy.png";
 link.href = uri;
 link.click();
 }
 app/routes/gravatar.js handle download image

    Slide 116

    Slide 116 text

    {{#link-to "gravatar" tagName="li"}}
 {{#link-to "gravatar"}}
 Gravatar
 {{/link-to}}
 {{/link-to}}
 app/templates/application.hbs Link to gravatar route

    Slide 117

    Slide 117 text

    Install Md5 package bower install --save-dev JavaScript-MD5

    Slide 118

    Slide 118 text

    var app = new EmberApp();
 
 app.import("bower_components/JavaScript-MD5/js/md5.js");
 
 module.exports = app.toTree();
 Brocfile.js import md5 in brocfile

    Slide 119

    Slide 119 text

    window", "-Promise", "md5" ], "browser": true, ...
 Brocfile.js add md5 to .jshintrc

    Slide 120

    Slide 120 text

    Preview:

    
 app/templates/gravatar.hbs Add gravatar to template

    Slide 121

    Slide 121 text

    
 {{x-slider value=pixelatePercentage}}

    
 app/templates/gravatar.hbs Add slider to template

    Slide 122

    Slide 122 text

    
 {{pixelate-image src=base64Url value=pixelatePercentage}}

    
 app/templates/gravatar.hbs Add pixelate to template

    Slide 123

    Slide 123 text

    This was more complicated. - Set up gravatar route - Handled download action - Installed bower component - Included lib in Brocfile and .jshintrc - Mixed pixelate and gravatar together It’s done!

    Slide 124

    Slide 124 text

    Now we can get back to focusing on moving fast and shipping, with a better set of tools i just wanna code!

    Slide 125

    Slide 125 text

    @tehviking THANKS! The Frontside

    Slide 126

    Slide 126 text

    always remember!