introduction-to-embercli



introduction-to-embercli

0 0


introduction-to-embercli

Ember CLI Slides for Indianapolis Meetup

On Github jheth / introduction-to-embercli

Introduction to ember-cli

Joe Heth

@jheth

http://github.com/jheth

http://www.ember-cli.com/

What is it?

  • Command Line Utility (ember new [project])
  • Conventional Project Structure
  • Asset Pipeline (broccoli)
  • Based on EmberAppKit project (deprecated)
Ember is largely based on Rails conventions (project creation, generators, file structure)

Asset Pipeline

Powered By Broccoli (Beta)

  • Handlebars, Emblem
  • LESS, SASS, Compass, Stylus
  • CoffeeScript, EmberScript
  • Minified JS & CSS
  • Additional Plugins
A fast, reliable asset pipeline, supporting constant-time rebuilds and compact build definitions. Comparable to the Rails asset pipeline in scope, though it runs on Node and is backend-agnostic.

Installation

Requires NodeJS

Ember CLI

npm install -g ember-cli

Bower

npm install -g bower

PhantomJS

npm install -g phantomjs
Bower - Package Manager for front-end dependencies up to date. PhantomJS used by default for integration testing. brew install node npm install -g grunt-cli

New Project

ember new blog
version: 0.1.2
installing
  create .bowerrc
  create .editorconfig
  create .ember-cli
  create .jshintrc
  create .travis.yml
  create Brocfile.js
  create README.md
  create app/app.js
  create app/components/.gitkeep
  create app/controllers/.gitkeep
  create app/helpers/.gitkeep
  create app/index.html
  create app/models/.gitkeep
  create app/router.js
  create app/routes/.gitkeep
  create app/styles/.gitkeep
  create app/styles/app.css
  create app/templates/.gitkeep
  create app/templates/application.hbs
  create app/templates/components/.gitkeep
  create app/views/.gitkeep
  create bower.json
  create config/environment.js
  create .gitignore
  create package.json
  create public/.gitkeep
  create public/crossdomain.xml
  create public/robots.txt
  create testem.json
  create tests/.jshintrc
  create tests/helpers/resolver.js
  create tests/helpers/start-app.js
  create tests/index.html
  create tests/test-helper.js
  create tests/unit/.gitkeep
  create vendor/.gitkeep
Installed packages for tooling via npm.
Installed browser packages via Bower.
Successfully initialized git.
Show Brocfile.js, bower.json, package.json, testem.json

Project Structure

File/folder Purpose app/app.js Your application’s entry point. This is the module that is first executed. app/index.html The only actual page of your single-page app! Includes dependencies and kickstarts your Ember application. app/router.js Your route configuration. The routes defined here correspond to routes in app/routes/. app/styles/ Contains your stylesheets, whether SASS, LESS, Stylus, Compass, or plain CSS (though only one type is allowed, see Asset Compilation). These are all compiled into app.css. app/templates/ Your Handlebars templates. These are compiled to templates.js. The templates are named the same as their filename, minus the extension (i.e. templates/foo/bar.hbs -> foo/bar). app/controllers/ app/models/ Modules resolved by the Ember CLI resolver.

Running the Server

cd blog
ember server
version: 0.1.2
Livereload server on port 35729
Serving on http://0.0.0.0:4200/

Build successful - 372ms.

Slowest Trees                  | Total
-------------------------------+----------------
Concat                         | 96ms
ES3SafeFilter                  | 36ms
ES6Concatenator                | 33ms
JSHint - App                   | 31ms
CustomStaticCompiler           | 20ms
TreeMerger (appAndDependencies) | 19ms
Node's Express Server, running on port 4200 LiveReload Broccoli build output

Live Coding

  • Create Routes, Controller, Model
  • Create Fixture Adapter
  • Write Handlebar templates

Generate Routes

jheth@macbook: ~/blog$ ember generate route posts
// app/routes/posts.js
import Ember from 'ember';

export default Ember.Route.extend({
  model: function() {
    return this.store.find('post');
  }
});
jheth@macbook: ~/blog$ ember generate route post
// app/routes/post.js
import Ember from 'ember';

export default Ember.Route.extend({
  model: function(params) {
    return this.store.find('post', params.post_id);
  }
});
// app/router.js
Router.map(function() {
  this.route('posts');
  this.route('post', { path: '/post/:post_id'});
});

Generator Model / Fixtures

jheth@macbook: ~/blog$ ember generate model post
// app/models/post.js
import DS from 'ember-data';

var Post = DS.Model.extend({
  title: DS.attr('string'),
  body: DS.attr('string'),
  author: DS.attr('string'),
  createdAt: DS.attr('date'),
  isPublished: DS.attr('boolean')
});

Post.reopenClass({
  FIXTURES: [
    { id: 1, title: 'Title A', body: 'Body A', author: 'Joe',
      createdAt: '2014-10-27T11:45:13', isPublished: false },
    { id: 2, title: 'Title B', body: 'Body B', author: 'Paul',
      createdAt: '2014-10-27T12:15:00', isPublished: false }
  ]
});

export default Post;

Generate Adapter

ember generate adapter post
version: 0.1.2
installing
  create app/adapters/post.js
installing
  create tests/unit/adapters/post-test.js
// app/adapters/post.js
import DS from 'ember-data';

