Knockout计算的数组未正确更新

时间:2015-01-13 17:01:08

标签: html knockout.js

根据我的viewmodel,我无法更新视图。总结我正在做的事情:我有一系列项目(AllCredentials)。项目的一个属性是“IsSelected”值(true或false)。当项的值为false(创建AllCredentials数组时的默认值)时,该项将显示在“UnselectedCredentials”列表中。当双击此列表中的项目时,“IsSelected”的值将被切换,从而使其显示在另一个“SelectedCredentials”列表中。

当我测试它时,双击时“IsSelected”值正在切换,但计算出的数组(其中2个,每个对应2个列表)没有相应地添加/删除(如当双击“UnselectedCredentials”列表中的某个项目时,“IsSelected”的值应该从false切换为true,从而将其从该列表中删除并将其添加到“SelectedCredentials”列表中。

这是我的代码:

视图模型:

var TestNWJS = TestNWJS || {};

TestNWJS.QualificationList = (function () {

//private functions

function FindUnselectedCredentials() { //function to populate UnselectedCredentials list

    var filtering = ko.utils.arrayFilter(TestNWJS.QualificationList.ViewModel.AllCredentials(), function (item) {
        return item.IsSelected === false;
    });

    return filtering;
}

function FindSelectedCredentials() { //function to populate SelectedCredentials list

    var filtering = ko.utils.arrayFilter(TestNWJS.QualificationList.ViewModel.AllCredentials(), function (item) {
        return item.IsSelected === true;
    });

    return filtering;
}

function CreateQualificationModel(allCredentialsList) {
    TestNWJS.QualificationList.ViewModel = {};

    TestNWJS.QualificationList.ViewModel.AllCredentials = ko.observableArray(allCredentialsList);

    TestNWJS.QualificationList.ViewModel.UnselectedCredentials = ko.computed(FindUnselectedCredentials, this);
    TestNWJS.QualificationList.ViewModel.SelectedCredentials = ko.computed(FindSelectedCredentials, this);

    TestNWJS.QualificationList.ViewModel.AllCredentials.extend({ notify: 'always' });
    TestNWJS.QualificationList.ViewModel.UnselectedCredentials.extend({ notify: 'always' });
    TestNWJS.QualificationList.ViewModel.SelectedCredentials.extend({ notify: 'always' });
}

function toggleselected(id) {
    var match = ko.utils.arrayFirst(TestNWJS.QualificationList.ViewModel.AllCredentials(), function (item) {
        id = parseInt(id);
        return id === item.Id;
    });

    match.IsSelected = !match.IsSelected;
    return match;
}

//public function
return {

    Init: function (allCredentialsList) {
        CreateQualificationModel(allCredentialsList);

        //when you select something from the dropdown this will happen.
        $("select[name='QualificationFilter']").change(function (e) {
            var id = $(this).val();
            e.preventDefault();
            var form = $(e.target).parents("form");
            var url = window.location.href.substr(0, window.location.href.lastIndexOf("QualificationList") + 17)
            form.attr("action", url + "?Id=" + id);
            form.submit();
        });

        $("#UnselectedCredentialsList").live('dblclick', function (e) {
            toggleselected(this.value);
        });

        $("#SelectedCredentialsList").live('dblclick', function (e) {
            toggleselected(this.value);
        });

        ko.applyBindings(TestNWJS.QualificationList.ViewModel);
    }
}

})();

查看:

<div>
    <table>
        <tr>
            <td class="fieldName_td">
                @Html.Label("Available Credentials")
            </td>
            <td class="fieldData_td">
                <select data-bind="options: UnselectedCredentials,
                                   optionsText: 'Name',
                                   optionsValue: 'Id'"
                                   size="10" multiple="multiple" id="UnselectedCredentialsList"></select>
            </td>
        </tr>
    </table>
</div>

<div>
    <table>
        <tr>
            <td class="fieldName_td">
                @Html.Label("Selected Credentials")
            </td>
            <td class="fieldData_td">
                <select data-bind="options: SelectedCredentials,
                                   optionsText: 'Name',
                                   optionsValue: 'Id'"
                                   size="10" multiple="multiple" id="SelectedCredentialsList"></select>
            </td>
        </tr>
    </table>
</div>
}
@section scripts {
    @Scripts.Render("~/Scripts/knockout-2.2.1.js", "~/jscripts/Administration/Interfaces/QualificationList.js", "~/Scripts/knockout.mapping-latest.js")

<script type="text/javascript">
    $(function () {
        TestNWJS.QualificationList.Init(@Html.Raw(Model.JsonAllCredentials));
    })
</script>

}

只是为了澄清一下,在页面的初始加载时,“UnselectedCredentials”列表正确显示(意味着AllCredentials数组中所有凭据的“IsSelected”值等于false(这是最初的所有内容)正在出现) 。我遇到的问题与双击触发器切换值后视图(和可能的视图模型)无法正确更新有关。

1 个答案:

答案 0 :(得分:1)

我认为问题可能是JSON数据的初始加载。如果我没有弄错,Knockout不会自动使JSON对象的属性可观察。 observableArray仅查看在数组中添加或删除项目的时间,而不是其中的项目是否已更改。您可能需要为JSON数据编写反序列化器,或者使用构造函数创建一个Credential类(使所有属性都可观察),您可以在将它们放入可观察数组之前将JSON数据的元素提供给它们。

查看Knockout Mapping Plugin。这可能会有所帮助

我不确定你是否受到视图模型的约束,但我还建议简化一下。没有理由拥有多个数组并来回移动项目。一个更简单的实现是拥有一个数组,根据Selection属性是true还是false显示在表中。像这样:

<div>
    <table>
        <tr>
            <td class="fieldName_td">
                @Html.Label("Available Credentials")
            </td>
            <td class="fieldData_td">
                 <table data-bind="foreach:Credentials">
                     <!-- ko if: !Selected -->
                     <tr>
                         <td data-bind="text><select data-bind="text: Name, $root.click: toggleSelected></td>
                     </tr>
                 </table>
            </td>
        </tr>
    </table>
</div>

<div>
    <table>
        <tr>
            <td class="fieldName_td">
                @Html.Label("Selected Credentials")
            </td>
            <td class="fieldData_td">
                 <table data-bind="foreach:Credentials">
                     <!-- ko if: Selected -->
                     <tr>
                         <td data-bind="text><select data-bind="text: Name, $root.click: toggleSelected></td>
                     </tr>
                 </table>
            </td>
        </tr>
    </table>
</div>
}

在viewmodel中,将所有凭据放在一个可观察数组中(凭证的每个属性也应该是可观察的),然后viewmodel必须做的唯一工作(只要Selected属性是可观察的)

toggleSelection = function(credential) {
    credential.Selected = !credential.Selected;
}

然后,当您需要使用所选凭据时,只需过滤数组以查找所选凭据。

祝你好运。

编辑:关于Credential类,假设唯一的属性是Name和Selected,可能看起来像:

function Credential(name, selected) {
    this.Name = ko.observable(name);
    this.Selected = ko.observable(selected);
}

然后你可以遍历你的JSON数据数组

 var Credentials = ko.observableArray();
 credentialsFromJson.forEach(function(c) {
     var credential = new Credential(c.Name, c.Selected);
     Credentials.push(credential);
 }

这使得每个属性都可以观察到。如果您想要观看很多属性,请查看上面提到的ko.mapping插件。

这有帮助吗?如果我正确地读取代码,计算出的数组不会更新,因为KO正在观察没有发生任何事情,并且使Selected属性可观察应该可以解决这个问题。