Using Knockout.js with newest jQuery Files and ASP.NET MVC

Ciel picture Ciel · Oct 26, 2010 · Viewed 7.4k times · Source

I am attempting to use Knockout.js with ASP.NET MVC 3.0 (Title gave it away, didn't it?!)

http://knockout.js.com

I am running into some problems (more related to the new jQuery Tmpl Engine than ASP.NET MVC 3.0).

I am using Steve Sanderson's Example Program in my test, and have mostly replicated his results with the new Razor View Engine (I do not believe Razor has anything to do with my problem).

http://blog.stevensanderson.com/2010/07/12/editing-a-variable-length-list-knockout-style/

However I want to do more with the natural jquery binding, instead of the HTML binding attributes. This is described in detail on knockout's tutorial. http://knockoutjs.com/documentation/template-binding.html

However I am unable to reproduce this as it explains. I will show below my View Code. My problem is coming from the fact that {{foreach (i, gift) gifts}} doesn't work. I have tried many variants ( {{foreach (i, gift) gifts()}} as I have seen in other examples).

I am using the latest knockout.js file. I am using jQuery 1.4.3. I am using http://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.js for templating engine. However, I have also tried this using the same tmpl.js file that is included on knockous.js's website, and I get the same results.

When using jQuery Templating, Per the current specification, the template does not render.

The jQuery Template Tags Documentation is found Here : http://api.jquery.com/category/plugins/templates/template-tags/

In case anyone was confused about my exact model. If I replace {{foreach (i, gift) gifts}} with {{foreach gift}}, then the model renders and behaves correctly, but I cannot use the jQuery native ${property} declarations for anything.

HTML

@model IEnumerable<Knockout.GiftModel>
@using System.Web.Script.Serialization;

@{
    View.Title = "Index";
    Layout = "~/Views/Shared/_Site.cshtml";
}

    <h2>Gift list editor</h2>

    <form class="giftListEditor" action="/home/index" method="post" >
        <table> 
            <tbody data-bind="template:'giftRowTemplate'"></tbody> 
        </table>

        <button data-bind="click: addGift">Add Gift</button>
        <button data-bind="enable: gifts().length > 0" type="submit">Submit</button>
    </form>

    <script type="text/html" id="giftRowTemplate">
        {{each (i, gift) gifts}}
        <tr> 
            <td>Gift name: <input class="required" data-bind="value: Title, uniqueName: true" /> ${Title} </td> 
            <td>Price: $ <input class="required number" data-bind="value: Price, uniqueName: true"/></td> 
            <td><a href="#" data-bind="click: function() { viewModel.removeGift( $value ) }">Delete</a></td> 
        </tr>
        {{/each}}
    </script>

    <script type="text/javascript">
        var initialData = @(new HtmlString(Model.ToJson()));
        var viewModel = {
            gifts: ko.observableArray(initialData),
            addGift: function () {
                this.gifts.push({ Title: "", Price: "" });
            },

            removeGift: function (gift) {
                this.gifts.remove(gift);
            },

            save: function () {
                ko.utils.postJson(location.href, { gifts: this.gifts });
            }
        };

        ko.applyBindings(viewModel);
        $("form").validate({ submitHandler: function() { viewModel.save() } });
    </script>

Answer

Joel Cunningham picture Joel Cunningham · Oct 27, 2010

I would approach this differently. I would use the following line:

<tbody data-bind='template: { name: "giftRowTemplate", foreach: gifts }'></tbody>

instead of:

<tbody data-bind="template:'giftRowTemplate'"></tbody>

That way you dont need to use the {{each (i, gift) gifts}} line in the template that is giving you trouble.