javascript - Knockout JS 设置 optionsValue 破坏了我的代码

标签 javascript jquery knockout.js

下面的代码经过简化,请参阅 fiddle :http://jsfiddle.net/QTUqD/7/

基本上我是在数据绑定(bind)下设置设备名称,但我还需要指定发送到数据库的选项值,但是当我设置它时,显示数据绑定(bind)为空。

<script id="extItems" type="text/html">
   <tr>
        <td data-bind="text: device() && device().name"></td>
    </tr>
</script>

<script id="editExts" type="text/html">
    <tr>
        <td>

        <select data-bind="options: $root.devicesForItem($data), optionsText: 'name', value: device, optionsValue: 'id'"></select>

        </td>
    </tr>
</script>
<小时/>
    window.ExtListViewModel = new function () {
    var self = this;
    window.viewModel = self;

    self.list = ko.observableArray();

    self.pageSize = ko.observable(10);
    self.pageIndex = ko.observable(0);
    self.selectedItem = ko.observable();
    self.extQty = ko.observable();
    self.devices = ko.observableArray();
    self.addressList = ko.observableArray(['addressList']);
    self.availableDevices = ko.computed(function() {        
        var usedQuantities = {}; // for each device id, store the used quantity
        self.list().forEach(function(item) {
            var device = item.device();
            if (device) {
                usedQuantities[device.id] = 1 + (usedQuantities[device.id] || 0);
            }
        });

        return self.devices().filter(function(device) {
            var usedQuantity = usedQuantities[device.id] || 0;
            return device.qty > usedQuantity;
        });
    });

    // need this to add back item's selected device to its device-options,
    // and to maintain original order of devices
    self.devicesForItem = function(item) {        
        var availableDevices = self.availableDevices();
        return self.devices().filter(function(device) {
            return device === item.device() || availableDevices.indexOf(device) !== -1;
        });
    }

    self.edit = function (item) {
        if($('#extMngForm').valid()) {
            self.selectedItem(item);
        }
    };

    self.cancel = function () {
        self.selectedItem(null);
    };

    self.add = function () {
        if($('#extMngForm').valid()) {
            var newItem = new Extension();
            self.list.push(newItem);
            self.selectedItem(newItem);
            self.moveToPage(self.maxPageIndex());
        }
    };
    self.remove = function (item) {

            if (confirm('Are you sure you wish to delete this item?')) {

                    self.list.remove(item);
                    if (self.pageIndex() > self.maxPageIndex()) {
                        self.moveToPage(self.maxPageIndex());
                    }

            }
            $('.error').hide();
    };
    self.save = function () {
        if($('#extMngForm').valid()) {
            self.selectedItem(null);
        };
    };

    self.templateToUse = function (item) {
        return self.selectedItem() === item ? 'editExts' : 'extItems';
    };

    self.pagedList = ko.dependentObservable(function () {
        var size = self.pageSize();
        var start = self.pageIndex() * size;
        return self.list.slice(start, start + size);
    });
    self.maxPageIndex = ko.dependentObservable(function () {
        return Math.ceil(self.list().length / self.pageSize()) - 1;
    });
    self.previousPage = function () {
        if (self.pageIndex() > 0) {
            self.pageIndex(self.pageIndex() - 1);
        }
    };
    self.nextPage = function () {
        if (self.pageIndex() < self.maxPageIndex()) {
            self.pageIndex(self.pageIndex() + 1);
        }
    };
    self.allPages = ko.dependentObservable(function () {
        var pages = [];
        for (i = 0; i <= self.maxPageIndex() ; i++) {
            pages.push({ pageNumber: (i + 1) });
        }
        return pages;
    });
    self.moveToPage = function (index) {
        self.pageIndex(index);
    };


};

ko.applyBindings(ExtListViewModel, document.getElementById('extMngForm'));

function Extension(extension, name, email, vmpin, device, macAddress, shipTo){
    this.extension = ko.observable(extension);
    this.name = ko.observable(name);
    this.email = ko.observable(email);
    this.vmpin = ko.observable(vmpin);
    this.device = ko.observable(device);
    this.macAddress = ko.observable(macAddress);
    this.shipTo = ko.observable(shipTo);
}

最佳答案

当您使用optionsValue时,KO会将属性值写入您针对value绑定(bind)的任何内容。因此,它现在将使用 id 而不是对象来填充值。

有几种方法可以解决这种情况,即您既需要值(用于发送到数据库)又需要对象(用于绑定(bind) UI 的其他部分)。

一个非常典型的解决方案是在对象上创建一个计算的可观察对象,该对象获取当前选定的对象并返回 id。

因此,在您的扩展中,您将执行以下操作:

this.device = ko.computed({
    read: function() {
        var device = this.device.asObject();
        return device && device.id;
    },
    deferEvaluation: true, //deferring evaluation, as device.asObject has not been created yet
}, this);

//create this as a sub-observable, so it just disappears when we turn this into JSON and we are just left with the id to send to the DB
this.device.asObject = ko.observable(device);

然后删除 optionsValue 并将 value 绑定(bind)到 device.asObject

在本例中,我添加了 asObject 子可观察对象,因此当您将其转换为 JSON (ko.toJSON) 发送到服务器。此技术唯一棘手的部分是,如果您要从服务器加载现有数据,则需要使用选项中的适当选择来填充 asObject

这是一个示例:http://jsfiddle.net/rniemeyer/Q3PEv/

我使用的另一个选项是继续使用 optionsValue,但随后创建一个自定义绑定(bind)来跟踪单独的可观察对象中的对象。这是一个自定义绑定(bind),它为与 value 绑定(bind)的任何内容创建一个 asObject 子可观察对象。这样你就真的不需要在 View 模型中搞乱它了。

//when using optionsValue, still track the select object in a different observable
ko.bindingHandlers.valueAsObject = {
    init: function(element, valueAccessor, allBindingsAccessor) {
        var value = allBindingsAccessor().value,
            prop = valueAccessor() || 'asObject';

        //add an "asObject" sub-observable to the observable bound against "value"
        if (ko.isObservable(value) && !value[prop]) {
              value[prop] = ko.observable();
        }
    },
    //whenever the value or options are updated, populated the "asObject" observable
    update: function(element, valueAccessor, allBindingsAccessor) {
        var prop = valueAccessor(),
            all = allBindingsAccessor(),
            options = ko.utils.unwrapObservable(all.options),
            value = all.value,
            key = ko.utils.unwrapObservable(value),
            keyProp = all.optionsValue,
            //loop through the options, find a match based on the current "value"
            match = ko.utils.arrayFirst(options, function(option) {
               return option[keyProp] === key;
            });

        //set the "asObject" observable to our match
        value[prop](match);
    }
};

示例:http://jsfiddle.net/rniemeyer/E2kvM/

关于javascript - Knockout JS 设置 optionsValue 破坏了我的代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15719298/

相关文章:

javascript - knockout js点击绑定(bind)不起作用

jquery - 停止单击方法中的数据绑定(bind)事件

javascript - Cucumber 中的未定义步骤,步骤定义调用页面对象函数

javascript - 在express.js中处理POST请求时出现404错误

javascript - 带有 javascript 的 Google BigQuery - 如何获取查询 exe 时间以及查询处理的数据量(大小)

javascript - 如何在需要时仅提交一个隐藏/显示字段数据 - Laravel

由字符串索引的Javascript数组无法使用JSON.stringify转换为JSON?

Javascript/jQuery 是否可以通过另一个函数跳出一个函数?

jquery - jqgrid 显示链接

knockout.js - 如何将 DOM 元素绑定(bind)到 KnockoutJS 中的特定对象?