Rendering third-party plugins

A common issue when rendering views is not rendering plugins from others because they are designed to work with traditional web applications but not with SPA; this is because many plugins are DOM-dependent, which means that the target element should exist in the actual DOM. To see this issue more clearly, let me show you an example with the jQueryUI Calendar plugin. Let's add a birthdate field to our ContactEditor, replacing the age field.

// index.html
// ...
<div class="form-group">
  <label for="birthdate">Birth date</label>
  <input id="birthdate " type="text"
    class="form-control" value="<%= birthdate %>" />
//...

And make the proper changes in the view:

class ContactForm extends ModelView {
  // ...
  serializeData() {
    return _.defaults(this.model.toJSON(), {
      name: '',
      birthdate: '',
      // ...
    });
  },
  saveContact(event) {
    event.preventDefault();
    this.model.set('name', this.$el.find('#name').val());
    this.model.set('birthdate',
      this.$el.find('#birthdate').val()
    );
    // ...
  },
// ...
});

To show a calendar on the birthdate field we need to call $('#birthdate').datepicker()somewhere, but what is the best place to do that?

// ... edit-contact.js
class ContactEditor {
  // ...

  showEditor(contact) {
    var contactForm = new ContactForm({model: contact});
    this.region.show(contactForm);
    contactForm.$('#birthdate').datepicker();

    this.listenTo(contactForm, 'form:save', this.saveContact);
    this.listenTo(contactForm, 'form:cancel', this.cancel);
  };
};

After calling the show() method on the region object, the contactForm view is live in the DOM, so it makes sense to call the datepicker() method after that. However this is not a good strategy because our controller object knows about DOM elements, which are not its responsibility.

Views should be responsible for dealing with the DOM, so rendering third-party plugins is included. Another approach could be to extend the render() method on the FormView class but we already have the onRender() callback, which is called after the rendering process.

// ... edit-contact.js
var ContactForm extends ModelView {
  // ...
  onRender() {
    this.$('#birthdate').datepicker();
  },
  //...
});

But this is not going to work because we are rendering the view on a region. Did you remember the show() method?

class Region {
// ...
  openView(view) {
    this.ensureEl();
    view.render();
    this.$el.html(view.el);
  }
// ...
}

The showing process first renders the view in memory and after that makes it available on the DOM. That's why this doesn't work. The intent of the onRender() method is to make template changes before making them available on the DOM. We need to add a new callback method that will be called when the view is in the DOM.

class Region {
// ...
  openView(view) {
    this.ensureEl();
    view.render();
    this.$el.html(view.el);

    // Callback when the view is in the DOM
    if (view.onShow) {
      view.onShow();
    }
  }
// ...
}

Remember to make this feature available in CollectionView too.

// common.js
class CollectionView extends Backbone.View {
  // ...
  onShow() {
    var children = this.children || {};
    _.each(children, child => {
      if (child.onShow) {
        child.onShow();
      }
    });
  }
}

So, our ContactForm will end with something like this.

// apps/contacts/contactEditor.js
class ContactForm extends ModelView {
  // ...

  // Call the onShow method for each children
  onShow() {
    // Ensure that children exists
    var children = this.children || {};

    _.each(children, child => {
      if (child.onShow) {
        child.onShow();
      }
    });
  }
  //...
}

Remember, most third-party plugins need to have the element in the DOM or they will not work, so you should call the plugin only after rendering the view. The best place to call plugins is in the extended view class so the responsibility for DOM manipulation is encapsulated in the view.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.216.27.251