Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Ember: The Magic, Demystified

Ember: The Magic, Demystified

How do the names that you provide to Ember actually end up resulting in Ember looking for a given route/controller/view/template combination? How does this apply to components and their templates? This talk reviews the Ember internals to illuminate that this isn't magical, but quite straight-forward.

Robert Jackson

June 17, 2014
Tweet

More Decks by Robert Jackson

Other Decks in Programming

Transcript

  1. Ember: The Magic,
    Demystified

    View Slide

  2. Who the heck is this guy?
    ● Ember Core Team
    ● General OSS addict
    ● DockYarder
    twitter: rwjblue
    github: rjackson

    View Slide

  3. Sweet Ember Shirts

    View Slide

  4. Sweet Ember Shirts

    View Slide

  5. View Slide

  6. Ember: The Magic,
    Demystified

    View Slide

  7. Magic Naming?

    View Slide

  8. Items to review:
    ● Lookup From Routes to Templates
    ● Component Lookup

    View Slide

  9. Route Lookup

    View Slide

  10. Route Lookup
    ● Route
    ● Controller
    ● View
    ● Template

    View Slide

  11. Route Lookup
    // app/router.js
    var Router = Ember.Router.extend();
    Router.map(function() {
    this.route('posts');
    });
    export default Router;

    View Slide

  12. Route Lookup
    {{! /app/templates/posts.hbs }}
    Woot!! You found the posts route!

    View Slide

  13. Route Lookup
    http://locahost:4200/posts
    How does this:

    View Slide

  14. Route Lookup
    End up with this?

    View Slide

  15. Route Lookup
    <br/>var Klass = require('sample-app/app')['default'];<br/>window.SampleApp = Klass.create();<br/>
    Ember.Application.create

    View Slide

  16. Route Lookup
    this.setupEventDispatcher();
    this.ready(); // user hook
    this.startRouting();
    this.resolve(this);
    Ember.Application#didBecomeReady

    View Slide

  17. Route Lookup
    this.setupEventDispatcher();
    this.ready(); // user hook
    this.startRouting();
    this.resolve(this);
    Ember.Application#didBecomeReady

    View Slide

  18. Route Lookup
    this.constructor.map(Ember.K);
    this._setupRouter(router, location);
    this.handleURL(‘/posts’);
    Ember.Router#startRouting

    View Slide

  19. Route Lookup
    var dsl = DSL.map(function() {
    this.resource(
    'application', { path: "/" },
    function() { callback.call(this); });
    });
    Ember.Router.map

    View Slide

  20. Route Lookup
    var dsl = DSL.map(function() {
    this.resource(
    'application', { path: "/" },
    function() { callback.call(this); });
    });
    Ember.Router.map

    View Slide

  21. Route Lookup - Deep Dive
    Ember.Router#_setupRouter
    ● Sets `router.getHandler`
    ● Sets URL update hooks

    View Slide

  22. Route Lookup
    var R = container.lookupFactory('route:basic')
    return function(name) {
    var handler = this.container.lookup('route:' + name);
    handler.routeName = name;
    if (handler) { return handler;}
    this.container.register('route:' + name, R.extend());
    return this.container.lookup('route:' + name);
    };
    Ember.Router#_getHandlerFunction

    View Slide

  23. Route Lookup
    ● Router.map sets up the handlers
    ● router.js handles the actual route transitions
    ● New route is entered: getHandler(‘posts’)
    ● So we have a Route instance, now what?

    View Slide

  24. Route Rendering
    ● router.js invokes various async hooks
    (activate, model, redirect, etc)
    ● router.js invokes route.setup

    View Slide

  25. Route Rendering
    var controllerName = this.controllerName || this.routeName,
    controller = this.controllerFor(controllerName, true);
    if (!controller) {
    controller = this.generateController(controllerName,context);
    }
    Ember.Route#setup

    View Slide

  26. Route Rendering
    this.setupController(controller, context, transition);
    this.renderTemplate(controller, context);
    Ember.Route#setup

    View Slide

  27. Route Rendering
    var name = this.routeName;
    var templateName = this.templateName || name;
    var viewName = this.viewName || name;
    Ember.Route#render

    View Slide

  28. Route Rendering
    var view = container.lookup('view:' + viewName),
    template = view ? view.get('template') : null;
    if (!template) {
    template = container.lookup('template:' + templateName);
    }
    Ember.Route#render

    View Slide

  29. Route Rendering
    options = normalizeOptions(this, name, template, options);
    view = setupView(view, container, options);
    appendView(this, view, options);
    Ember.Route#render

    View Slide

  30. Route Lookup
    app/routes/posts.js
    app/controllers/posts.js
    app/views/posts.js
    app/templates/posts.hbs
    Files looked up:

    View Slide

  31. Route Lookup
    app/routes/posts.js
    app/controllers/posts.js
    app/views/posts.js
    app/templates/posts.hbs
    Files looked up:

    View Slide

  32. Route Lookup
    // packages/ember-routing/lib/system/router.js#L319
    // Ember.Router#_getHandlerFunction
    var handler = this.container.lookup('route:' + name);
    handler.routeName = name;
    app/routes/posts.js

    View Slide

  33. Route Lookup
    app/routes/posts.js
    app/controllers/posts.js
    app/views/posts.js
    app/templates/posts.hbs
    Files looked up:

    View Slide

  34. Route Lookup
    // packages/ember-routing/lib/system/route.js#L696
    // Ember.Route#setup
    controller = this.controllerFor(controllerName, true);
    if (!controller) {
    controller = this.generateController(controllerName,context);
    }
    app/controllers/posts.js

    View Slide

  35. Route Lookup
    app/routes/posts.js
    app/controllers/posts.js
    app/views/posts.js
    app/templates/posts.hbs
    Files looked up:

    View Slide

  36. Route Lookup
    // packages/ember-routing/lib/system/route.js#L1377
    // Ember.Route#render
    view = container.lookup('view:' + viewName),
    view = setupView(view, container, options);
    appendView(this, view, options);
    app/views/posts.js

    View Slide

  37. Route Lookup
    app/routes/posts.js
    app/controllers/posts.js
    app/views/posts.js
    app/templates/posts.hbs
    Files looked up:

    View Slide

  38. Route Lookup
    app/templates/posts.hbs
    // packages/ember-routing/lib/system/route.js#L1381
    // Ember.Route#render
    template = view ? view.get('template') : null;
    template = template || container.lookup('template:' + templateName);
    options = normalizeOptions(this, name, template, options);
    view = setupView(view, container, options);

    View Slide

  39. Component Lookup

    View Slide

  40. Component Lookup
    ● Layout
    ● Component

    View Slide

  41. // app/templates/application.hbs
    {{x-foo bar="baz"}}
    // app/templates/components/x-foo.hbs
    {{foobar}}
    Component Lookup
    Templates:

    View Slide

  42. export default Ember.Component.extend({
    foobar: Ember.computed('bar', function() {
    return 'foo' + Ember.get(this, 'bar');
    })
    });
    Component Lookup
    Component: x-foo

    View Slide

  43. var id = new Handlebars.AST.IdNode([{ part: '_triageMustache' }]);
    mustache = new Handlebars.AST.MustacheNode(
    [id].concat([mustache.id]),
    mustache.hash,
    !mustache.escaped
    );
    return Handlebars.Compiler.prototype.mustache.call(this, mustache);
    Component Lookup
    Ember.Handlebars.Compiler.mustache

    View Slide

  44. var helper = EmberHandlebars.resolveHelper(
    options.data.view.container,
    property
    );
    if (helper) { return helper.call(this, options); }
    Component Lookup
    _triageMustacheHelper

    View Slide

  45. var componentLookup = container.lookup('component-lookup:main');
    var Component = componentLookup.lookupFactory(name, container);
    if (Component) {
    helper = EmberHandlebars.makeViewHelper(Component);
    container.register('helper:' + name, helper);
    }
    Component Lookup
    resolveHelper

    View Slide

  46. Component Lookup
    Em.ComponentLookup.lookupFactory
    var fullName = 'component:' + name,
    templateFullName = 'template:components/' + name,
    templateRegistered = this.container &&
    this.container.has(templateFullName);
    if (templateRegistered)
    container.injection(fullName, 'layout', templateFullName);

    View Slide

  47. Component Lookup
    Em.ComponentLookup.lookupFactory
    var Component = container.lookupFactory(fullName);
    if (templateRegistered || Component)
    if (!Component) {
    container.register(fullName, Ember.Component);
    Component = container.lookupFactory(fullName);
    }

    View Slide

  48. Component Lookup
    app/components/x-foo.js
    app/templates/components/x-foo.hbs
    Files looked up:

    View Slide

  49. app/components/x-foo.js
    app/templates/components/x-foo.hbs
    Component Lookup
    Files looked up:

    View Slide

  50. Component Lookup
    app/components/x-foo.js
    // packages/ember-handlebars/lib/component_lookup.js#L16
    // Ember.ComponentLookup#lookupFactory
    var Component = container.lookupFactory(fullName);

    View Slide

  51. Component Lookup
    app/components/x-foo.js
    app/templates/components/x-foo.hbs
    Files looked up:

    View Slide

  52. Component Lookup
    app/templates/components/x-foo.hbs
    // packages/ember-handlebars/lib/component_lookup.js#L13
    // Ember.ComponentLookup#lookupFactory
    templateFullName = 'template:components/' + name;
    container.injection(fullName, 'layout', templateFullName);

    View Slide

  53. Magic?

    View Slide

  54. Nope, Just Javascript

    View Slide