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

Building Apps with Ember (a postmortem)

Sean
July 26, 2013

Building Apps with Ember (a postmortem)

Sean Creeley (https://twitter.com/screeley) will be postmorteming his recent experience rewriting the Embedly user app in Ember. Sean will talk about what Ember is good at doing, what it's not so great at, code patterns, and ways to structure your apps. He will go over useful Ember features that aren't documented well and end with an overview of a recent tutorial series he and his colleagues published on Ember development on the Embedly blog (http://blog.embed.ly/tagged/emberjs).

Sean

July 26, 2013
Tweet

More Decks by Sean

Other Decks in Programming

Transcript

  1. BUILDING
    APPS
    WITH
    EMBER
    (A POSTMORTEM)

    View full-size slide

  2. WHAT WE BUILT

    View full-size slide

  3. No
    comparing
    and
    contrasting.

    View full-size slide

  4. @screeley Angular is faster than Ember,
    here's the link to prove it: bit.ly/11mNonE
    @screeley Backbone is 1,500 lines of
    code, Ember has 27,000. Talk about bloat.
    @screeley neckbeard, neckbeard,
    neckbeard. I hate you.

    View full-size slide

  5. @screeley You are a gentleman and a
    scholar, i <3 you #yolo

    View full-size slide

  6. APPLICATION
    window.Dolphin = Em.Application.create({
    rootElement: $('#dolphin'),
    ready: function(){}
    });

    View full-size slide

  7. ROUTER
    Dolphin.Router.map(function() {
    this.route('about');
    this.resource('company', function(){
    this.route('team');
    });
    });
    /
    /about
    /company
    /company/team

    View full-size slide

  8. TEMPLATES


    {{outlet}}


    Welcome to Dolphin!
    Helping Dolphins become the smartest species
    since 2013
    application.handlebars
    index.handlebars

    View full-size slide

  9. MODELS
    Dolphin.CompanyTeamRoute = Em.Route.extend({
    model: function(){
    return {name: 'sean'}
    }
    });

    {{name}}

    company/team.handlebars

    View full-size slide

  10. MODELS
    Dolphin.CompanyTeamRoute = Em.Route.extend({
    model: function(){
    return [
    {name: 'sean'},
    {name: 'kawandeep'},
    {name: 'andy'}]
    }
    });

    {{#each controller}}
    {{name}}
    {{/each}}

    View full-size slide

  11. MODELS
    Dolphin.CompanyTeamRoute = Em.Route.extend({
    model: function(){
    return Dolphin.User.find({is_team: true});
    }
    });

    View full-size slide

  12. CONTROLLERS
    Dolphin.CompanyTeamController =
    Em.ArrayController.extend({
    sortAscending: ['years_coding'],
    totalYears: function(){
    return this.reduce(function(total, user) {
    return total + user.get('years_coding');
    }, 0);
    }.property('@each.years_coding')
    });

    View full-size slide

  13. VIEWS
    Dolphin.CompanyTeamView = Em.View.extend({
    didInsertElement: function () {
    // fade images in.
    this.$().find('img').animate({
    opacity: 1
    }, 2000);
    }
    });

    View full-size slide

  14. ARRAYS
    var arr = Em.A(["a", null, "b", "c"]);
    arr.contains("a"); // true
    arr.compact(); // ["a", “b”, "c"]
    arr.without("a"); // ["b", "c"]
    arr.some(function(){});
    arr.every(function(){});
    arr.map(function(){})
    arr.reduce(function(){}, 0)
    arr.forEach(function(){})
    arr.reject(function(){})

    View full-size slide

  15. BUILTINS
    Ember.isNone
    Ember.isEmpty
    Ember.isArray
    Ember.keys
    Ember.computed.equal
    Ember.computed.match
    Ember.computed.or
    Ember.without

    View full-size slide

  16. #1
    Do things the
    Ember way.

    View full-size slide

  17. #2
    Naming
    Conventions are
    EVERYTHING

    View full-size slide

  18. /#/about
    App.AboutRoute
    App.AboutView
    App.AboutController
    about.handlebars

    View full-size slide

  19. #3
    Think like
    Russian Dolls

    View full-size slide

  20. comment
    post
    blog

    View full-size slide

  21. this.resource('blog', {path:'/blogs/:blog_id'},
    function(){
    this.resource('post',{path:'/posts/:post_id'},
    function(){
    this.route('comment',
    {path: '/comment/:id'});
    });
    });
    /blog/:id/post/:id/comment/:id

    View full-size slide

  22. application.handlebars
    blog.handlebars
    post.handlebars
    comment.handlebars

    View full-size slide

  23. #4
    You will never
    use
    “text/x-handlebars”

    View full-size slide

  24. data-template-name="blog">

    {{outlet}}


    Em.TEMPLATES['blog'] =
    Em.Handlebars.compile(
    '{{outlet}}');

    View full-size slide

  25. #5
    You needs to
    use “needs”

    View full-size slide

  26. App.CommentController = Em.ObjectController.extend({
    needs:["blog", "post"],
    postBinding: "controllers.post",
    blogBinding: "controllers.blog",
    });

    View full-size slide

  27. #6
    jQuery is not the
    enemy.

    View full-size slide

  28. App.CommentView = Em.View.extend({
    template: Em.Handlebars.compile(
    '{{name}}{{{body}}}'),
    didInsertElement: function(){
    this.$().find('p').animate({
    opacity: 1
    }, 1000);
    }
    });

    View full-size slide

  29. #7
    Ember Data is like
    ten thousand
    spoons when all
    you need is a knife.

    View full-size slide

  30. #8
    Understand
    Ember Data's
    protocol and
    protect yourself.

    View full-size slide

  31. {
    "blogs": [
    {
    "id": 1,
    "name": "Embedly Blog"
    },
    {
    "id": 1,
    "name": "Embedly Blog"
    },
    ....
    ]
    }
    /api/blogs

    View full-size slide

  32. {"users": [
    {
    "id": 1,
    "name": "Sean",
    "email": "[email protected]"
    },
    {
    "id": 2,
    "name": "Kawandeep",
    "email": "[email protected]"
    },
    ....
    ]}
    /api/users

    View full-size slide

  33. #9
    deferReadiness
    is your friend.

    View full-size slide

  34. App.deferReadiness();
    // Wait for all the javascript files to load.
    $(document).ready(function(){
    App.User.current(function(user, profile,
    organizations){
    // Set everything else up.
    App.set('user', user);
    // Will start everything up.
    App.advanceReadiness();
    });
    });

    View full-size slide

  35. #10
    Save high level
    objects on the
    Application.

    View full-size slide

  36. var Clip = Em.Namespace.create();
    Clip.Copy = Em.View.extend({
    didInsertElement: function(){
    this.$().attr('data-clipboard-text',
    Dolphin.get('org.api_key'));
    var clip = new ZeroClipboard( this.$().get(0), {
    moviePath: "/scripts/vendor/zero/zero-
    clipboard.swf"
    });
    }
    });

    View full-size slide

  37. #11
    Objects, objects
    everywhere.

    View full-size slide

  38. App.AuthedRoute = Em.Route.extend({
    redirect: function(){
    if (Em.isNone(Shine.get('user'))){
    this.transitionTo('login');
    return false;
    }
    return true;
    }
    });

    View full-size slide

  39. #12
    Promise to use
    promises.

    View full-size slide

  40. App.OrganizationRoute = App.AuthedRoute.extend({
    model: function(){
    var promise = Em.Deferred.create();
    App.Organization.find(
    {slug:params.slug}).then(function(results){
    App.set('org', results.objectAt(0));
    promise.resolve(results.objectAt(0));
    });
    return promise;
    }
    });

    View full-size slide

  41. #13
    “states” move
    the world.

    View full-size slide

  42. #14
    Ember gives
    you debugging
    tools, use them.

    View full-size slide

  43. App = Ember.Application.create({
    LOG_STACKTRACE_ON_DEPRECATION : true,
    LOG_BINDINGS : true,
    LOG_TRANSITIONS : true,
    LOG_TRANSITIONS_INTERNAL : true,
    LOG_VIEW_LOOKUPS : true,
    LOG_ACTIVE_GENERATION : true
    });
    {{debugger}}
    {{log controller}}

    View full-size slide

  44. #15
    Let Handlebars
    helper you.

    View full-size slide

  45. Em.Handlebars.registerBoundHelper(
    'prettyCents', function (as_cents) {
    var as_dollars = as_cents / 100;
    return '$' + as_dollars;
    });

    View full-size slide

  46. #16
    Use Ember's
    standard library.

    View full-size slide

  47. #17
    USE GRUNT

    View full-size slide

  48. #18
    It's probably not
    Ember's fault,
    you just think it
    is.

    View full-size slide

  49. QUESTIONS?
    @screeley
    @embedly

    View full-size slide