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)

  2. Sean Creeley @screeley [email protected]

  3. WHAT WE BUILT

  4. Why Ember?

  5. No comparing and contrasting.

  6. @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.
  7. @screeley You are a gentleman and a scholar, i <3

    you #yolo
  8. EMBER INTRO

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

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

    /about /company /company/team
  11. TEMPLATES <div class="row"> <div class="large-12 columns"> {{outlet}} </div> </div> <h1>Welcome

    to Dolphin!</h1> <p>Helping Dolphins become the smartest species since 2013</p> application.handlebars index.handlebars
  12. MODELS Dolphin.CompanyTeamRoute = Em.Route.extend({ model: function(){ return {name: 'sean'} }

    }); <div> <h1>{{name}}</h1> </div> company/team.handlebars
  13. MODELS Dolphin.CompanyTeamRoute = Em.Route.extend({ model: function(){ return [ {name: 'sean'},

    {name: 'kawandeep'}, {name: 'andy'}] } }); <ul> {{#each controller}} <li>{{name}}</li> {{/each}} </ul>
  14. MODELS Dolphin.CompanyTeamRoute = Em.Route.extend({ model: function(){ return Dolphin.User.find({is_team: true}); }

    });
  15. 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') });
  16. VIEWS Dolphin.CompanyTeamView = Em.View.extend({ didInsertElement: function () { // fade

    images in. this.$().find('img').animate({ opacity: 1 }, 2000); } });
  17. 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(){})
  18. BUILTINS Ember.isNone Ember.isEmpty Ember.isArray Ember.keys Ember.computed.equal Ember.computed.match Ember.computed.or Ember.without

  19. POSTMORTEM

  20. #1 Do things the Ember way.

  21. #2 Naming Conventions are EVERYTHING

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

  23. #3 Think like Russian Dolls

  24. comment post blog

  25. 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
  26. application.handlebars blog.handlebars post.handlebars comment.handlebars

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

  28. <script type="text/x-handlebars" data-template-name="blog"> <div> {{outlet}} </div> </script> Em.TEMPLATES['blog'] = Em.Handlebars.compile(

    '<div>{{outlet}}</div>');
  29. #5 You needs to use “needs”

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

  31. #6 jQuery is not the enemy.

  32. App.CommentView = Em.View.extend({ template: Em.Handlebars.compile( '<b>{{name}}</b><p>{{{body}}}</p>'), didInsertElement: function(){ this.$().find('p').animate({ opacity:

    1 }, 1000); } });
  33. #7 Ember Data is like ten thousand spoons when all

    you need is a knife.
  34. #8 Understand Ember Data's protocol and protect yourself.

  35. { "blogs": [ { "id": 1, "name": "Embedly Blog" },

    { "id": 1, "name": "Embedly Blog" }, .... ] } /api/blogs
  36. {"users": [ { "id": 1, "name": "Sean", "email": "[email protected]" },

    { "id": 2, "name": "Kawandeep", "email": "[email protected]" }, .... ]} /api/users
  37. #9 deferReadiness is your friend.

  38. 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(); }); });
  39. #10 Save high level objects on the Application.

  40. 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" }); } });
  41. #11 Objects, objects everywhere.

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

    } return true; } });
  43. #12 Promise to use promises.

  44. 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; } });
  45. #13 “states” move the world.

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

  47. 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}}
  48. #15 Let Handlebars helper you.

  49. Em.Handlebars.registerBoundHelper( 'prettyCents', function (as_cents) { var as_dollars = as_cents /

    100; return '$' + as_dollars; });
  50. #16 Use Ember's standard library.

  51. #17 USE GRUNT

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

    is.
  53. QUESTIONS? @screeley @embedly