Wednesday, March 1, 2017

KnockoutJS - Templating

Template is a set of DOM elements which can be used repetitively. Templating makes it easy to build complex applications due to its property of minimizing duplication of DOM elements.
There are 2 ways of creating templates:

  • Native templating- This method supports control flow bindings like foreach, with and if. These bindings capture HTML markup existing in the element and use it as template for random items. No external library is required for this templating.
  • String-based templating- KO connects to third party engine to pass ViewModel values into it and injects resulting markup into document. E.g. JQuery.tmpl and Underscore Engine.

Syntax

template: <parameter-value>

<script type="text/html" id="template-name">
...
...   // DOM elemets to be processed
...
</script>
Note that type is provided as text/html in script block to notify KO that, it is not an executable block and is just template block which needs to be rendered.

Parameters

Combination of following properties can be sent as parameter-value to template.
  • name - This represents name of template.
  • nodes - This represents array of DOM nodes to be used as template. This parameter is ignored if name parameter is passed.
  • data - This is nothing but data to be shown via template.
  • if - Template will be served if the given condition results in true or true like value.
  • foreach - To serve template in foreach format.
  • as - This is just to create an alias in foreach element.
  • afterAdd, afterRender, beforeRemove - These are all to represent callable functions to be executed depending on operation performed.

Observations

Rendering a named template

Templates are defined implicitly by HTML markup inside DOM when used with control flow bindings. But if you want to, you can factor out templates into a separate element and then reference them by name.

Example

<!DOCTYPE html>
<head>
   <title>KnockoutJS Templating - Named Template</title>
   <script src="https://ajax.aspnetcdn.com/ajax/knockout/knockout-3.3.0.js" type="text/javascript"></script>
</head>
<body>

   <h2>Friends List</h2>
   Here are the Friends from your contact page:
   <div data-bind="template: { name: 'friend-template', data: friend1 }"></div>
   <div data-bind="template: { name: 'friend-template', data: friend2 }"></div>

   <script type="text/html" id="friend-template">
       <h3 data-bind="text: name"></h3>
       <p>Contact Number: <span data-bind="text: contactNumber"></span></p>
       <p>Email-id: <span data-bind="text: email"></span></p>
   </script>

   <script type="text/javascript">
        function MyViewModel() {
            this.friend1= { name: 'Smith', contactNumber: 4556750345, email: 'smith123@gmail.com' };
            this.friend2 = { name: 'Jack', contactNumber: 6789358001, email: 'jack123@yahoo.com' };
        }

   var vm = new MyViewModel();
   ko.applyBindings(vm);
</script>
</body>
</html>

Output

Let's carry out the following steps to see how the above code works:
  • Save the above code in template-named.htm file.
  • Open this HTML file in a browser.
  • Here friend-template is used 2 times.

Using "foreach" in template

Below is an example of using foreach parameter along with template name.

Example

<!DOCTYPE html>
<head>
   <title>KnockoutJS Templating - foreach used with Template</title>
   <script src="https://ajax.aspnetcdn.com/ajax/knockout/knockout-3.3.0.js" type="text/javascript"></script>
</head>
<body>

   <h2>Friends List</h2>
   Here are the Friends from your contact page:
   <div data-bind="template: { name: 'friend-template', foreach: friends }"></div>

   <script type="text/html" id="friend-template">
       <h3 data-bind="text: name"></h3>
       <p>Contact Number: <span data-bind="text: contactNumber"></span></p>
       <p>Email-id: <span data-bind="text: email"></span></p>
   </script>

   <script type="text/javascript">
        function MyViewModel() {
            this.friends = [
               {name: 'Smith', contactNumber: 4556750345, email: 'smith123@gmail.com' },
               { name: 'Jack', contactNumber: 6789358001, email: 'jack123@yahoo.com' },
               { name: 'Lisa', contactNumber: 4567893131, email: 'lisa343@yahoo.com' }
            ]
        }

   var vm = new MyViewModel();
   ko.applyBindings(vm);
</script>
</body>
</html>

Output

Let's carry out the following steps to see how the above code works:
  • Save the above code in template-foreach.htm file.
  • Open this HTML file in a browser.
  • Here foreach control is used in template binding.

Creating alias using as keyword for foreach items

Below is how an alias can be created for a foreach item:
<div data-bind="template: { name: 'friend-template', foreach: friends,  as: 'frnz' }"></div>
It becomes easy to refer to parent objects from inside of foreach loops by creating alias. This feature is useful when the code is complex and nested at multiple levels.

Example

<!DOCTYPE html>
<head>
   <title>KnockoutJS Templating - using alias in Template</title>
   <script src="https://ajax.aspnetcdn.com/ajax/knockout/knockout-3.3.0.js" type="text/javascript"></script>
