Skip to content
This repository has been archived by the owner on Jul 8, 2020. It is now read-only.

Rendering (The M Project 1.x)

hano edited this page Dec 3, 2013 · 1 revision

Introduction

The Rendering in The M-Project is one of the core concepts and most important features since we deploy JavaScript code, that creates HTML on the fly. In addition to writing to the DOM, we also need our theming engine, jQuery mobile, to run through it and to do the styling. To provide fast page transitions, we always render and theme all static parts of an application right on startup. So we can switch between pages smoothly, without having to worry about their rendering. The only thing we need to render everytime a page is loaded, are their dynamic parts (e.g. a list view showing just requested tweets). But since the page's skeleton is already there, we can easily load these dynamic parts, integrate them into the DOM using jQuery, and then force the affected ui elements to render respectively to update.

M.Application – Let's render the application

M.Application can be seen as the root class of an application. It has properties like viewManager, eventDispatcher or modelRegistry that contain information about the whole application (e.g. the view manager knows every view) or have a key role in handling a certain problem (e.g. the event dispatcher handles every incoming event). Since it knows all the views within an application, this is where we start the rendering, more precisely in the application's main() method.

What we basically do is to iterate through all views, find every M.Page and call its render() method. Simplified, this looks like the following:

main: function() {
    for(i in this.viewManager.viewList) {
        if(this.viewManager.viewList[i].type === 'M.PageView') {
            this.viewManager.viewList[i].render();              
        }
    }
}

M.Page – Break it down to a single page

Pages are the main division units wihtin an application or, in other words, an application consists of many pages. These pages (M.Page) use the same principle as M.Application to render its child views. The only difference is that a page doesn't know what type its child views are of, while an application knows that it has to look for pages. Within the render() method of a page view we iterate through its child views, without knowing their types, and simply call their render() methods. But instead of having all views writing to the DOM themselves, we collect all data and only write to the DOM once per page. Therefore every view has an html property where it stores its DOM output and returns it to its parent view. So you can see that the rendering process works recursively: M.Page stores its opening HTML in its local html property and calls its child view's render() method. They also store their opening HTML in their html property and call their child views's render() method. Once there is no child view available, this view adds its closing HTML to its html property and returns the whole HTML representation to its parent view. So when all views within a page were rendered, M.Page gets the whole content, add its own closing HTML and then finally calls writeToDOM() method to put all the HTML into the DOM. The following sequence diagram visualizes this process for a simple application containing of a toolbar with a label and a scroll view with an image inside.

M.Label – A basic sample view

Now let's take a look at how we do the rendering of a basic view component like a label. Since it is a very simple rendering, we will start with a code sample right away:

render: function() {
    this.html += '<div id="' + this.id + '" ' + this.style() + '>' + this.value + '</div>';
    return this.html;
}

As you can see we store a label's HTML representation in the view's html property and then simply return this to the label's parent view. What you can also see is that we always provide a view's HTML representation with an unique identifier for being able to specifically access it. This is important especially for Data Binding. In addition to that we use a style() method to give the label some specific styles that can be defined within the application code. For example you can force a label to display inline.

M.ToolbarView – A more complex container view

In comparison to a label, a toolbar is much more complex. Not only does it accept child views but also provides three defined locations to display those child views. Basically this doesn't change anything. M.ToolbarView also stores its HTML representation in its html property and finally returns this to its parent view. What is new here, is the renderChildViews() method. It is used to iterate through the toolbar's child views and to call their specific render() method. But let's take a look at the toolbar's render() first:

render: function() {
    this.html += '<div id="' + this.id + '" data-role="' + this.anchorLocation + '" data-position="fixed">';
    this.renderChildViews();
    this.html += '</div>';

    return this.html;
}

As you can see we do pretty much the same as with a simple label. We open a div, we set the toolbar's id and we do some styling with the data-role (header or footer) and data-position (fixed: toolbar is always visible at the top or bottom, independent on the page's content) properties. But before we then close the div, we call the renderChildViews() method. In this method we iterate through the toolbar's child views, detect at which position (anchorLocation) the current child view should be displayed and then call the child view's render() method. The following source shows a simplified variant of this method to give you a basic understanding:

renderChildViews: function() {
    for(var i in childViews) {
        var view = this[childViews[i]];

        switch (view.anchorLocation) {
            case M.LEFT:
                this.html += '<div class="ui-btn-left">';
                this.html += view.render();
                this.html += '</div>';
                break;

            case M.CENTER:
                this.html += '<h1>';
                this.html += view.render();
                this.html += '</h1>';
                break;

            case M.RIGHT:
                this.html += '<div class="ui-btn-right">';
                this.html += view.render();
                this.html += '</div>';
                break;
        }
    }
}