0

下面的代码被简化了,见fiddle:http: //jsfiddle.net/QTUqD/7/

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

<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);
}
4

1 回答 1

2

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

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

一个非常典型的解决方案是在你的对象上创建一个计算的 observable,它接受当前选择的对象并返回 id。

所以,在你的Extension你会做这样的事情:

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绑定valuedevice.asObject

在这种情况下,我添加了asObjectsub-observable,因此当您将其转换为 JSON ( ko.toJSON) 发送到服务器时,它就会消失。这种技术唯一棘手的部分是,如果您从服务器加载现有数据,那么您需要asObject从您的选项中填充适当的选择。

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

Another option that I have used is to continue to use optionsValue, but then to create a custom binding that tracks the object in a separate observable. Here is a custom binding that creates an asObject sub-observable for whatever is bound against value. This way you really don't need to mess with it at all in your view model.

//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);
    }
};

Sample here: http://jsfiddle.net/rniemeyer/E2kvM/

于 2013-03-30T16:03:45.510 回答