使用可观察数组进行Knockoutjs映射和验证

时间:2013-09-25 12:55:12

标签: knockout.js knockout-mapping-plugin knockout-validation

我正在尝试创建一个KnockoutJS视图模型,它模拟客户订单和许多订单商品。我想加载初始数据,并验证数据。

到目前为止,我可以使用knockoutjs.mapping加载数据,验证使用映射添加的数据。

// data to load into viewmodel
var modeldata = {
  "OrderID":1,
  "ReturnString":null,
  "CustomerName":"First Customer",
  "OrderDate":"2013-09-16T19:41:40.1639709+01:00",
  "OrderItems": [
     {"ItemID":0,
      "ItemName":"Name_0",
      "ItemPrice":0.0,
      "_destroy":false
     },
     {"ItemID":1,
      "ItemName":"Name_1",
      "ItemPrice":10.0,
      "_destroy":false
     },
     {"ItemID":2,
      "ItemName":"Name_2",
      "ItemPrice":20.0,
      "_destroy":false
     }
   ]
};


// setup defaults for validation
var validationOptions = {
    insertMessages: true,
    decorateElement: true,
    errorElementClass: 'errorCSS',
    messagesOnModified: true,
    debug: true,
    grouping: {
        deep: true,
        observable: false //Needed so added objects AFTER the initial setup get included
    },
};

ko.validation.init(validationOptions);


// define array model
var Item = function () {
    var self = this;
    ItemID = ko.observable();
    ItemName = ko.observable().extend({
      required: { message: '* item name needed' }
    });
    ItemPrice = ko.observable();
    _destroy = false;
}

// define view model
var ViewModel = function (data) {
    var self = this;
    self.OrderID = ko.observable();
    self.ReturnString = ko.observable();
    self.CustomerName = ko.observable().extend({
      required: { message: '* customer name needed' }
    });
    self.OrderDate = ko.observable();
    self.OrderItems = ko.observableArray([]);  // to be array of "Item"

    // create validation group
    self.orderErrors = ko.validation.group(self);
    self.orderItemErrors = ko.validation.group(
      self.OrderItems, { deep: true }
    );

    self.lineItemsValid = function () {
        var LValid = false;
        if (self.orderItemErrors().length > 0) {
            if (self.orderItemErrors()[0] != null) // important to test for null
                LValid = false;
            else 
                LValid = true;
            }
        else  LValid = true 
        if (LValid) {
            return true
            }
        else
            {
            self.orderItemErrors.showAllMessages();
            return false;
            }
    }

    self.orderValid = function () {
        var LValid = false;
        if (self.orderErrors().length > 0) {
            if (self.orderErrors()[0] != null) // important to test for null
                LValid = false;
            else
                LValid = true;
        }
        else LValid = true
        if (LValid) {
            return true
        }
        else {
            self.orderErrors.showAllMessages();
            return false;
        }
    }

    self.isValid = function () {
        if(self.orderValid() & self.lineItemsValid()){
        alert('All ok!')
        }
        else{
        alert('Errors!');}
    }


    // operations
    self.addLineItem = function () {
        self.OrderItems.unshift(new Item());
    }

    self.removeLineItem = function (item) {
        self.OrderItems.destroy(item);
    }

    // load data into model
    self.loadData = function () {
        ko.mapping.fromJS(modeldata, {}, self);
    }


}

$(document).ready(function () {
    var viewModel = new ViewModel()
    ko.applyBindings(viewModel);
});

问题:

(1)我也可以使用click函数添加订单项,但是这些数据似乎没有在可观察数组中更新。但是,当我调用“删除”项目函数时,数组项被标记为已删除。

(2)当我使用映射加载项目并测试验证(required = true)时,对于通过映射加载的项目,它仅 ,而不是在映射完成后添加的订单项目

(3)当我更新通过映射引入的订单项时,更改立即反映在observable数组中,当我更新我在映射后添加的订单项时,没有更新在阵列中。

