jQuery templates plugin: how to create two-way binding?

7.3k views Asked by At

I started using jQuery templates plugin (the one Microsoft created), but now I face this problem: the template is for a bunch of forms bound to an array of objects; when I change something on one of the forms, I want the bound object to update and I can't figure out how to automate that.

Here's a simple example (real life template and object are much more complex) :

<!-- Template -->
<script type="text/html" id="tmplTest">
    <input type="text" value="${textvalue}"/>
</script>

<!-- object to bind -->
<script type="text/javascript">
    var obj = [{textvalue : "text1"},{textvalue : "text2"}]

    jQuery("#tmplTest").tmpl(obj)
</script>

This will populate two textboxes, each bound to a value from corresponding object. Now, if I change a value in one of the textboxes, I need to update corresponding data object's value. Any idea how to do that?

2

There are 2 answers

11
Sergei Golos On BEST ANSWER

jQuery template doesn't actually implement two-way data binding, but another Microsoft developed jQuery plugin does. This Scott Guthrie post actually covers both the tmpl plug in and Data Linking plugin. Scroll down to "Support for Client Data-Linking" where Scott goes into detail on how the Data Linking plug in works.

However, for two way data binding, i find the knockoutjs extension to be much better and cleaner. The declarative syntax keeps the markup clean and the custom data binding overrides allow for a multitude of applications. Finally the mapping plugin is pretty great for processing JSON from the server into binding. Finally knockoutjs also has bindings based on tmpl plugin as well.

Good luck with your problem.

EDIT: updated Code Example

Scrips required:

<script src="/Scripts/jquery-1.5.0.min.js" type="text/javascript"></script>    
<script src="/Scripts/jquery.tmpl.js" type="text/javascript"></script> 
<script src="/Scripts/knockout.js" type="text/javascript"></script>      
<script src="/Scripts/knockout.mapping.js" type="text/javascript"></script>    

Then here is the meat and potatoes

<!-- div for loading the template -->
<div data-bind='template: { name: "tmplTest", foreach: viewModel.list }'>    
</div>

<!-- your template -->
<script type='text/html' id='tmplTest'>
    <div>        
        <span data-bind='text: textvalue, uniqueName: true'></span>
        <input data-bind='value: textvalue, uniqueName: true, valueUpdate:"afterkeydown"'/>
    </div>
</script>

<script type='text/javascript'>
       var viewModel = ko.mapping.fromJS(
        {            
            list:[  { textvalue: "text1" },
                    { textvalue: "text2"}   ]
                }); 

        $(function() {
            ko.applyBindings(viewModel);
        });
 </script>
3
Josiah Ruddell On

You could write your own data link. Note: I needed the index to make this work, so I added your data into an array, and put an each in the template. If someone knows how to get the index without using an each please add :) jsfiddle link

<script type="text/html" id="tmplTest">
    {{each inputs}}
    <input type="text" class="datalink" data-key="textvalue" data-index="${$index}" value="${$value.textvalue}"/>
    {{/each}}
</script>
<div id="output"></div>


$.extend(window, {
    data:[
        {inputs: [{textvalue:"text1"},{textvalue:"text2"}]}
    ]
});

$('#output').append($("#tmplTest").tmpl(window.data));

$('#output .datalink').live('change', function(){ // update object on change
    var $this = $(this),
        d = $this.data();
    window.data[0].inputs[d.index*1][d.key] = $this.val();
    console.log(window.data[0].inputs);
});