Mike Flynn
Mike Flynn

Reputation: 24315

Upgrade to knockout.js 2.2.0 causes foreach template not to render anymore

I just upgraded to knockout.js 2.2.0 and a foreach statement isnt working anymore with a computed observable. If I switched back to 2.1 it works. The computed observable updatedValues doesnt repopulate the foreach in the html below. Keep in mind the html below is dynamically inserted into the dom on a single ajax page and applied bindings.

ko.bindingHandlers.fields = {
        init: function (element, valueAccessor) {

            var value = ko.utils.unwrapObservable(valueAccessor());

            app.viewModel.members.meta = {
                values: ko.observableArray(value.values),
                remove: function () {
                    app.viewModel.members.meta.values.remove(this);
                    return false;
                },
                add: function () {
                    app.viewModel.members.meta.values.push({ Name: '', Value: '', Index: ko.observable(app.viewModel.members.meta.values().length) });
                    return false;
                },
                max: value.max
            };

            app.viewModel.members.meta.updatedValues = ko.computed(function () {
                if (this.values()) {
                    for (var i = 0; i < this.values().length; i++) {
                        if (this.values()[i].Index)
                            this.values()[i].Index(i);
                        else
                            this.values()[i].Index = ko.observable(i);
                    }
                }

                return this.values;
            }, app.viewModel.members.meta);
        }
    };

<div class="control-group">
    @Html.LabelFor(q => q, "Custom Fields") 
    <div class="controls">
        <div class="meta" data-bind="fields: { values: @Model.Meta.OrderBy(q => q.Name).ToJSON(), max: @Model.MaxCount }">
            <div data-bind="foreach: members.meta.updatedValues">
                <div class="form-inline">
                    <div class="input-prepend">
                        <span class="add-on">Name</span>
                        @Html.TextBox("Key", string.Empty, new { maxvalue = "100", data_bind = "value: Name, attr: { name: '" + Model.PropertyName + "[' + Index() + '].Name', id: '" + Model.PropertyName + "[' + Index() + '].Name' }" })
                    </div>
                    @if(!Model.HideValues)
                    {
                        <div class="input-prepend">
                            <span class="add-on">Value</span>
                            @Html.TextBox("Value", string.Empty, new {data_bind = "value: Value, attr: { name: '" + Model.PropertyName + "[' + Index() + '].Value', id: '" + Model.PropertyName + "[' + Index() + '].Value' }"})
                        </div>
                    }
                    <a href="#" class="btn btn-mini btn-danger" title="Remove Field" data-bind="click: $parent.members.meta.remove"><i class="icon-minus icon-white"></i></a>
                </div>
            </div>
            <div class="control-group">
                @Html.Button("New Field", new { type="button", @class="btn", data_bind = "click: members.meta.add, visible: members.meta.max >= members.meta.values().length" })
                @Html.Partial(MVC.Shared.Views.Controls.Help, new HelpModel { Url = Url.Action(Model.ActionResult ?? MVC.Members.Dashboard.CustomFieldsHelp()), Title = Model.Title ?? "Custom Fields" })
            </div>
        </div>
    </div>
</div>

Upvotes: 1

Views: 1255

Answers (1)

Matija Grcic
Matija Grcic

Reputation: 13371

Have a look about the updates done here Knockout 2.2.0 released.

foreach and template enhancements

When a function is used for the template name, it now receives the binding context as the second argument.

The foreach functionality now does its best to understand items that were moved and move their content rather than re-render them.

beforeMove and afterMove callbacks can now be configured when using foreach functionality.

The foreach and template bindings now accept an as option to give $data an alias.

The afterRender, beforeRemove, afterAdd, beforeMove, and afterMove callbacks will no longer cause dependencies.

I think that computed should be returning a plain array so change it to values: [] because in Knockout 2.2 there is no twice-unwraping of the observables when using foreach().

You can always log an issue here: https://github.com/SteveSanderson/knockout/issues

Upvotes: 2

Related Questions