如何将父函数添加到 KnockOut 双嵌套映射?

How to add parent function to KnockOut double-nested mapping?

我正在使用来自 JS 的 KnockOut 映射从 JSON 对象创建我的视图模型,如下所示:

{
    "cats": [{
        "name": "fluffy",
        "color": "brown",
        "kittens": [{
            "name": "spot",
            "color": "brown"
        }, {
            "name": "rascal",
            "color": "grey"
        }, {
            "name": "trouble",
            "color": "white"
        }]
    }, {
        "name": "kitty",
        "color": "red",
        "kittens": [{
            "name": "lady",
            "color": "red"
        }, {
            "name": "skat",
            "color": "striped"
        }]
    }]
}

html:

<div data-bind="foreach:cats">
    <span data-bind="text:name"></span>
    <table>
        <tr data-bind="foreach: kittens">
             <td data-bind="text:name"></td>
             <td data-bind="text:color"></td>
             <td><a data-bind="click: $parent:showParentColor" href="#">Parent Color</a></td>
        </tr>
    </table>
</div>

Javascript:

var KittenModel = function (data) {
    ko.mapping.fromJS(data, {}, this);

    // ... various computed values added to this
}

var mapping = {
    'kittens': {
        create: function(options) {
            return new KittenModel(options.data);
        }
    },
    'otherItem': {
        create: function(options) {
             return ('otherStuff');
        }
     }
}

var data = { ... }; // the JSON above

var CatsViewModel = ko.mapping.fromJS(data, mapping);

问题:

在哪里以及如何放置 showParentColor() 函数,以便数据绑定在小猫中工作 table?例如:

function showParentColor(cat) {
    alert(cat.color);
}

谢谢!

您可以根据您的视图模型层次结构使用以下其中一项:

  • $root :这指向根 context.The 最顶层 parent 上下文中的主视图模型 object。
  • $parents 数组: 这是一个包含所有视图模型的数组。

    • $parents[0] : parent 视图模型上下文。(也与 $parent)

    • $parents[1]:第二个parent视图模型上下文。(grand parent)

    • $parents[2]:第三个parent查看模型上下文。 (great-grand parent)

    • 等等....


更新: 如果你想在 CatsViewModel 级别添加一个函数,你只需将你的函数添加到创建的模型中。

示例:https://jsfiddle.net/kyr6w2x3/87/

JS:

 CatsViewModel.showParentColor = function(item){
      console.log(item.name());
      console.log(item.color());
    }

查看:

<a data-bind="click: $parents[1].showParentColor">

以下是您的模型的层次结构

- CatsViewModel 
    - cats : observableArray
        - name : observable
        - color : observable
        - kittens : observableArray
               - name : observable
               - color : observable
    - showParentColor : function


备选方案: 您可以自己完成这项工作并创建您的 models.Would 更容易根据您的内容进行修改和维护 want.You 也可以添加 click 在你想要的任何模型中运行。

示例:http://jsfiddle.net/kyr6w2x3/91/

HTML :

<div data-bind="foreach:cats">
    <span data-bind="text:name"></span>
    <table>
    <tbody data-bind="foreach: kittens">
       <tr>
             <td data-bind="text:name"></td>
             <td data-bind="text:color"></td>
             <td><a data-bind="click: $parent.showParentColor" href="#">Parent Color</a></td>
        </tr>
    </tbody>

    </table>
</div>

JS:

 var data = {
    "cats": [{
        "name": "fluffy",
        "color": "brown",
        "kittens": [{
            "name": "spot",
            "color": "brown"
        }, {
            "name": "rascal",
            "color": "grey"
        }, {
            "name": "trouble",
            "color": "white"
        }]
    }, {
        "name": "kitty",
        "color": "red",
        "kittens": [{
            "name": "lady",
            "color": "red"
        }, {
            "name": "skat",
            "color": "striped"
        }]
    }]
}

var CatsViewModel = function (data){ 
  var self = this;
  self.cats = ko.observableArray($.map(data.cats, function (item) {
        return new CatItemViewModel(item);
    }));
}
var CatItemViewModel = function (data){
  var self = this;
  self.name = ko.observable(data.name);
  self.color = ko.observable(data.color);
  self.kittens = ko.observableArray($.map(data.kittens, function (item)     {
   var newData = Object.assign({}, item, { parent: self.name()});
        return new KittenModel(newData);
   }));
   self.showParentColor = function (item){
      console.log("Parent Name: " , self.name());
      console.log("Name: " , item.name());
      console.log("Color: " , item.color());
   }
}
var KittenModel = function (data) {
  var self = this;
  self.name = ko.observable(data.name);
  self.color = ko.observable(data.color);
  self.parent = ko.observable(data.parent);
}
var vm = new CatsViewModel(data);
ko.applyBindings(vm);

注意:我更喜欢其他答案的方法而不是这个方法,但是为了有多个选项,我确实想提出一个替代解决方案。

备选方案 1

您可以在 KittenModel 中创建一个 "static" 方法来记录它通过的任何猫的颜色:

var KittenModel = function (data) {
  ko.mapping.fromJS(data, {}, this);
};

KittenModel.logCatColor = function(cat) {
  console.log(cat.color);
};

现在,因为您的视图可以访问父子结构,所以您可以使用任何您想要的父级调用此方法:

<!-- to log the parent's color -->
<div data-bind="click: logCatColor.bind(null, $parent)"></div>

<!-- knockout automatically passes `$data` as a first argument,
     so you won't need to bind the method to log your own color -->
<div data-bind="click: logCatColor"></div>

一个例子:

ko.applyBindings({
  name: "parent",
  child: {
    name: "child",
    logName: function(entity) {
      console.log(entity.name);
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<h1 data-bind="text: name"></h1>
<div data-bind="with: child" style="border: 1px solid black">
  <h2 data-bind="text: name">
    </h2>
  <button data-bind="click: logName">
    log my name
  </button>
  <button data-bind="click: logName.bind(null, $parent)">
    log my parent's name
  </button>
</div>

备选方案 2

在作为选项传递的工厂函数中,dataparent 都可用:options.data 包含当前映射的项目。 options.parent 在这种情况下将引用父级 cat。我还没有测试过这个,但这可能有效:

'kittens': {
    create: function(options) {
        var dataWithParent = Object.assign({}, 
                               options.data, 
                               { parent: options.parent });

        return new KittenModel(dataWithParent);
    }
},