I have this code:
<div id="menu">
<ul data-bind="foreach: elements">
<li>
<span data-bind="text:domObj().tagName +' [' + classOriginal() + ']', click: $root.elementClick"></span>
<ul class="menuclassitem" style="display:none" data-bind="foreach: classFixes">
<li data-bind="text:$data, click:$root.classClick.bind($parent)"></li>
</ul>
</li>
</ul>
</div>
As you can see, I am trying to pass the $parent object (the current item in the "elements" array) to the $root.classClick function but the value that is actually passed is the $data of the current inner loop (the $data of the current "classFixes" array)
Does anyone have an idea how to use the $parent inside the classClick function?
Quite a fossil question but it still may help someone.
This question is more related to the way how .bind()
than to KnockoutJS. So first we need to understand how bind() works:
The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called. more...
Now let get back to the question. By default KnockoutJS passes current $data
as a as first argument for function received by click
-binding. Same $data
is used as default context. After @DorR wrote click:$root.classClick.bind($parent)
click binding received a function with redefined context (to $parent) and predefined args, which allowed KO to pass $data as a first arg. $parent is now accessible in classClick
as this
.
To illustrate how it works I made this small snippet below. Try clicking on buttons from different columns and check debug log below to see how arguments passed by KO are being shifted to the right by arguments predefined by .bind()
function VM() {
const self = this;
self.debug = ko.observable('');
self.catalog = [
{
name:'Section1',
items:[
{id:1,name:'Item1'},
{id:2,name:'Item2'},
]
},
{
name:'Section2',
items:[
{id:4,name:'Item4'},
]
}
];
self.orderClick = function(arg1,arg2,arg3,arg4) {
let dbgText = "this:<br> ";
dbgText += JSON.stringify(this);
dbgText += "<br>1st arg:<br> ";
dbgText += JSON.stringify(arg1);
dbgText += "<br>2nd arg:<br> ";
dbgText += JSON.stringify(arg2);
dbgText += "<br>3nd arg:<br> ";
dbgText += JSON.stringify(arg3);
dbgText += "<br>4th arg:<br> ";
dbgText += JSON.stringify(arg4);
self.debug(dbgText);
}
}
ko.applyBindings(new VM())
table {
border-collapse: collapse;width:100%;
}
td {
border:1px solid #CCC;
text-align:center;
font-family:sans-serif;
font-size:11px;
white-space:nowrap;
}
button {
display:block; margin:5px auto;
}
.dbglog {
padding:5px 10px;
border:1px solid #AAA;
background-color:#EEE;
font-family:"Lucida Console", Monaco, monospace;
font-size:12px;min-height:50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<table data-bind="foreach: catalog">
<tr>
<td colspan=10><b data-bind="text:$data.name"></b></td>
<td>
<table data-bind="foreach:$data.items" class=nested>
<tr>
<td width=10%>
<i data-bind="text:$data.name"></i>
</td>
<td width=30%>
no .bind()
<button data-bind="click:$root.orderClick">click</button>
</td>
<td width=30%>
.bind($parent)
<button data-bind="click:$root.orderClick.bind($parent)">click</button>
</td>
<td width=30%>
.bind($parent,$data,'smthelse')
<button data-bind="click:$root.orderClick.bind($parent,$data,'text')">click</button>
</td>
</tr>
</table>
</td>
</tr>
</table>
<div class=dbglog data-bind="html:$root.debug"></div>
Hope this answer will help someone. =)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With