How to add parent function to KnockOut double-nest

2019-07-20 01:25发布

I'm using KnockOut mapping fromJS to create my View Model from a JSON object like the following:

{
    "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);

Question:

Where and how do I put the showParentColor() function so that the the data-bind works in the kitten table? For example:

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

Thanks!

2条回答
ら.Afraid
2楼-- · 2019-07-20 01:42

Note: I like the other answer's approach better than this one, but did want to present an alternative solution for the sake of having multiple options.

Alternative 1

You could create a "static" method in your KittenModel that logs any cat's color that it's passed:

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

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

Now, because your view has access to the parent-child structure, you can call this method with any parent you want:

<!-- 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>

An example:

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>

Alternative 2

In the factory function you're passing as an options, you have both data and parent available: options.data holds the item being currently mapped. options.parent in this case will refer to the parent cat. I haven't tested this, but this might work:

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

        return new KittenModel(dataWithParent);
    }
},
查看更多
甜甜的少女心
3楼-- · 2019-07-20 01:49

You may use one of the following based on your view model hierarchy :

  • $root : This points to the main view model object in the root context.The top most parent context.
  • $parents array : This is an array which contains all your view models.

    • $parents[0] : The parent view model context.(also it’s the same as $parent)

    • $parents[1]: The second parent view model context.(grand parent)

    • $parents[2]: The third parent view model context . (great-grand parent)

    • And so on....


Update : If you want to add a function in CatsViewModel level, you simply add your function to created model.

Example :https://jsfiddle.net/kyr6w2x3/87/

JS:

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

View:

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

Below is the hierarchy of your model

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


Alternative : You can do the job yourself and create your models.Would be easier for you to modify and maintain based on what you want.You can also add click function inside any models you want.

Example :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);
查看更多
登录 后发表回答