knockout.js – 复杂对象的选项绑定selectedObject总是不在列表中

我经常有这样的对象:
var objectToMap = {
    Id: 123,UserType:{
        Id: 456,Name:"Some"
    }
};

当我需要在用户界面中修改此对象时,我想从某个列表中进行选择.例如,数组:

var list = [
    {Id:456,Name: "Some"},{Id:567,Name: "Some other name"}];

我使用选项绑定,类似的东西:

<select data-bind="options: list,optionsText: 'Name',value: UserType,optionsCaption: 'Select...'"></select>

问题是,淘汰赛认为来自objectToMap {Id:456,名称:“Some”}的UserType与列表中的对象{Id:456,名称:“Some”}不同.因此,自动UserType从列表中获取未定义但不需要的选项.

我通过这种方式克服了问题:我使用ko.utils.arrayFirst在列表中找到项目并替换objectToMap中的UserType.但这看起来很难看,需要额外的编码.有更好的方法吗?

解决方法

即使我认为淘汰行为是正确的,正如我在评论中提到的,这里有几种方法可以解决您的问题(请注意下面的所有代码删除错误检查;例如,您应该处理没有错误检查的情况具有给定id的项目在列表中找到,下面的代码不是这样的.

您还可以创建一个自定义bindingHandler,它确保属性值设置为list属性中的项目,基于其他一些形式的相等性(例如它们的JSON表示相等).但是,对我来说,感觉好像这种逻辑(将属性值与list属性中的项匹配)更适合视图模型或可能是扩展器,因为这些方法比涉及bindingHandler更容易创建单元测试这个逻辑.

备选方案1 – 使用optionsValue

使用optionsValue绑定,您可以将选择列表设置为绑定到实际ID.这在很多方面实际上都是’正确’的方式,因为objectToMap可能不应该包含整个UserType,而只是包含id.您可以在viewmodel上添加一个计算属性,该属性根据id获取正确的UserType.

self.myObject = {
    id: 9348,userTypeId: ko.observable(2)
};
//Add the userType computed property to the object
self.myObject.userType = ko.computed(function(){
    var id = self.myObject.userTypeId();
    return self.items.filter(function(item){
        return item.id === id;
    })[0];
});

然后,您将绑定到userTypeId属性,如下所示:

<select data-bind="options:items,value: myObject.userTypeId,optionsValue: 'id',optionsText: 'name'">
</select>

代码可以在jsfiddle中进行测试:http://jsfiddle.net/6Sg29/

备选方案2 – 创建查找扩展程序

如果您确实不想向对象添加其他属性,则可以创建一次性查找扩展器,该查找扩展器将根据使用id查找正确项目的比较函数从列表中查找值.例如,如果名称属性在对象上的项目与列表中的项目之间不同,则观察此解决方案将隐藏不匹配的数据.

这种扩展器的一个例子是:

ko.extenders.oneTimeLookup = function(target,options) {
    var comparePropertyName = options.compare;
    var item = target();
    var foundItem = options.list.filter(function(listItem){
        return item[comparePropertyName] === listItem[comparePropertyName];
    })[0];
    target(foundItem);
    return target;
};

扩展器的使用方式如下:

self.myObject = {
    id: 9348,userType: ko.observable({
        id: 2,name: 'OriginalName'
    }).extend({ oneTimeLookup: { list: self.items,compare: 'id' } })
};

在这种情况下,绑定可以只是一个简单的选项绑定,指向已扩展的userType observable的值,因为我们已经执行了查找

<select data-bind="options:items,value: myObject.userType,optionsText: 'name'">
</select>

可以在http://jsfiddle.net/qGFQ7/的jsfiddle中测试此代码

备选方案3 – 在创建对象时进行手动查找.

这是您已经使用的解决方案,您可以在进行任何绑定之前进行手动查找并使用列表中的项替换该对象.这类似于备选方案2,但可重用性较低.哪一个更清楚地表明你的意图,你必须自己决定.

摘要

我建议使用第一种方法,因为原始对象可能不应该保留对完整项目的引用,因为您有一个要从中检索项目的查找列表.在第二种和第三种方法中,您只是丢弃可能隐藏潜在错误的数据.但这只是我对这个问题的一般看法,当然可能会有一些特殊情况我会考虑替代2或3.

相关文章

前言 做过web项目开发的人对layer弹层组件肯定不陌生,作为l...
前言 前端表单校验是过滤无效数据、假数据、有毒数据的第一步...
前言 图片上传是web项目常见的需求,我基于之前的博客的代码...
前言 导出Excel文件这个功能,通常都是在后端实现返回前端一...
前言 众所周知,js是单线程的,从上往下,从左往右依次执行,...
前言 项目开发中,我们可能会碰到这样的需求:select标签,禁...