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

Interop! Building a better Backbone.View

RJ Zaworski
September 12, 2014

Interop! Building a better Backbone.View

RJ Zaworski

September 12, 2014
Tweet

More Decks by RJ Zaworski

Other Decks in Technology

Transcript

  1. Let’s Talk Backbone For everything else, frameworks ★ Thorax (templating,

    structure, data-binding) ★ Marionette (structure) ★ ...and a cast of thousands
  2. Portable Views can be reused ★ outside the host application

    ★ outside each other ★ outside Backbone
  3. Ex. 1: Backbone Goal: render a <figure> from a Backbone.Model

    . var template = _.template([ '<img src="<%- image %>" />', '<figcaption><%- title % ></figcaption>' ].join('')) var FigureView = Backbone.View.extend({ tagName: 'figure', render: function () { var attrs = this.model.toJSON(); this.el.innerHTML = template(attrs); return this; } });
  4. Ex. 1: Backbone Goal: decompose <figure> into an <img> and

    a <figcaption> var Image = Backbone.View.extend({ tagName: 'img', // ... }); var Figcaption = Backbone.View.extend({ tagName: 'figcaption', // ... }); // in parent's render() method this.$el.empty() .append(imageView.render().el) .append(figcaptionView.render().el);
  5. Ex. 1: Backbone ★ manipulate DOM directly ★ what about

    old state? var Image = Backbone.View.extend({ tagName: 'img', // ... }); var Figcaption = Backbone.View.extend({ tagName: 'figcaption', // ... }); // in parent's render() method this.$el.empty() .append(imageView.render().el) .append(figcaptionView.render().el);
  6. Ex. 1: Backbone Goal: update <figcaption> when model changes var

    Figcaption = Backbone.View.extend({ initialize: function () { this.listenTo( this.model, 'change', this.onModelChange ); }, onModelChange: function () { alert('changed'); this.render(); }, // ...
  7. Ex. 1: Backbone What if we remove the parent view?

    var Figure = Backbone.View.extend({ events: { 'click .js-close': 'onCloseClick' }, onCloseClick: function (e) { e.preventDefault(); this.$el.remove(); }, // ... }
  8. Ex. 1: Backbone What if we remove the parent view?

    Events stay bound! var Figcaption = Backbone.View.extend({ initialize: function () { this.listenTo( this.model, 'change', this.onModelChange ); }, onModelChange: function () { alert('changed'); this.render(); }, // ...
  9. Ex. 2: Backbone + React React Children... var ImageView =

    React.createClass({ render: function () { return React.DOM.img({ src: this.props.image }); } }); var FigcaptionView = React.createClass({ render: function () { return React.DOM.figcaption(null, this.props.title ); } });
  10. Ex. 2: Backbone + React ...with a Backbone.View parent var

    imageEl = $('<div />')[0]; var figcaptionEl = $('<div />')[0]; this.$el.empty() .append(imageEl) .append(figcaptionEl); React.renderComponent( ImageView(this.model.toJSON()), imageEl ); React.renderComponent( FigcaptionView(this.model.toJSON()), figcaptionEl );
  11. Ex. 2: Backbone + React Container <div> aside, ★ Complexity

    contained in parent ★ Children are easy to work with! ★ No child zombies
  12. Wire Backbone data to React UI It’s just an “M”

    and a “V”: ★ Make friends with toJSON() ★ Keep the interface small
  13. Ex. 3: React Goal: render a (React-powered) <figure> from a

    Backbone.Model . var FigureView = React.createClass({ render: function () { return React.DOM.figure({}, [ ImageView(this.props), FigcaptionView(this.props) ]); } }); var props = model.toJSON(); React.renderComponent( new FigureView(props), document.querySelector('.container') );
  14. Ex. 3: React ★ Complexity contained in top-level app ★

    All views are easy to work with! ★ No zombies
  15. Ex. 3: React ...and the interface is tiny! var FigureView

    = React.createClass({ render: function () { return React.DOM.figure({}, [ ImageView(this.props), FigcaptionView(this.props) ]); } }); var props = model.toJSON(); React.renderComponent( new FigureView(props), document.querySelector('.container') );
  16. Web Components Our simple view fits into the DOM <figure>

    <img src="images/milo.jpg" /> <figcaption>Milo</figcaption> </figure>
  17. Web Components What if we could wrap any view up

    like this? <figure> <img src="images/milo.jpg" /> <figcaption>Milo</figcaption> </figure> <my-figure title="Milo" image="images/milo.jpg"> </my-figure>
  18. Web Components The bad news: they’re not ready yet ★

    Limited native support ★ Implemented via Polymer, X-Tags
  19. Ex. 4: Web Components Register <my-figure> var proto = Object.create(HTMLElement.prototype);

    document.registerElement('my-figure', { prototype: proto });
  20. Ex. 4: Web Components proto.createdCallback = function () { var

    img = this.img = document.createElement('img'); var shadow = this.createShadowRoot(); shadow.appendChild(img); }; proto.attributeChangedCallback = function (key) { switch (key) { case 'image': this.img.setAttribute('src', this.getAttribute('image')); break; } };
  21. Ex. 4: Web Components Sending in the model... var figure

    = document.createElement('my-figure'); figure.setAttribute('image', model.get('image')); $('body').appendChild(figure);
  22. Ex. 4: Web Components Sending in the model... var figure

    = document.createElement('my-figure'); figure.setAttribute('image', model.get('image')); $('body').appendChild(figure); <body> <my-figure image="images/milo.jpg"></my-figure> </body>
  23. Ex. 4: Web Components Just like that, we’re back to

    the DOM. model.on('change', function () { $('my-figure').attr(model.toJSON()); });