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

Posted

技术标签:

【中文标题】使用可观察数组进行 Knockoutjs 映射和验证【英文标题】:Knockoutjs mapping and validation with observable arrays 【发布时间】:2013-10-01 01:57:01 【问题描述】:

我正在尝试创建一个 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 函数添加订单项,但是这些数据似乎没有在 observable 数组中得到更新。但是,当我调用“删除”项函数时,数组项被标记为已删除。

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

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

我在这里有一个 JSFiddle:

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

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

谢谢

【问题讨论】:

【参考方案1】:

只是有一些小疏忽而已。

首先,让我们跳到(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 数组中的每个项目传递给 itemMappingcreate 函数。然而,我们现在有一个问题。 Item 函数不带任何参数。所以让我们解决这个问题。并且,在此过程中,让映射器再次帮助我们为我们的 Item 创建可观察属性。

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.lineItemsValidself.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) 我也可以使用点击功能添加订单商品,但这些商品的数据似乎没有在可观察数组中更新。

我们对 Item 函数所做的修改已解决此问题。

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

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

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

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

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

这里是modified jsFiddle。

干杯!

附:请+1,因为这个答案润滑了你的***! :)

【讨论】:

嗨罗杰 - 真的非常感谢你,给***上油了! ...非常感谢。

以上是关于使用可观察数组进行 Knockoutjs 映射和验证的主要内容,如果未能解决你的问题,请参考以下文章

knockoutjs 可观察对象绑定的可观察数组

将 KnockoutJS 可观察数组传递给 HTTP Post 控制器方法的 AJax 调用失败

KnockoutJS - 可观察对象的可观察数组,包含来自 SQL 服务器的数据

KnockoutJS:模板未在可观察数组更改时更新(仅在添加时,在删除时有效)

Knockoutjs:如何使添加到列表中的输入值也可观察

KnockoutJS 3.X API 第八章 映射(mapping)插件