Introduction
In today's article you will learn about Unobtrusive Event Handlers in Knokcoutjs.
You have now seen that Data Bind provides a clean way to bind to a View model. But if our application is using event handling then it will result in too much data-binding attributes. You might use functions to solve this problem but Knockout provides two alternative helper functions that will allow you to identify the data associated with a DOM element. These two helper functions are as follows:
- ko.dataFor(element): It returns the data that was available for binding against the element.
- ko.contextFor(element): it returns the entire binding context that was available to the DOM element.
These two functions can be used in event handlers that are attached unobtrusively using jQuery click or bind and so on.
Now I will explain these thing with an example. I'll create an application where some parents will be containing some childs and when the user clicks on "Add child" then the child will be shown with the parent name. If children are added again and again then they will show whether they are grandchildren or great-grandchildren.
Step 1
First of all, you need to add an external Knockoutjs file and jQuery-1.7.2.min.js into your application, you can either download these from here "Knockoutjs" or can download my application that is available at the start of this article in Zip Format and then can use the file attached with this Zip file.
After downloading the file you need to call it in the head section of your application.
<head runat="server">
<title></title>
<script src="knockout-2.3.0.js"></script>
<script src="jquery-1.7.2.min.js"></script>
</head>
Step 2
Now we can work on our application. First we will work on the ViewModel. For that you need to add a Script tag under the Body Section and then add this code:
<script>
var People = function (name, children) {
this.name = ko.observable(name);
this.children = ko.observableArray(children || []);
};
var person = function () {
this.people = ko.observableArray([
new People("Moh"),
new People("Anu", [
new People("Ram"),
new People("Kam")
])
]);
this.addNew = function (name, peopleArray) {
peopleArray.push(new People(name));
};
};
ko.applyBindings(new person());
$("#people").delegate(".remove", "click", function () {
var context = ko.contextFor(this),
peopleArray = context.$parent.people || context.$parent.children;
peopleArray.remove(context.$data);
return false;
});
$("#people").delegate(".add", "click", function () {
var context = ko.contextFor(this),
childName = context.$data.name() + " child",
peopleArray = context.$data.people || context.$data.children;
context.$root.addNew(childName, peopleArray);
return false;
});
</script>
Here first I created a function named People, in this function two Observables are declared and among these, one is an observable Array. These are named as name and children.
Then one more function is declared named person, in this function some values are provided in the Observable Array.
Then one more function is created named addNew, this function contains a push function of Knockout that is used to add new entries to the Array.
Then event handlers are attached using jQuery. First remove is created on click of button. Here you can see that I had used ko.contextFor(this), since I already explained that contextFor that will return the entire binding context or you can say that it will work on the complete function. Here remove(context.$data) is used that will remove the data from the array.
Then another event handler is used to add the click of button the event. Here also contextFor is used so it will also work on the entire function.
Our work on the View Model is now completed so we can proceed to the View part or Design part of our application.
Step 3
<ul id="people" data-bind='template: { name: "peopleTmpl", foreach: people }'>
</ul>
<script id="peopleTmpl" type="text/html">
<li>
<a class="remove" href="#"> remove </a>
<span data-bind='text: name'></span>
<a class="add" href="#"> add child </a>
<ul data-bind='template: { name: "peopleTmpl", foreach: children }'></ul>
</li>
</script>
Here an Unordered List is created that is bound to the people Array.
Then two links are used, one of them will delete the data and the other one will add a new entry in the Array.
The complete code of this application is as follows:
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
<script src="knockout-2.3.0.js"></script>
<script src="jquery-1.7.2.min.js"></script>
</head>
<body>
<form id="form1" runat="server">
<ul id="people" data-bind='template: { name: "peopleTmpl", foreach: people }'>
</ul>
<script id="peopleTmpl" type="text/html">
<li>
<a class="remove" href="#"> remove </a>
<span data-bind='text: name'></span>
<a class="add" href="#"> add child </a>
<ul data-bind='template: { name: "peopleTmpl", foreach: children }'></ul>
</li>
</script>
<script>
var People = function (name, children) {
this.name = ko.observable(name);
this.children = ko.observableArray(children || []);
};
var person = function () {
this.people = ko.observableArray([
new People("Moh"),
new People("Anu", [
new People("Ram"),
new People("Kam")
])
]);
this.addNew = function (name, peopleArray) {
peopleArray.push(new People(name));
};
};
ko.applyBindings(new person());
$("#people").delegate(".remove", "click", function () {
var context = ko.contextFor(this),
peopleArray = context.$parent.people || context.$parent.children;
peopleArray.remove(context.$data);
return false;
});
$("#people").delegate(".add", "click", function () {
var context = ko.contextFor(this),
childName = context.$data.name() + " child",
peopleArray = context.$data.people || context.$data.children;
context.$root.addNew(childName, peopleArray);
return false;
});
</script>
</form>
</body>
</html>
Output
You can run now your application, on debugging the application you will get output like this:
Now I am clicking two times on "Add Child", this will add two children under "Moh".
Now I am adding one child under the "Ram" and one more child under this new child, they both will show their rank in this parent/child tree.