敲除映射问题 Create/Update

Trouble with Knockout Mapping Create/Update

我正在尝试映射数据,以便仅在值实际更改时才重新呈现元素。

{
    Apps : [
        {
            "Categories" : [{
                    "Name" : "#Some,#More,#Tags,#For,#Measure"
                }
            ],
            "Concentrator" : "",
            "Health" : 1,
            "Id" : 2648,
            "Ip" : "1.1.1.1",
            "IsDisabled" : true,
            "IsObsolete" : false,
            "Name" : "",
            "Path" : "...",
            "SvcUrl" : "http://1.1.1.1",
            "TimeStamp" : "\/Date(1463015444163)\/",
            "Type" : "...",
            "Version" : "1.0.0.0"
        }
        ...
    ]
    ...
}

var ViewModel = function() {
    self.Apps = ko.observableArray([]);
}

var myModel = new ViewModel();

var map = {
    'Apps': {
        create: function (options) {
            return new AppModel(options.data);
        },

        key: function(data) { return ko.utils.unwrapObservable(data.Id); }
    }
}

var AppModel = function(data){
    data.Categories = data.Categories[0].Name.split(',');
    ko.mapping.fromJS(data, { }, this);
    return this;
}

function UpdateViewModel() {
    return api.getDashboard().done(function (data) {
        ko.mapping.fromJS(data, map, myModel);
    });
}

loopMe(UpdateViewModel, 5000);

function loopMe(func, time) {
    //Immediate run, once finished we set a timeout and run loopMe again
    func().always(function () {
        setTimeout(function () { loopMe(func, time); }, time);
    });
}

<script type="tmpl" id="App-template">
    <div>
        <!-- ko foreach: Categories -->
        <span class="btn btn-default btn-xs" data-bind="text:$data"></span>
        <!-- /ko -->
    </div>
</script>

在 UpdateViewModel 的第一个 运行 中,我将按预期看到 5 个跨度。在第二次调用时,接收到相同的数据,它被更新为一个显示 [Object object] 的跨度,这是因为它仍然认为 Categories 是一个对象数组而不是一个字符串数组。

如果我在我的地图中将 'create' 更改为 'update',一切似乎都已修复,但是无论数据是否更改,似乎每次都会重新渲染跨度。​​

任何人都可以帮我指明我需要去的方向,这样我就可以

  1. 将类别数组从对象调整为字符串
  2. 只有 re-render/render changed/new 项

这里是 Fiddle 显示行为

问题在于这些行:

var AppModel = function(data){
    data.Categories = data.Categories[0].Name.split(','); // <-- mainly this one
    ko.mapping.fromJS(data, { }, this);
    return this;
}

有两个问题:

  1. 你改变了 data 对象,它(至少在我们的 repro 中)改变了 data 引用的原始对象。所以第一次传入一个 fakeData 对象时,那个对象就地改变了,并且永远是 "fixed".

  2. 你在AppModelconstructor函数中改变它,它只被第一次调用。根据您的 key 函数,第二次构造函数应该 而不是 被调用,而是 ko-mapping 应该保留原始对象并在适当的位置进行变异。但它会使用 "wrongly" 格式 data.Categories 属性.

在我看来,正确的修复是在您的数据层中,我们已在重现中对其进行了模拟,因此我的回答向您展示如何操作毫无意义。

另一种更 hacky 的方法是在映射中使用 update 方法,如下所示:

update: function(options) {
  if (!!options.data.Categories[0].Name) {
    options.data.Categories = options.data.Categories[0].Name.split(',');
  }
  return options.data;
},

当它遇到一个 "unmodified" 数据对象时,它会做同样的改变。请参阅 this jsfiddle 以了解实际的解决方案。