</head>
<body>

   <h2>Friends List</h2>
   Here are the Friends from your contact page:
   <ul data-bind="template: { name: 'friend-template', foreach: friends, as: 'frnz' }"></ul>

   <script type="text/html" id="friend-template">
      <li>
         <h3 data-bind="text: name"></h3>
         <span>Contact Numbers</span>
         <ul data-bind="template: { name : 'contacts-template', foreach:contactNumber, as: 'cont'} "></ul>
         <p>Email-id: <span data-bind="text: email"></span></p>
      </li>
   </script>

   <script type="text/html" id="contacts-template">
      <li>
         <p><span data-bind="text: cont"></span></p>
      </li>
   </script>

   <script type="text/javascript">
        function MyViewModel() {
            this.friends = ko.observableArray( [
               {name: 'Smith', contactNumber: [ 4556750345, 4356787934 ] , email: 'smith123@gmail.com' },
               { name: 'Jack', contactNumber: [ 6789358001, 3456895445 ], email: 'jack123@yahoo.com' },
               { name: 'Lisa', contactNumber: [ 4567893131, 9876456783, 1349873445 ],  email: 'lisa343@yahoo.com' }
            ]);
        }

   var vm = new MyViewModel();
   ko.applyBindings(vm);
</script>
</body>
</html>

Output

Let's carry out the following steps to see how the above code works:
  • Save the above code in template-as-alias.htm file.
  • Open this HTML file in a browser.
  • Alias is used instead of full name of arrays.

Using afterAdd, beforeRemove and afterRender

There are situations wherein extra custom logic needs to be run on DOM elements created by template. In that case following callbacks can be used. Consider that you are using foreach element then
  • afterAdd - This function is invoked when a new item is added to array mentioned in foreach.
  • beforeRemove - This function is invoked just before removing item from array mentioned in foreach.
  • afterRender - Function mentioned here is invoked every time foreach is rendered and new entries are added to array.

Example

 <!DOCTYPE html>
 <head>
    <title>KnockoutJS Templating - Use of afterRender Template</title>
    <script src="https://ajax.aspnetcdn.com/ajax/knockout/knockout-3.3.0.js" type="text/javascript"></script>
    <script src="https://code.jquery.com/jquery-2.1.3.min.js" type="text/javascript"></script>
 </head>
 <body>

    <h2>Friends List</h2>
    Here are the Friends from your contact page:
    <div data-bind="template: { name: 'friend-template', foreach: friends , afterRender: afterProcess}"></div>

    <script type="text/html" id="friend-template">
        <h3 data-bind="text: name"></h3>
        <p>Contact Number: <span data-bind="text: contactNumber"></span></p>
        <p>Email-id: <span data-bind="text: email"></span></p>
        <button data-bind="click: $root.removeContact">remove </button>
    </script>

    <script type="text/javascript">
         function MyViewModel() {
             self= this;
             this.friends = ko.observableArray([
                {name: 'Smith', contactNumber: 4556750345, email: 'smith123@gmail.com' },
                { name: 'Jack', contactNumber: 6789358001, email: 'jack123@yahoo.com' },
             ])

             this.afterProcess = function(elements, data){
                $(elements).css({color: 'magenta' });
             }

             self.removeContact = function(){
                self.friends.remove(this);
             }
         }

    var vm = new MyViewModel();
    ko.applyBindings(vm);
 </script>
 </body>
 </html>

Output

Let's carry out the following steps to see how the above code works:
  • Save the above code in template-afterrender.htm file.
  • Open this HTML file in a browser.
  • Here afterProcess function is executed every time foreach is rendered.

Choosing template dynamically

If there are multiple templates available then one can be chosen dynamically by making name as observable parameter. So template value will be re-evaluated as the name parameter changes and in turn data will be re-rendered.

Example

<!DOCTYPE html>
<head>
   <title>KnockoutJS Templating - Dynamic Template</title>
   <script src="https://ajax.aspnetcdn.com/ajax/knockout/knockout-3.3.0.js" type="text/javascript"></script>

</head>
<body>

   <h2>Friends List</h2>
   Here are the Friends from your contact page:
   <div data-bind="template: { name: whichTemplate, foreach: friends }"></div>

   <script type="text/html" id="only-phon">
       <h3 data-bind="text: name"></h3>
       <p>Contact Number: <span data-bind="text: contactNumber"></span></p>
   </script>

   <script type="text/html" id="only-email">
       <h3 data-bind="text: name"></h3>
       <p>Email-id: <span data-bind="text: email"></span></p>
   </script>


   <script type="text/javascript">
        function MyViewModel() {

            this.friends = ko.observableArray([
               {name: 'Smith', contactNumber: 4556750345, email: 'smith123@gmail.com' ,active: ko.observable(true) },
               {name: 'Jack', contactNumber: 6789358001, email: 'jack123@yahoo.com', active: ko.observable(false)  },
            ]);

            this.whichTemplate = function(friends){
               return friends.active() ? "only-phon" : "only-email";
            }
        }

   var vm = new MyViewModel();
   ko.applyBindings(vm);
</script>
</body>
</html>

Output

Let's carry out the following steps to see how the above code works:
  • Save the above code in template-dynamic.htm file.
  • Open this HTML file in a browser.
  • Template to be used is decided depending on active flag value.

Using external string based engines

Native templating works perfectly with various control flow elements even with nested code blocks. KO also offers a way to integrate with external templating library like Underscore templating Engine or JQuery.tmpl.
As mentioned on official site JQuery.tmpl is no longer under active development since December 2011. Hence KO's native templating is only recommended instead of JQuery.tmpl or any other string based template engine.
Please refer to official site for more details on this.

No comments:

Post a Comment