Knockout has been growingly popular for being simple in implementation and easy maintainability. There are several features good enough to attract far more than other counterparts available currently in the market. It lets developers leverage the power of MVVM pattern to increase modularity with clean separation of concerns. As highlighted by the official Knockout website, the 4 key concepts of Knockout are,
For those who haven’t tried Knockout yet, please visit official documentation at knockoutjs.com.
In this particular article, we’ll concentrate only on the last concept mentioned above and build a simple demo to see how template binding can be helpful in our real-life projects.
Some assumptions
Demo
For the purpose of demo, I have created a very simple sample app in which a school named Bhubaneswar Public School needs their annual sports results to be saved for future reference. For now, I haven’t used any database to store the data captured, just to make things simple and concentrate on the concept Template Binding.
Note: You may notice that the article is longer than what is needed for explaining just the template binding. This is because, I have tried to present a walkthrough how the final demo is built and tried to explain all the important things which we came across while creating it. The intention is to make it easier to understand for those who have just started learning Knockout and for those who already know some basics, it may serve as recap.
Let’s start the designing work now. That’s just very simple HTML tags and some basic CSS styling, that’s all.
HTML
Before this, you should have a basic HTML template/structure ready with you.
Your next task is to include the knockout library in your project or point it to a CDN. For the source code attached, I have taken the local copy approach so that the downloaded source code should work when the machine is offline too.
For local copy reference,
- <script src="knockout-3.4.2.js"></script>
or point to the CDN link,
- <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
You can place this piece of link to external script anywhere in the page, <head> tag being the most common place people use to place link to external scripts. In this example, I have added it to the bottom of the <body> tag to make sure that it doesn’t block the HTML being parsed while it’s getting downloaded.
Then let’s build a simple table with required table headers.
- <table>
- <thead>
- <tr>
- <th>Event</th>
- <th>Position</th>
- <th>Name</th>
- <th>Class</th>
- <th>Roll No.</th>
- <th></th>
- </tr>
- </thead>
- </table>
We have grouped all the header cells (<th>) under <thead> tag here.
Then let’s create the row which actually will contain the data or will accept user inputs against them.
- <tr>
- <td>
- <select name="events" ></select>
- </td>
- <td>
- <select name="positions" ></select>
- </td>
- <td>
- <input name="studentName" />
- </td>
- <td>
- <select name="classes"></select>
- </td>
- <td>
- <input name="rollno" />
- </td>
- <td>
- <a href="#" name="deleteStudent">Delete</a>
- </td>
- </tr>
Here things to notice are, we haven’t added any options to the dropdowns as we want them to be bind dynamically from database (JSON data for now) and added an anchor tag which will serve the delete functionality.
After this, add another row to place a add button.
- <tr>
- <td>
- <a href="#" name="addStudent">Add Student</a>
- </td>
- </tr>
We’ll have a very simple layout here so, very few CSS things to follow.
- html {
- display: table;
- margin: auto;
- text-align: center;
- }
Next, we need to repeat the rows with the data already available in the database (for now, just the JSON array). But before doing that let’s start building our ViewModel. To do that, start a new <script> tag at the end of the <body> tag.
- <script>
- var viewModel = {
- events: ["High-Jump", "Long-Jump", "Javelin-Throw"],
- positions: ["1st", "2nd", "3rd"],
- classes: ["Class - IX", "Class – X”]
- };
- </script>
Here we have defined the arrays for binding dropdowns events, positions and classes. In real word work, these values can be obtained via Ajax call from a database using any server side languages. Now let’s bind the dropdowns with these values using a databound attribute known as Options Binding as follows-
- <select name="events" data-bind="options: events ">
If you try running the page in the browser, the events dropdown should now show the options from the events array as defined in the ViewModel. Do the same for other dropdowns.
- <select name="positions" data-bind="options: positions "></select>
- <select name="classes" data-bind="options: classes "></select>
We are now ready with the template. We need the second row; i.e., the row with dropdown and textboxes to be repeated with the already available data. To do that, we need to create a Student class (or, constructor function) with the required properties as follows (make sure to place this just above our ViewModel).
- function Student(event, position, name, classname, roll) {
- this.event = event;
- this.position = position;
- this.name = name;
- this.classname = classname;
- this.roll = roll;
- }
Let’s build the JSON data so that we can demonstrate how to bind /generate rows for the already available data (place this after the ViewModel).
- var DataFromDB = '[{"event":"High-Jump", "position":"1st", "name":"Suvendu","classname":"Class - IX","roll":9},{"event":"High-Jump", "position":"2nd", "name":"Shekhar","classname":"Class - IX","roll":12}]';
Let’s parse the JSON string to make it JavaScript object using an utility function i.e. ko.utils.parseJson which does a JSON.Parse.
- var dataFromDB = ko.utils.parseJson(DataFromDB);
We need to do a kind of mapping of our JSON data with the Student properties.
- var mappedStudentData = ko.utils.arrayMap(dataFromDB, function (student) {
- return new Student(student.event, student.position, student.name, student.classname, student.roll);
- });
With this we generated mapped arrays of objects with the help of ko.utils.arrayMap. It executes a function for each item in an array and pushes the result of the function to a new array that is returned. Now go back to the ViewModel and add another observable array to which we can assign our mapped student data.
- students: ko.observableArray([])
Then assign the mapped data to the student property and apply bindings at the end of our script section.
- viewModel.students(mappedStudentData);
- ko.applyBindings(viewModel);
You’re probably wondering by now about the template binding which the title of the article states but you haven’t experienced any such thing yet. Well, the stage is all set to introduce it.
“The template
binding populates the associated DOM element with the results of rendering a template.”
There are 2 ways to use template binding, Native Templating using foreach, if, with etc. and String-base Templating using third party template engine. In this particular article we are focused only to show the Native Templating using foreach. So, to go further with our demo, let’s wrap our data row within pair of <script> tags and give it a name.
- <script id="template_students" type="text/html">
- <tr>...
- </tr>
- </script>
Now, just above this, put a pair of <tobody> tag. We will be injecting our data rows in to this as it will now act as a container for our data rows.
- <tbody data-bind="template: { name: 'template_students', foreach: students }"></tbody>
I believe not much explanation will be required to understand the above piece of code. It’s just the syntax to bind data against a template. It says generate template with data for each student in students using the template template_students. The first parameter “name” requires the ID of an element that contains the template to be rendered. Second parameter serves as data required for binding the template. We need to modify the template to set values from the students data with property as defined in our Student class and is as follows-
- <tr>
- <td>
- <select name="events" data-bind="options: events, value: event"></select>
- </td>
- <td>
- <select name="positions" data-bind="options: positions, value: position"></select>
- </td>
- <td>
- <input name="studentName" data-bind="value: name" />
- </td>
- <td>
- <select name="classes" data-bind="options: classes, value: classname"></select>
- </td>
- <td>
- <input name="rollno" data-bind="value: roll" />
- </td>
- <td>
- <a href="#" name=”deleteStudent” >Delete</a>
- </td>
- </tr>
Now, if you view this page in browser, you will see something like following-
Note that text fields are correctly filled with the data that we stored in the JSON array but not the dropdowns. That’s because we are in the foreach context and we are trying to access ViewModel top level data but it actually refers to the current item in the foreach loop. $root property came into rescue here. The $root context always refers to the top-level ViewModel, regardless of loops or other changes in scope. So, let’s add $root to the binding source of dropdowns we have like-
- <select name="events" data-bind="options: $root.events, value: event"></select>
If we run this page in browser, we should see something like following which is very much same as what we wanted as our initial output.
Now, let’s add a function in our ViewModel to add and delete a student data via Add Student and Delete buttons respectively. Place following functions just below the observable students array we have defined earlier.
- addStudent: function () {
- this.students.push(new Student("", "", "","",""));
- },
- deleteStudent: function (student) {
- this.students.remove(student);
- }
Using click binding change the markup as follows-
- <a href="#" name="deleteStudent" data-bind="click: $root.deleteStudent.bind($root)" >Delete</a>
and
- <a href="#" name="addStudent" data-bind="click: addStudent" >Add Student</a>
Thing to notice, we don’t need $root for calling addStudent function as it is outside the foreach loop.
We’ll also do a bit of changes in our Student class to make sure our changes are done with the view and UI also gets updated in our model and vice-versa. For that we need to make all our properties observable.
- function Student(event, position, name, classname, roll) {
- this.event = ko.observable(event);
- this.position = ko.observable(position);
- this.name = ko.observable(name);
- this.classname = ko.observable(classname);
- this.roll = ko.observable(roll);
- }
Our demo is ready to be rendered in to the bowser!
Conclusion
This is just a simple demo which briefly describes how we can use template binding with foreach. We can then build our data from the model for sending to the server and to be saved to the database. There are also several ways/types of templating which I didn’t mention here. I will try to write another article with probably a more complete demo consisting of all other templating types and building data required to send to the server and all.
Hopefully, you loved reading this. Please share your valuable feedback.
Thanks!