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

Finding Patterns in Front-end JS

Finding Patterns in Front-end JS

We’re all building a client-side framework. And they’re all different implementations of the same stuff. I want to compare the approaches that popular frameworks take to solving these patterns. If they even try to solve them at all. I also want to include info about the custom framework that Yammer uses. It’ll be illustrative to see how all of these have different approaches to the same set of patterns.

Marco Rogers

May 28, 2014
Tweet

More Decks by Marco Rogers

Other Decks in Programming

Transcript

  1. FINDING PATTERNS "When it comes down to it, everybody is

    building variants of the same MVCish framework. We should be talking about the shared patterns." ! - Me
  2. MODELS • Objects representing your data • Data properties that

    are read/write • Persist data to the server (usually) • Event listeners that fire when data changes
  3. MODELS: BACKBONE.JS var thread = new Backbone.Model({ ! id: 100,!

    latest_reply_at: '2014-05-28T17:36:05.707Z'! }); ! ! thread.get(‘id’) === 100;! ! thread.on(‘change:latest_reply_at’, function() {! /* Do Stuff */! });! ! thread.set(‘latest_reply_at’, (new Date).toISOString());
  4. MODELS: YAM.JS var thread = yam.model.Thread.create({ ! id: 100, !

    latest_reply_at: '2014-05-28T17:36:05.707Z'! });! ! thread.id === 100;! ! thread.hook(‘onUpdate’, function(oldData, newData) {! /* Do Stuff */! });! ! thread.update({! latest_reply_at: (new Date).toISOString()! });
  5. MODELS: ANGULAR feedApp.controller('ThreadViewCtrl', function ($scope) { ! $scope.thread = {

    ! id: 100, ! latest_reply_at: '2014-05-28T17:36:05.707Z' ! }; ! ! $scope.thread.id === 100;! ! $scope.$watch(‘thread.latest_reply_at’, function(old, new) { ! /* Do Stuff */ ! }); ! ! $scope.thread.latest_reply_at = (new Date).toISOString();! });
  6. MODELS • Objects representing your data • Data properties that

    are read/write • Event listeners that fire when data changes • Persist data to the server (usually)
  7. ADVANCED PATTERNS • Nested Views • Complex Rendering • Model

    Relationships • Local Data Caching • Data-binding (two-way)
  8. VIEWS • Render some markup to the DOM • Respond

    to user input from the DOM • Update the DOM based on some changes
  9. NESTED VIEWS • Organize views into a parent-child tree •

    Parents recursively render children • The tree has a shared lifecycle
  10. NESTED VIEWS: YAM.JS ThreadListItem = Backbone.Component.extend({! _addThreadStarter: function () {!

    this.threadStarter = new ThreadStarter(message, {...});! this.prepend(this.threadStarter);! });! ! _addReplyListItem: function (reply) {! this.replyListItem = new MessageListItem(reply, {…});! this.append(this.replyListItem); ! });! });! ! var threadList = new ThreadListItem();! threadList.render();
  11. NESTED VIEWS: MARIONETTE.JS MessageListItem = Backbone.Marionette.ItemView.extend({});! ! ThreadListItem = Backbone.Marionette.CompositeView.extend({!

    itemView: MessageListItem! });! ! var threadList = new ThreadListItem({! model: someModel,! collection: someCollection! });! ! threadList.render();
  12. NESTED VIEWS: EMBER.JS App.MessageListItem = Ember.Component.extend({…});! ! <div class=“thread-list-item”>! {{thread-starter}}!

    <ul class="thread-replies">! {#each reply in threadReplies}}! {{message-list-item reply=reply}}! {{/each}}! </ul>! </div>
  13. EMERGING PATTERNS • Custom components (Polymer, React) • Real-time updates

    (Socket.IO, Pusher) • Real-time sync (Firebase, Meteor) • Persistent offline cache (hood.ie) • Device capabilities (e.g. camera, location)
  14. REAL-TIME UPDATES • Subscribe to real-time updates from the server

    • Dispatch updates to the model layer • Register to receive events with new model data
  15. REAL-TIME: ANGULAR var ItemListController = function($scope, $http, Pusher) {! $scope.items

    = [];! ! var retrieveItems = function () {! $http.get('/api/items').success(function (items) {! $scope.items = items;! });! };! ! Pusher.subscribe('items', 'updated', function (item) {! $scope.items.push(item);! });! ! retrieveItems();! http://blog.pusher.com/making-angular-js-realtime-with-pusher/
  16. REAL-TIME: EMBER.JS Discourse.TopicController = Discourse.ObjectController.extend(..., {! subscribe: function() {! var

    topicController = this;! var bus = Discourse.MessageBus;! ! bus.subscribe("/topic/" + this.get('id'), function(data) {! // Add the new post into the stream! topicController.get(‘postStream')! .triggerNewPostInStream(data.id);! });! }! } http://balinterdi.com/2014/01/14/how-real-time-updates-work-in- discourse.html
  17. REAL-TIME: YAM.JS // FeedModel! yam.model.define('yam.model.Feed', function () {! this.clientize(‘yam.client.RealtimeFeedClient’);! !

    // DataClient! yam.ctor(‘yam.client.BaseRealtimeClient’, {! connectToRealtime: function(params) {! yam.client.bayeux.subscribe({! id: this.getUrl(),! url: this.getRealtimeUrl(),! onData: _.bind(this.onRealtimeData, this)! });! }! });
  18. REAL-TIME: YAM.JS var feedModel = yam.model.Feed.create(…);! ! // Listen for

    events! feedModel.hook(‘onThreadUpdate’, function(thread) {! // Could be from an ajax request, could be real-time! // We usually don’t care! });
  19. REAL-TIME: METEOR Messages = new Meteor.Collection(‘messages’);! ! var query =

    Messages.find();! query.observeChanges({! added: function (id, message) { /* Do stuff */ },! removed: function () { /* Do stuff */ }! });
  20. REAL-TIME: METEOR <template name="messageList">! {#each messages}! <li>{{body}}</li>! {/each}! </template>! !

    Template.messageList.messages = function () {! return Messages.find();! };
  21. FINDING PATTERNS "I'm more interested in optimizing a person's understanding

    of problems than their understanding of solutions.” ! - Kris Gale