Module: UWA/Class/View

UWA/Class/View

The container/object for the Javascript logic that renders and updates an isolated logical chunk of UI.

A UWA.Class.View does not store any data or groups of data; it is commonly the hub that references UWA.Class.Model or {@link module:UWA/Class/Collection.UWA.Class.Collection|UWA.Class.Collection} of data and does something with that data.

If it helps, think of UWA.Class.View as the location in your Javascript code that brings together and controls the following parts:

  • data (i.e. UWA.Class.Model or {@link module:UWA/Class/Collection.UWA.Class.Collection|UWA.Class.Collection} of models)
  • compiled HTML templates
  • reference/hook into DOM element that contains the HTML markup forming the UI
  • events and functions driving the UI

The views' job, then, is to take these parts (i.e. data, template, DOM, events) and render the UI region. Instead of digging into a JSON object, looking up an element in the DOM, and updating the HTML by hand, you can (for example) bind your view's render function to the model's "onChange" event — and now everywhere that model data is displayed in the UI, it is always immediately up to date !

Here are some points about views:

  • UWA.Class.View contain all of the parts necessary to construct an isolated and logical region of a web application UI. UWA.Class.View objects glue together data, templates, events and the rendering and re-rendering logic that occurs when data changes.
  • UWA.Class.View don't determine anything about your HTML or CSS for you, and can be used with any JavaScript templating library.
  • Views do not have to be connect to a model or a collection (i.e. data). A view can simply manage an isolated and logical region of a web application.
  • It is common practice for a view to reference compiled templates (i.e. chunks of html meant to present some data). This might be a stretch for some considering that views typically are templates. This is not the case with UWA.Class.View! UWA.Class.View are not templates!
  • UWA.Class.View don’t contain the HTML markup for your application; they contain the logic behind the presentation of the model’s data to the user. The presentation itself is achieved using a JavaScript templating engine (up to you to pick up your favorite templating engine) and CSS stylesheets* (pick up your favorite CSS guru).

Events triggered by instances of UWA.Class.View:

By default, instances of UWA.Class.View do not fire any event other than the ones fired by its base class UWA.Controls.Abstract :

Event name Description
'onPreInject' Triggered before module:UWA/Controls/Abstract.UWA.Controls.Abstract#inject
'onPostInject' Triggered after module:UWA/Controls/Abstract.UWA.Controls.Abstract#inject
'onResize' Invoked when control needs to resize.

But, since UWA.Class.View implements UWA.Class.Events, your sub-classes inheriting from UWA.Class.View are able to fire any custom event you choose to define. This can be useful for some observed sub-views to communicate with their observing parent/aggregating view!

Define your own custom view by sub-classing UWA.Class.View:

Get started with views by defining your custom view class by extending/subclassing the base class {@link module:UWA/Class/View.UWA.Class.View|UWA.Class.View} before you instantiate it so that you can add your own domain specific properties. You'll want to override the render function, specify your declarative events, and perhaps the tagName, className, or id of the View's root container element. You can also define some domain specific properties for instances to inherit. (see code example below) Don't forget to override the destroy function !

Properties like tagName, {@link module:UWA/Class/View.UWA.Class.View#id|id}, {@link module:UWA/Class/View.UWA.Class.View#className|className}, {@link module:UWA/Class/View.UWA.Class.View#container|container}, and domEvents may also be defined as a function, if you want to wait to define them until runtime (for example to wait until the DOM is ready).

Example

var doc, DocumentRow, docRow1;

doc = new UWA.Class.Model({
  title: 'The Big Nowhere',
  author: 'James Ellroy',
  publisher: 'Rivages Noir'
});

DocumentRow = UWA.Class.View.extend(UWA.Class.Debug, {

  tagName: "li",

  className: "document-row",

  domEvents: {
    "click .icon":         "open",
    "click button.edit":   "openEditDialog",
    "click button.delete": "delete"
  },

  setup: function() {
    this.listenTo(this.model, "onChange", this.render);
    this.log('initialized!');
  },

  render: function () {
    // Here typically we should render a compiled template...
    // Also note that there is no need to re-bind DOM events
    // thanks to event delegation handled by the 'container'
    // of the View !
    this.container.setContent([
        {
            tag: 'span',
            'class': 'icon',
            text: ''
        },
        {
            tag: 'span',
            'class': 'title',
            text: this.model.get('title')
        },
        {
            tag: 'button',
            'class': 'edit'
        },
        {
            tag: 'button',
            'class': 'delete'
        }
    ]);
    return this;
  },

  open: function (domClickEvent) {
    this.dispatchEvent('onOpen', [this.model]);
    // this event can be caught by the parent view to display
    // the doc abstract in an overlay panel for example.
  },

  openEditDialog: function (domClickEvent) {
    this.dispatchEvent('onOpenEditDialog', [this.model]);
    // this event can be caught by the parent view to display
    // the doc editor in an overlay panel for example.
  },

  delete: function (domClickEvent) {
      var that = this;
      that.disable(); // to disable user's interaction with the
                      // doc being removed.
      // Now let's destroy the model in the backend server :
      // if deletion in the backend succeeds, this view is destroyed
      // otherwise we warn our parent view that deletion failed and
      // enable this view.
      that.model.destroy({
        onComplete: that.destroy.bind(that),
        onFailure: function (model, response, options) {
          var msg = UWA.String.format(
             'Oops! Failed to delete doc {0} because {1}.',
             that.model.get('title'),
             response
          );
          that.dispatchEvent('onError', [msg]);
          that.enable();
        }
      }, {
        wait: true
      });
  },

  disable: function (timeOut) {
    return this;
  },

  enable: function () {
    return this;
  },

  // We override this destroy method to ...
  destroy: function () {
      // 1) stop listening to our observed model, this is very important
      // so that our view can be effectively removed by the GC (otherwise
      // it is held by the model, as a registered observer).
      this.stopListening();
      this.model = null;

      // 2) Eventually call this._parent() so that our container element
      // is destroyed and removed from the DOM.
      this._parent();
  }
});

UWA.Event.onDomReady(function () {
  docRow1 = new DocumentRow({
    model: doc,
    id: "document-row-" + doc.id
  });

  docRow1.addEvent('onOpen', function (doc) {
    alert(UWA.String.format('This is the abstract for document {0}', doc.get('title')));
  });

  docRow1.addEvent('onOpenEditDialog', function (doc) {
    alert(UWA.String.format('This is the editor for document {0}', doc.get('title')));
  });

  docRow1.render().inject(document.getElementById('docs'));
});

Classes

UWA.Class.View