Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

Wednesday, October 3, 12

Slide 3

Slide 3 text

WHY LEARN EMBER.JS? “It's not Ember that's hard. It's the concepts.” - http://trek.github.com/ Wednesday, October 3, 12

Slide 4

Slide 4 text

Wednesday, October 3, 12

Slide 5

Slide 5 text

GAME PLAN: •Ember Objects & Templates •Ember Views & Controllers •Ember Routers / State Managers •Strengths / Weaknesses Wednesday, October 3, 12

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

EM.OBJECT The Core of Ember.js Wednesday, October 3, 12

Slide 8

Slide 8 text

EM.OBJECT - FEATURES •Computed Properties •Observers •Bindings •Auto-Updating Templates Wednesday, October 3, 12

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

GETTING/SETTING user.set("firstName", "Benjamin"); user.setProperties({ firstName: "Benjamin" }); user.get("firstName"); // "Ben" user.get("lastName"); // "Hughes" Wednesday, October 3, 12

Slide 14

Slide 14 text

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("[email protected]") }); project.get("remaining"); # 1 Wednesday, October 3, 12

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

TEMPLATES Auto-updating templates bound to objects with Handlebars.js Wednesday, October 3, 12

Slide 19

Slide 19 text

JAVASCRIPT REQUIREMENTS Wednesday, October 3, 12

Slide 20

Slide 20 text

HANDLEBARS TEMPLATES • Placed in • Can be named (stored for later use), or unnamed (evaluate immediately in-line) • Deliberately limited semantics (supports auto-updating) Wednesday, October 3, 12

Slide 21

Slide 21 text

Hello, {{App.user.fullName}} App.user.set("firstName", "Benjamin"); App.user = App.User.create({firstName: "Ben", lastName: "Hughes"}); BASIC EXAMPLE Wednesday, October 3, 12

Slide 22

Slide 22 text

{{#with App.user}} Hello, {{fullName}} {{/with}} CONTEXT WITH “WITH” Wednesday, October 3, 12

Slide 23

Slide 23 text

Hello, Ben Hughes
UNDER THE HOOD Wednesday, October 3, 12

Slide 24

Slide 24 text

App.user = App.User.create(); {{#if App.user.lastName}} Hello, {{App.user.fullName}} {{else}} Sorry, not found. {{/if}} App.user.setProperties({firstName: "Ben", lastName: "Hughes"}); Wednesday, October 3, 12

Slide 25

Slide 25 text

App.company = Ember.Object.create({ people: [ { name: 'Yehuda' }, { name: 'Tom' } ] }); <ul> {{#each App.company.people}} <li>Hello, {{name}}!</li> {{/each}} </ul> Wednesday, October 3, 12

Slide 26

Slide 26 text

BINDING ATTRIBUTES
Logo
Logo
Wednesday, October 3, 12

Slide 27

Slide 27 text

ASYNCHRONOUS LOADING Ember’s approach to a difficult problem... Wednesday, October 3, 12

Slide 28

Slide 28 text

TRADITIONAL APPROACH jQuery.getJSON('/posts/1', function(post) { $("#post").html( "

" + post.title + "

" + "
" + post.body + "
" ); }); Wednesday, October 3, 12

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

LOADING USER VIA AJAX Hello, {{App.user.fullName}}. Your username is: {{App.user.username}}. App.user = App.User.find("rubiety"); Wednesday, October 3, 12

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

ONLY DISPLAY IF LOADED App.user = App.User.find("rubiety"); {{#if App.user.isLoaded}} Hello, {{App.user.fullName}}. Your username is: {{App.user.username}}. {{else}} Loading user... {{/if}} Wednesday, October 3, 12

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

EMBER-DATA A client-side ORM for Ember. Not covering, but more info here: https://github.com/emberjs/data Wednesday, October 3, 12

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

Wednesday, October 3, 12

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

EM.VIEW Wednesday, October 3, 12

Slide 40

Slide 40 text

NAMED VIEWS Hello, <b>{{name}}</b> 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

Slide 41

Slide 41 text

VIEWS INSIDE ANOTHER VIEW <h1>My Application</h1> {{#view App.SayHelloView name="Bob"}} Hello, <b>{{name}}</b> App.SayHelloView = Ember.View.extend({ templateName: 'say-hello' }); Wednesday, October 3, 12

Slide 42

Slide 42 text

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(); Hello, <b>{{user.fullName}}</b> App.user.set({firstName: "Benjamin"}); // Hello, Benjamin Hughes Wednesday, October 3, 12

Slide 43

Slide 43 text

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(); Hello, <b>{{fullName}}</b> Wednesday, October 3, 12

Slide 44

Slide 44 text

EVENTS WITH {{ACTION}} {{#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}} 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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

EM.CONTROLLER Controlling access to your model data... Wednesday, October 3, 12

Slide 47

Slide 47 text

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" }); <ul> {{#each controller}} <li>{{firstName}} {{lastName}}</li> {{/each}} </ul> App.usersView.append(); Wednesday, October 3, 12

Slide 48

Slide 48 text

<ul> {{#each controller}} <li>{{firstName}} {{lastName}}</li> {{/each}} </ul> 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

Slide 49

Slide 49 text

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", }); Hi, {{firstName}} {{lastName}} Wednesday, October 3, 12

Slide 50

Slide 50 text

EM.ROUTER Managing State Covering only briefly - more information at: http://trek.github.com/ Wednesday, October 3, 12

Slide 51

Slide 51 text

<h1>My Application</h1> {{outlet}} <h2>Users</h2> ... <h2>Projects</h2> ... 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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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: <ul> {{#each controller}} <li>{{firstName}} {{lastName}}</li> {{/each}} </ul> Wednesday, October 3, 12

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

CATPRINT ORDER BUILDER Wednesday, October 3, 12

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

QUESTIONS? Ben Hughes http://benhugh.es @rubiety http://emberjs.com/ http://emberjs.com/guides/ http://trek.github.com/ Wednesday, October 3, 12