Reputation: 2497
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!
Upvotes: 2
Views: 1482
Reputation: 3634
You may use one of the following based on your view model hierarchy :
$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)
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
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);
Upvotes: 4
Reputation: 23372
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.
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>
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);
}
},
Upvotes: 1