Introduction to Ember.js

92772ff5353c89d9bd10f8e334161e16?s=47 Ben Hughes
October 02, 2012

Introduction to Ember.js

Getting started with Ember.js.

92772ff5353c89d9bd10f8e334161e16?s=128

Ben Hughes

October 02, 2012
Tweet

Transcript

  1. INTRODUCTION TO EMBER.JS Ben Hughes SDJS, October 2012 http://benhugh.es @rubiety

    Wednesday, October 3, 12
  2. Wednesday, October 3, 12

  3. WHY LEARN EMBER.JS? “It's not Ember that's hard. It's the

    concepts.” - http://trek.github.com/ Wednesday, October 3, 12
  4. Wednesday, October 3, 12

  5. GAME PLAN: •Ember Objects & Templates •Ember Views & Controllers

    •Ember Routers / State Managers •Strengths / Weaknesses Wednesday, October 3, 12
  6. CONVENTIONS: •Top-level Application Namespace (Object) “App” •Classes are upper-case properties

    (App.User) •Instances are lower-case properties (App.user) •Camel-casing Wednesday, October 3, 12
  7. EM.OBJECT The Core of Ember.js Wednesday, October 3, 12

  8. EM.OBJECT - FEATURES •Computed Properties •Observers •Bindings •Auto-Updating Templates Wednesday,

    October 3, 12
  9. EM.OBJECT - CLASSES App.User = Ember.Object.extend({ firstName: null, lastName: null

    }); var user = App.User.create({ firstName: "Ben", lastName: "Hughes" }); user.get("firstName"); # "Ben" user.get("lastName"); # "Hughes" Wednesday, October 3, 12
  10. EM.OBJECT - INSTANCES var user = Ember.Object.create({ firstName: "Ben", lastName:

    "Hughes" }); Wednesday, October 3, 12
  11. WITHOUT CLASS var user = Ember.Object.create({ firstName: "Ben", lastName: "Hughes"

    }); Wednesday, October 3, 12
  12. COMPUTED PROPERTIES App.User = Ember.Object.extend({ firstName: null, lastName: null, fullName:

    function() { return this.get("firstName") + " " + this.get("lastName"); }.property("firstName", "lastName") }); var user = App.User.create({ firstName: "Ben", lastName: "Hughes" }); Wednesday, October 3, 12
  13. GETTING/SETTING user.set("firstName", "Benjamin"); user.setProperties({ firstName: "Benjamin" }); user.get("firstName"); // "Ben"

    user.get("lastName"); // "Hughes" Wednesday, October 3, 12
  14. ADVANCED COMPUTED PROPERTIES var project = Ember.Object.create({ todos: [ Ember.Object.create({done:

    false}) ], remaining: function() { return this.get("todos").filterProperty('done', false).get("length"); }.property("todos.@each.done") }); project.get("remaining"); # 1 Wednesday, October 3, 12
  15. OBSERVERS // Explicit: user.addObserver("fullName", function() { alert("Changed Full Name"); });

    user.set("firstName", "Benjamin"); // Implicit: App.User.reopen({ fullNameChanged: function() { alert("Changed Full Name"); }.observes('fullName') }); Wednesday, October 3, 12
  16. BINDINGS App.wife = Ember.Object.create({ householdIncome: 80000 }); App.husband = Ember.Object.create({

    householdIncomeBinding: "App.wife.householdIncome" }); App.husband.get("householdIncome"); // 80000 App.husband.set("householdIncome", 90000); App.wife.get("householdIncome"); // 90000 Wednesday, October 3, 12
  17. FEATURES GUIDE •Use Computed Properties to create a new property

    synthesizing other properties. •Use Observers to react to changes in another property. •Use Bindings to ensure objects in two different layers are always in sync. Wednesday, October 3, 12
  18. TEMPLATES Auto-updating templates bound to objects with Handlebars.js Wednesday, October

    3, 12
  19. JAVASCRIPT REQUIREMENTS <script src="js/jquery.js"></script> <script src="js/handlebars.js"></script> <script src="js/ember.js"></script> Wednesday, October

    3, 12
  20. HANDLEBARS TEMPLATES • Placed in <script type=”text/x-handlebars”> • Can be

    named (stored for later use), or unnamed (evaluate immediately in-line) • Deliberately limited semantics (supports auto-updating) Wednesday, October 3, 12
  21. <html> <body> <script type="text/x-handlebars"> Hello, {{App.user.fullName}} </script> </body> </html> App.user.set("firstName",

    "Benjamin"); App.user = App.User.create({firstName: "Ben", lastName: "Hughes"}); BASIC EXAMPLE Wednesday, October 3, 12
  22. <html> <body> <script type="text/x-handlebars"> {{#with App.user}} Hello, {{fullName}} {{/with}} </script>

    </body> </html> CONTEXT WITH “WITH” Wednesday, October 3, 12
  23. <body> <div id="ember120" class="ember-view"> Hello, <script id="metamorph-0-start" type="text/x-placeholder"></script> Ben Hughes

    <script id="metamorph-0-end" type="text/x-placeholder"></script> </div> </body> UNDER THE HOOD Wednesday, October 3, 12
  24. App.user = App.User.create(); <html> <body> <script type="text/x-handlebars"> {{#if App.user.lastName}} Hello,

    {{App.user.fullName}} {{else}} Sorry, not found. {{/if}} </script> </body> </html> App.user.setProperties({firstName: "Ben", lastName: "Hughes"}); Wednesday, October 3, 12
  25. App.company = Ember.Object.create({ people: [ { name: 'Yehuda' }, {

    name: 'Tom' } ] }); <script type="text/x-handlebars"> <ul> {{#each App.company.people}} <li>Hello, {{name}}!</li> {{/each}} </ul> </script> Wednesday, October 3, 12
  26. BINDING ATTRIBUTES <div id="logo"> <img {{bindAttr src="logoUrl"}} alt="Logo"> </div> <div

    id="logo"> <img src="http://www.mycorp.com/images/logo.png" alt="Logo"> </div> Wednesday, October 3, 12
  27. ASYNCHRONOUS LOADING Ember’s approach to a difficult problem... Wednesday, October

    3, 12
  28. TRADITIONAL APPROACH jQuery.getJSON('/posts/1', function(post) { $("#post").html( "<h1>" + post.title +

    "</h1>" + "<div>" + post.body + "</div>" ); }); Wednesday, October 3, 12
  29. EMBER’S APPROACH “In general, Ember's goal is to eliminate explicit

    forms of asynchronous behavior. As we'll see later, this gives Ember the ability to coalesce multiple events that have the same result. It also provides a higher level of abstraction, eliminating the need to manually register and unregister event listeners to perform most common tasks.” Wednesday, October 3, 12
  30. LOADING USER VIA AJAX <script type="text/x-handlebars"> Hello, {{App.user.fullName}}. Your username

    is: {{App.user.username}}. </script> App.user = App.User.find("rubiety"); Wednesday, October 3, 12
  31. WRONG WAY App.User.reopenClass({ find: function(username) { $.ajax({ url: '/users/' +

    username, dataType: 'json', success: function(response) { return response.data; } }); } }); App.User.find("rubiety"); Wednesday, October 3, 12
  32. RIGHT WAY App.User.reopenClass({ find: function(username) { var user = App.User.create({

    username: username }); $.ajax({ url: '/users/' + username, dataType: 'json', success: function(response) { user.setProperties(response.data); } }); return user; } }); App.User.find("rubiety"); Wednesday, October 3, 12
  33. ONLY DISPLAY IF LOADED App.user = App.User.find("rubiety"); <script type="text/x-handlebars"> {{#if

    App.user.isLoaded}} Hello, {{App.user.fullName}}. Your username is: {{App.user.username}}. {{else}} Loading user... {{/if}} </script> Wednesday, October 3, 12
  34. ONLY DISPLAY IF LOADED App.User.reopenClass({ find: function(username) { var user

    = App.User.create({ username: username }); $.ajax({ url: '/users/' + username, dataType: 'json', success: function(response) { user.setProperties(response.data); user.set("isLoaded", true); } }); return user; } }); App.User.find("rubiety"); Wednesday, October 3, 12
  35. EMBER-DATA A client-side ORM for Ember. Not covering, but more

    info here: https://github.com/emberjs/data Wednesday, October 3, 12
  36. VIEWS & CONTROLLERS Higher-level constructs for MVC design You an

    do quite a lot just with auto-updating templates and Ember.Object, but Ember supports a wealth of higher-level components for complex UI engineering Wednesday, October 3, 12
  37. Wednesday, October 3, 12

  38. MVC COMPONENTS • Model: Em.Object (or DS.Model) describes application structure,

    including relationships between models. • View: Em.View encapsulates templates and manages events. Usually corresponds to a named Handlebars.js template. • Controller: Mediate’s views access to model. • Em.ArrayController proxies to array data • Em.ObjectController proxies to a single object • State Manager: (Routers) Act as a map of your application and handle transitions as a user moves through it. Wednesday, October 3, 12
  39. EM.VIEW Wednesday, October 3, 12

  40. NAMED VIEWS <html> <head> <script type="text/x-handlebars" data-template-name="say-hello"> Hello, <b>{{name}}</b> </script>

    </head> </html> App.SayHelloView = Ember.View.extend({ templateName: 'say-hello' }); var view = App.SayHelloView.create({name: "Bob"}); view.appendTo("#container"); // or view.append(); (appends to body) view.remove(); Wednesday, October 3, 12
  41. VIEWS INSIDE ANOTHER VIEW <script type="text/x-handlebars"> <h1>My Application</h1> {{#view App.SayHelloView

    name="Bob"}} </script> <script type="text/x-handlebars" data-template-name="say-hello"> Hello, <b>{{name}}</b> </script> App.SayHelloView = Ember.View.extend({ templateName: 'say-hello' }); Wednesday, October 3, 12
  42. VIEWS BOUND TO OTHER DATA App.user = App.User.create({firstName: "Ben", lastName:

    "Hughes"}); App.SayHelloView = Ember.View.extend({templateName: 'say-hello'}); var view = App.SayHelloView.create({ userBinding: "App.user" }); view.append(); <script type="text/x-handlebars" data-template-name="say-hello"> Hello, <b>{{user.fullName}}</b> </script> App.user.set({firstName: "Benjamin"}); // Hello, <b>Benjamin Hughes</b> Wednesday, October 3, 12
  43. VIEW CONTEXT App.user = App.User.create({firstName: "Ben", lastName: "Hughes"}); App.SayHelloView =

    Ember.View.extend({templateName: 'say-hello'}); var view = App.SayHelloView.create({ userBinding: "App.user", contextBinding: "user", }); view.append(); <script type="text/x-handlebars" data-template-name="say-hello"> Hello, <b>{{fullName}}</b> </script> Wednesday, October 3, 12
  44. EVENTS WITH {{ACTION}} <script type="text/x-handlebars" data-template-name="user"> {{#if isEditing}} First Name:

    {{view Ember.TextField valueBinding="user.firstName"}} Last Name: {{view Ember.TextField valueBinding="user.lastName"}} <a href="#" {{action "show"}}>Show User</a> {{else}} <h1>Showing User {{user.fullName}}.</h1> <a href="#" {{action "edit"}}>Edit User</a> {{/if}} </script> App.UserView = Ember.View.extend({ templateName: "user", userBinding: "App.user" edit: function(e) { this.set("isEditing", true); }, show: function(e) { this.set("isEditing", false); } }); Wednesday, October 3, 12
  45. OTHER VIEW FEATURES (NOT COVERED) • Modifying a View’s HTML

    (Generated Tag) • Custom Handlebars Helpers • View Nesting / Hierarchy • Event propagation through View Hierarchy • {{action}} targeting another view or controller/router • View Components (Controls for Select, TextField, etc.) • ArrayProxy and Enumerable Wednesday, October 3, 12
  46. EM.CONTROLLER Controlling access to your model data... Wednesday, October 3,

    12
  47. App.usersController = Ember.ArrayController.create({ content: [ Em.Object.create({ firstName: "Ben", lastName: "Hughes"

    }), Em.Object.create({ firstName: "Oscar", lastName: "Peterson"}) ] }); App.usersView = Ember.View.create({ templateName: "users", controllerBinding: "App.usersController" }); <script type="text/x-handlebars" data-template-name="users"> <ul> {{#each controller}} <li>{{firstName}} {{lastName}}</li> {{/each}} </ul> </script> App.usersView.append(); Wednesday, October 3, 12
  48. <script type="text/x-handlebars" data-template-name="users"> <ul> {{#each controller}} <li>{{firstName}} {{lastName}}</li> {{/each}} </ul>

    </script> App.usersView.append(); App.users = [ Em.Object.create({ firstName: "Ben", lastName: "Hughes" }), Em.Object.create({ firstName: "Oscar", lastName: "Peterson"}), Em.Object.create({ firstName: "Kenny", lastName: "Barron"}), ]; App.usersController = Ember.ArrayController.create({ contentBinding: "App.users" }); App.usersView = Ember.View.create({ templateName: "users", controllerBinding: "App.usersController" }); Wednesday, October 3, 12
  49. App.user = Em.Object.create({ firstName: "Ben", lastName: "Hughes" }); App.userController =

    Ember.ObjectController.create({ contentBinding: "App.user" }); App.userView = Ember.View.create({ templateName: "user", controllerBinding: "App.userController", contextBinding: "controller", }); <script type="text/x-handlebars" data-template-name="user"> Hi, {{firstName}} {{lastName}} </script> Wednesday, October 3, 12
  50. EM.ROUTER Managing State Covering only briefly - more information at:

    http://trek.github.com/ Wednesday, October 3, 12
  51. <script type="text/x-handlebars" data-template-name="application"> <h1>My Application</h1> {{outlet}} </script> <script type="text/x-handlebars" data-template-name="users">

    <h2>Users</h2> ... </script> <script type="text/x-handlebars" data-template-name="projects"> <h2>Projects</h2> ... </script> App = Ember.Application.create(); App.ApplicationController = Ember.Controller.extend(); App.ApplicationView = Ember.View.extend(); App.UsersController = Ember.ArrayController.extend(); App.UsersView = Ember.View.extend(); App.ProjectsController = Ember.ArrayController.extend(); App.ProjectsView = Ember.View.extend(); Wednesday, October 3, 12
  52. App.Router = Ember.Router.extend({ root: Ember.Route.extend({ index: Ember.Route.extend({ route: "/", redirectsTo:

    "users" }); users: Ember.route.extend({ route: "/users", connectOutlets: function(router) { router.get("applicationController").connectOutlet("users", App.User.all()); } }); projects: Ember.route.extend({ route: "/projects", connectOutlets: function(router) { router.get("applicationController").connectOutlet("projects", App.Project.all()); } }) }); }); App.initialize(); // Now Available: App.router App.router.applicationController, App.router.usersController, App.router.projectsController Wednesday, October 3, 12
  53. router.get("applicationController").connectOutlet("users", App.User.all()); // The above line automatically does the following:

    // 1. New router.usersController with content = App.User.all(); router.usersController = App.UsersController.create({ content: App.User.all() }); // 2. New App.UsersView controller = router.usersController, context = controller var view = App.UsersView.create({ controller: router.usersController, context: controller }); // The view is rendered with the template (example below) // and inserted into {{outlet}} of ApplicationView: <script type="text/x-handlebars" data-template-name="users"> <ul> {{#each controller}} <li>{{firstName}} {{lastName}}</li> {{/each}} </ul> </script> Wednesday, October 3, 12
  54. OTHER ROUTER FEATURES (NOT COVERED) • Nested Routing • Routing

    with Named Parameters (/users/:user_id) • Serializing & Deserializing for State transition on # access • Wiring up view actions to router events (and state transitions) Wednesday, October 3, 12
  55. CATPRINT ORDER BUILDER Wednesday, October 3, 12

  56. EMBER STRENGTHS: • Scales well with complexity • Excellent strong

    object system throughout the library with robust data binding / dependency resolution • Robust View Hierarchy • Performant Event Delegation • Manages creating and destroying views efficiently for optimal memory management without leaks and phantom events • Excellent tools for complex UI engineering • Lean Templates with Handlebars.js Wednesday, October 3, 12
  57. EMBER WEAKNESSES: • A little too much ceremony for simple

    apps • Poor Documentation, Few non-trivial example apps available • Concepts are Difficult • A fair amount of “magic” and auto-instantiation with Controllers / Routers • Template auto-updating only works with Handlebars.js • Pre-Version 1.0, so still a bit premature, but getting there • Ember ORM (ember-data) “not production ready” • Script tag “markers” in rendered markup, and Ember IDs Wednesday, October 3, 12
  58. WHEN MIGHT YOU USE EMBER? • Complex applications with non-trivial

    nested UIs - Ember has hierarchical views and powerful state managers to make this easy • Applications with lots of data dependency/recalculation/re- rendering - Ember handles this very transparently WHEN TO NOT USE EMBER: • Applications with fairly simple views and minimal nesting / event handling Wednesday, October 3, 12
  59. QUESTIONS? Ben Hughes http://benhugh.es @rubiety http://emberjs.com/ http://emberjs.com/guides/ http://trek.github.com/ Wednesday, October

    3, 12