export default DS.FixtureAdapter.extend({
});

Generate Component

jheth@macbook: ~/blog$ ember generate components blog-post
version: 0.1.2
installing
  create app/components/blog-post.js
  create app/templates/components/blog-post.hbs
installing
  create tests/unit/components/blog-post-test.js
<div class="post">
<h1>{{post.title}}</h1>
<p>{{post.body}}</p>
</div>
{{#if isEditing}}

{{input type='text' value=post.title}}

{{textarea value=post.body}} Save {{else}}

{{post.title}}

{{post.body}} Edit {{#if post.isPublished}} Unpublish {{else}} Publish {{/if}} {{/if}}

Update Templates

// app/templates/application.hbs

{{link-to 'Posts' 'posts'}}
// app/templates/post.hbs

{{blog-post post=model}}

{{outlet}}
// app/templates/posts.hbs

<h1>My Blog ({{controller.length}})</h1>
{{#each}}
  <h1>{{#link-to 'post' this }}{{title}}{{/link-to}}</h1>
  Created by {{author}} on {{createdAt}}
  {{#if isPublished}}
    <strong>PUBLISHED</strong>
  {{/if}}
  <hr>
{{/each}}
Delete Post

New Post

Title: {{input type='text' value=title}} Body: {{textarea value=body}} Author: {{input type='text' value=author}}Save {{outlet}}

Advanced Component

export default Ember.Component.extend({
  isEditing: false,
  actions: {
    edit: function() {
      this.set('isEditing', true);
    },
    save: function() {
      this.set('isEditing', false);
    },
    publish: function() {
      this.set('post.isPublished', true);
    },
    unpublish: function() {
      this.set('post.isPublished', false);
    }
  }
});

Advanced Component Template

<div class="post">
  {{#if isEditing}}
    <h1>{{input type='text' value=post.title}}</h1>
    {{textarea value=post.body}}
    <br>
    <button {{action 'save'}}>Save</button>
  {{else}}
    <h1>{{post.title}}</h1>
    {{post.body}}
    <br>
    <button {{action 'edit'}}>Edit</button>

    {{#if post.isPublished}}
    <button {{action 'unpublish'}}>Unpublish</button>
    {{else}}
    <button {{action 'publish'}}>Publish</button>
    {{/if}}
  {{/if}}
</div>

Generate Controller

jheth@macbook: ~/blog$ ember generate controllers posts --type=array
import Ember from 'ember';

export default Ember.ArrayController.extend({
  actions: {
    createPost: function() {
      this.store.createRecord('post', {
        id: Math.floor((Math.random() * 100) + 1),
        title: this.get('title'),
        body: this.get('body'),
        author: this.get('author'),
        createdAt: new Date().toISOString(),
        isPublished: false
      });

      this.set('title', '');
      this.set('body', '');
      this.set('author', '');
    },
    deletePost: function(post) {
      post.destroyRecord();
    }
  }
});

Testing

jheth@macbook: ~/blog (master) $ ember test
version: 0.1.2
Built project successfully. Stored in "/Users/jheth/Documents/Development/blog/tmp/class-tests_dist-OJ6ax2Eb.tmp".
ok 1 PhantomJS 1.9 - JSHint - adapters: adapters/post.js should pass jshint
ok 2 PhantomJS 1.9 - JSHint - .: app.js should pass jshint
ok 3 PhantomJS 1.9 - JSHint - blog/tests/helpers: blog/tests/helpers/resolver.js should pass jshint
ok 4 PhantomJS 1.9 - JSHint - blog/tests/helpers: blog/tests/helpers/start-app.js should pass jshint
ok 5 PhantomJS 1.9 - JSHint - blog/tests: blog/tests/test-helper.js should pass jshint
ok 6 PhantomJS 1.9 - JSHint - blog/tests/unit/adapters: blog/tests/unit/adapters/post-test.js should pass jshint
ok 7 PhantomJS 1.9 - JSHint - blog/tests/unit/components: blog/tests/unit/components/blog-post-test.js should pass jshint
ok 8 PhantomJS 1.9 - JSHint - blog/tests/unit/models: blog/tests/unit/models/post-test.js should pass jshint
ok 9 PhantomJS 1.9 - JSHint - blog/tests/unit/routes: blog/tests/unit/routes/post-test.js should pass jshint
ok 10 PhantomJS 1.9 - JSHint - blog/tests/unit/routes: blog/tests/unit/routes/posts-test.js should pass jshint
ok 11 PhantomJS 1.9 - JSHint - components: components/blog-post.js should pass jshint
ok 12 PhantomJS 1.9 - JSHint - models: models/post.js should pass jshint
ok 13 PhantomJS 1.9 - JSHint - .: router.js should pass jshint
ok 14 PhantomJS 1.9 - JSHint - routes: routes/post.js should pass jshint
ok 15 PhantomJS 1.9 - JSHint - routes: routes/posts.js should pass jshint
ok 16 PhantomJS 1.9 - PostAdapter: it exists
ok 17 PhantomJS 1.9 - BlogPostComponent: it renders
ok 18 PhantomJS 1.9 - Post: it exists
ok 19 PhantomJS 1.9 - PostRoute: it exists
ok 20 PhantomJS 1.9 - PostsRoute: it exists

1..20
# tests 20
# pass  20
# fail  0

# ok