我在这里有一个JSFiddle:

http://jsfiddle.net/devops/ZsDjh/40/

我确信这与我如何添加到可观察数组有关但却看不到任何明显的东西 - 我显然缺少一些基本的东西......如果有人有任何想法?

感谢

1 个答案:

答案 0 :(得分:8)

只有一些小的疏忽。

首先,让我们跳转到(3)

  

(3)当我更新通过映射引入的订单项时,更改会立即反映在可观察数组中,当我更新映射后添加的订单项时,数组中没有更新。

在你的Item函数中,属性必须绑定到 this (self)。

var Item = function () {
    var self = this;
    self._destroy = false;
    self.ItemID = ko.observable();
    self.ItemName = ko.observable().extend({ required: { message: '* item name needed' } });
    self.ItemPrice = ko.observable();
}

现在为(2),

  

(2)当我使用映射加载项目并测试验证(required = true)时,它仅适用于通过映射加载的项目,而不适用于我在映射完成后添加的订单项目

这是因为您使用映射插件的方式以及如何向OrderItems observableArray添加新项目。

// load data into model
self.loadData = function () {
    ko.mapping.fromJS(modeldata, {}, self);
}

我无法从你的小提琴那里复制这个,但我对可能发生的事情非常了解。

由于映射插件只是从您的JSON创建可观察属性并分别在 self 上分配(或重新分配)属性,因此它不知道每个 OrderItems 对象在数组中是一个 new Item()对象。它只是创建匿名的可观察对象并将它们放在一个新的observableArray中,然后将其分配给 self.OrderItems

您必须指示映射器如何处理您的数组。

// outside your viewmodel
var itemMapping = {
    create: function (options) {
        return new Item(options.data);
    }
};

// load data into model
self.loadData = function () {
    ko.mapping.fromJS(modeldata, { OrderItems: itemMapping }, self);
}

现在我们将JSON数组中的每个项目传递到 itemMapping 创建函数。但是,我们现在有一个问题。 Item函数不带任何参数。所以我们来修复它。而且,尽管如此,让我们让mapper再次帮助我们创建项目的可观察属性。

var Item = function (data) {
    var self = this;
    self._destroy = false;
    //self.ItemID = ko.observable(data.ItemID);
    //self.ItemName = ko.observable(data.ItemName).extend({ required: true });
    //self.ItemPrice = ko.observable(data.ItemPrice);
    ko.mapping.fromJS(data, {}, self);
    self.ItemName.extend({ required: { message: '* item name needed' } });
}

由于我们修改了您的Item函数,我们需要更新 addLineItem 方法。

// operations
self.addLineItem = function () {
    // set the default values
    self.OrderItems.unshift(new Item({ ItemId: null, ItemName: "", ItemPrice: 0 }));
}

现在,摆脱 self.lineItemsValid self.orderValid 方法,并删除 self.orderItemErrors 属性。

重命名并更新 isValid 方法。

self.checkValid = function () {
    if(self.isValid()){
      alert('All ok!');
    }
    else{
      self.orderErrors.showAllMessages();
    }
}

self.isValid()是验证插件创建的方法。你覆盖了它。

并且,更新您的HTML。

<a href="#" data-bind="click: checkValid">Check is valid</a>

最后(1),

  

(1)我还可以使用click函数添加订单项,但是这些数据似乎没有在可观察数组中更新。

这是通过我们对功能进行的修改来解决的。

  

但是,当我调用“删除”项目函数时,数组项会被标记为已删除。

我不确定您是希望将其完全删除还是仅标记为 _destroyed

如果您只是想删除它,请更新 removeLineItem 方法。

self.removeLineItem = function (item) {
    self.OrderItems.remove(item);
}

请参阅Observable Arrays上的淘汰文档,了解删除和销毁方法,以确定最适合您的方法。

这是modified jsFiddle

干杯!

P.S。请注意,因为这个答案涂抹了你的车轮! :)