Helma Logo
main list history

Helma 2 Templates - czv

./Site/list.view.control

    // function at Site.prototype.views.list.control(that)
    if (this.topics.count()) {
        that.topics = {};
        for (var topic in this.topics) {
            that.topics.name = this.topics[topic].name;
            that.topics.count = this.topics[topic].comments.count();
        }
    }

./Site/list.view

    <table border="1">
        <tr check="topics">
            <th lookup="true">Topic Name</th>
            <th lookup="true">Comment Count</th>
        </tr>
        <tr loop="topics" class="%% 'rowcolor'+ ($topics % 2) %%">
            <td>%% topics$.name %%</td>
            <td check="topics$.count == 0" lookup="true">
                No comments yet</td>
            <td check="topics$.count == 1" lookup="true">
                %% topics$.count %% comment</td>
            <td check="topics$.count > 1" lookup="true">
                %% topics$.count %% comments</td>
        </tr>
        <tr check="!topics">
            <td colspan="2" align="center" lookup="true">
                This list is empty</td>
        </tr>
    </table>

./Site/list.control

    // function at Site.prototype.controls.list
    var content = {
        main : this.views.list.render()
    };
    var page = Page.views.default.render(content);
    return page;

this.views.list.control() is invoked when the list view is rendered. When it is invoked, "that" is the object which was passed as parameter of the render() method or otherwise an empty/default object. "that" is the object that will be used as the Javascript context when rendering the view.

Views would be E4X Javascript XML objects. When Helma would render these views, it would parse them for "lookup", "check" and "loop" attributes and then evaluate them in the context of "that".

When parsing the "loop" attributes, Helma would loop through the indicated object (topics) with "for(var $topics in topics)" and repeat the specified element (the table row) with topics$ == topics[$topics]

When parsing the "lookup" attributes, Helma would check if a lookup handler function exists and if so would apply it to the element's content.

this.controls.list would be the function that is called in order to handle the request, similar to todays this.list_action.

As a reality check for this proposal I so far came up with a specialized prototype of the rendering method. The idea seems to work quite nicely already with current builds of Helma 2. I thought E4X could make implementing this very easy, but I was surprised that it really turned out that way in practice:

    function handle_get() {
        // first prototype for the next incarnation of mocha objects
        
        // the render function, providing the magic
        var render = function(view) {
            // loop
            var toLoop = view..*.(@loop.toSource() != '<></>');
            for (var item in toLoop) {
                var dataName = toLoop[item].@loop.toString();
                delete toLoop[item].@loop;
                var data = eval(dataName);
                var stamp = toLoop[item].toString();
                for (var i in data) {
                    var stencil = stamp.replace(
                        eval("/%% "+dataName+"\\$\\.(.+) %%/g"),
                        '{ '+dataName+'['+i+'].$1 }'
                    );
                    stencil = stencil.replace(
                        eval('/"'+dataName+'\\$/g'),
                        '"'+dataName+'['+i+']'
                    );
                    stencil = stencil.replace(
                        eval('/"%% (.<a href="http://helma.org/wiki/create?name=)\\$'+dataName+'(." class="undefinedLink">)\\$'+dataName+'(.</a>) %%"/g'),
                        '{ $1'+i+'$2 }'
                    );
                    toLoop.parent().insertChildBefore(
                        toLoop[item],
                        eval(stencil)
                    );
                }
                delete toLoop[item].parent().*[toLoop[item].childIndex()];
            }
           
            // lookup
            var toLookup = view..*.(@lookup.toSource() != '<></>');
            for (var item in toLookup) {
                delete toLookup[item].@lookup;
                var lookedup = '('+toLookup[item].children()[0]+')';
                toLookup[item].setChildren(lookedup);
            }
       
            // check
            var toCheck = view..*.(@check.toSource() != '<></>');
            for (var item in toCheck) {
                if (eval(toCheck[item].@check.toString()))
                    delete toCheck[item].@check;
                else
                    toCheck[item] = '';
            }
            return view;
        }
       
        // an example skin
        var myview = <table border="1">
            <tr check="topics">
                <th lookup="true">Topic Name</th>
                <th lookup="true">Comment Count</th>
            </tr>
            <tr loop="topics" class="%% 'rowcolor'+ ($topics % 2) %%">
                <td>%% topics$.name %%</td>
                <td check="topics$.count == 0" lookup="true">
                    No comments yet</td>
                <td check="topics$.count == 1" lookup="true">
                    %% topics$.count %% comment</td>
                <td check="topics$.count > 1" lookup="true">
                    %% topics$.count %% comments</td>
            </tr>
            <tr check="!topics">
                <td colspan="2" align="center" lookup="true">
                    This list is empty</td>
            </tr>
        </table>;
       
        // the example data
        var topics = [
            {name : 'Peter', count : 0},
            {name : 'Wolf', count : 2},
            {name : 'John', count : 1},
            {name : 'Andy', count : 8}
        ];
       
        // rendering the list
        var listtable = render(myview.copy());
    
        // rendering the empty table
        topics = null;
        var emptytable = render(myview.copy());
       
        // assembling the page
        var page = <html><body>{ listtable }<hr/>{ emptytable }</body></html>;
       
        // writing the page source to the reponse buffer
        res.contentType = "text/html";
        res.write(page.toSource());
    }

Resulting in the following output:

    <html>
      <body>
        <table border="1">
          <tr>
            <th>(Topic Name)</th>
            <th>(Comment Count)</th>
          </tr>
          <tr class="rowcolor0">
    
            <td>Peter</td>
            <td>(No comments yet)</td>
          </tr>
          <tr class="rowcolor1">
            <td>Wolf</td>
            <td>(2 comments)</td>
          </tr>
    
          <tr class="rowcolor0">
            <td>John</td>
            <td>(1 comment)</td>
          </tr>
          <tr class="rowcolor1">
            <td>Andy</td>
            <td>(8 comments)</td>
    
          </tr>
        </table>
        <hr/>
        <table border="1">
          <tr>
            <td colspan="2" align="center">
                (This list is empty)</td>
          </tr>
        </table>
    
      </body>
    </html>

Links to this page: Helma 2 Templates