1
votes

I've try use jsrender/jsviews first time. It looks awesome, but I'm not find clear documentation or example how to dynamically bind event handlers for generated content.

For example pure jQuery old approach was:

Code from bean class to render collection of objects:

container = $('#tabs-my');
this.load( // Obtain array of objects
    $.proxy(function(list){
        container.html('');
        list.forEach(
            $.proxy(function(it, i){
                container.append(this.renderItem(it));
            }
            ,this)
        );
    }
    ,this)
);

And in object itself render method:

,renderItem: function(/*Specialist*/ it){
    var container = $('<div class="specialist-item" />')
    container.append(
        $('<span class="x">Remove</span>').click($.proxy(function(){
            this.removeSpecialistFromList(it.id)
        }
        ,this))
    );
    container.append(
        $('<span class="x">Edit</span>').click($.proxy(function(){
            this.renderSaveForm(container)
        }
        ,this))
    );
    container.append('<p><b>' + it.name + '</b> &nbsp; <i>' + it.phone + '</i></p>' +
    '<p>' + it.skill + '</p>' +
    ( it.city ? '<p>' + it.city.name + '</p>' : ''));
    return container;
}

Note I bind handler via closure content for current object without use any external identificators in tag itself. Then I try use templating to separate content from visialisation.

My template:

<div class="specialists-list">
    Items in list: {{:specialists.length}}
    {^{for specialists}}
        <div class="specialist-item">
            <span class="x">Remove</span><span class="x">Edit</span>

            <p><b class="name">{{:name}}</b> &nbsp; <i class="phone">{{:phone}}</i></p>
            <p class="skill">{{:skill}}</p>
            <p class="city">{{:city.name}}</p>
        </div>
    {{/for}}
</div>

And rendered like:

var template = $.templates({
    specialistsTmpl: tmpl
});
$.templates.specialistsTmpl.link(container, {
    specialists: list
});

I realize it could be done using common handlers in attributes something like:

<span class="x" data-id="{{:id}}">Edit</span>

And then try obtain that object again from external. But it is workaround and is not desired. Is there way to bind handlers in template or via helpers, custom tags?

2
I realise events like onAfterCreate (github.com/BorisMoore/jsrender/issues/48) may help, but is it documented more somewhere? How I can filter elements on it and obtain content on what it working (for example current element of {{for}} loop)?Hubbitus

2 Answers

2
votes

There are many samples on http://www.jsviews.com which include event binding, and handlers such as for removing or inserting data items. Did you look at the examples here: http://www.jsviews.com/#samples/editable - you will find four different approaches to the same scenario.

For example:

Template

{^{for languages}}
  <input data-link="name" />
  <img class="removeLanguage" .../>
{{/for}}

Code:

$.link.movieTmpl("#movieList", app)
  .on("click", ".removeLanguage", function() {
    var view = $.view(this);
    $.observable(view.parent.data).remove(view.index);
    return false;
  });

Note the use of var view $.view(this); - you pass in the element (this) that is clicked on, and $.view(clickedElement) returns you the view, from which you can get view.index (the item index - in the case of iteration over an array), view.data (the current data item - in you case that would be the specialist item), view.parent.data (in your case, the specialists array) etc.

Of course since view.data is the current data item, if your data item is in effect a view model, with methods, you can call a method: view.data.someMethod(...).

But as an alternative to using the jQuery on() for binding handlers, you can use declarative binding directly in the template like this:

{^{for specialists}}
    <div class="specialist-item">
        <span class="x" data-link="{on removeMe}">Remove</span> ...
        ...
    </div>
{{/for}}

where I assume your specialist has a removeMe() method. The "{on ...}" binding binds by default to the click event, and you can bind to methods on your data, or to helpers, etc.

Take a look at this example: http://jsfiddle.net/BorisMoore/cGZZP/ - which uses {on ...} for binding to helpers for modifying a two-dimensional array.

I hope to create some new samples using {on ...} binding before too long.

BTW I don't recommend using the onAfterCreate for doing event binding. Either of the above approaches are better, and will ensure correctly disposal of event bindings.

0
votes

I'm not 100% sure I completely understand but I believe I do. You want to use a different approach. Look at .on for jQuery. I've switched to using it most all the time. The call back function doesn't need to change. It is really pretty nice.

In my case, I was creating thousands of event handlers and it was killing my performance. So I switched to using .on and it solved my problem.

This doesn't exactly answer your question... but I think its a better solution.