3/22/13

Dynamic tabs with knockoutjs - part 3

Gettig all the widgets to work together
Now lets make some slight modifications to tabsViewModel so it will be able to host
the widgets from previous post:

var tabViewModel=function(title, active, model, templateName){ 
  this.title=ko.observable(title);
  this.active=ko.observable(active);
  this.model=ko.observable(model);
  this.templName=templateName;
};
var tabsViewModel=function(){  
  this.tabsArr=ko.observableArray([]); 

 
  this.makeTabActive=function(data){

    
      $.each(this.tabsArr(),function(ind,item){
       item.active('');
   });


   
     data.active('active');
  
  }.bind(this);
  

  this.addTab=function(title, active, model, template){
    var nt=new tabViewModel(title, active, model, template);
 this.tabsArr.push(nt);
    this.makeTabActive(nt);
  }.bind(this);


Only difference is that now there is new parameter inside tab model constructor -the model.
Here we sending the widget that will reside in the tab .

Changing templates
Also templates for tabs must be changed for be able to support model parameter:






Adding functions to each of the widgets:
Each widget must be able now to connect with tabs widget and with other 'brother' widgets inside tabs:

//for add the search tab
homeViewModel.prototype.openSearchTab=function(){
   tabs.tabsArr.push(new tabViewModel('search','' ,new searchViewModel(),'searchTmpl'))
};
//for add the person tab
searchViewModel.prototype.openPersonTab=function(data){

   var f=data.firstname;
   var l=data.lastname;
   var newPerson=new personViewModel(f,l);
   tabs.addTab(f+' '+l,'' ,newPerson,'personTmpl');
};
//for make new friend to appear the home tab
personViewModel.prototype.addFriend=function(){
   var homeVM=tabs.tabsArr()[0].model();
   homeVM.friends.push({name:this.firstname()})
}


Thats it, now our tabbed social network application is ready and functioning.
Hope you enjoy to read this post...
download source
demo

Dynamic tabs with knockoutjs - part 2

Creating 3 simple different widgets
In this post we will create a 3 simple widgets which will be hosted inside the tabs.

Home widget
This widget shows the friends collection, and able to open search page.
Model:

var homeViewModel=function(){  
  this.typename='home';    
  this.templName='homeTmpl';
  this.friends=ko.observableArray([{name:'Shlomo'}]) 
};
View:

<script id="homeTmpl" type="text/html">
 <h3>
  wellcome to Social Network!</h3>
 <b>friends: </b> <hr/>
   <ul data-bind="foreach:friends">
   <li data-bind="text:name"></li> </ul> <div> Wanna find more friends? <button data-bind="click:openSearchTab"
>click here</button><
 /div> </script> <!--ko template: { name: 'homeTmpl' }--><!--/ko-->
demo
Search widget
This widet must show the search engine:
To simplify the functionality, our search may find only to persons:
Vasia and Kolia

Model:

var searchViewModel=function(){
  
  this.typename='search';  
  var that=this;
  this.results=ko.observableArray([]);
  this.searchterm=ko.observable();

  this.clickSearch=function(){
  
    this.results([]);//empty the results array 
    switch(that.searchterm()){   
      case 'k':
        this.results.push({firstname:'Kostia',lastname:'Baranov'});
        break;
      case 'v':    
        this.results.push({firstname:'Vasia',lastname:'Kozlov'});
        break;
   default :
     this.results.push({firstname:'Vasia',lastname:'Kozlov'});
  this.results.push({firstname:'Kostia',lastname:'Baranov'});
    }
  };
  
  
  this.templName='searchTmpl';
};

View:

  <script id="searchTmpl" type="text/html">
    <div data-bind="text:typename">
    </div>
    <input data-bind="value:searchterm" />
    <button class="o" data-bind="click:clickSearch">press</button>

    <table data-bind="foreach:results" class="results">
      <tr>
        <td data-bind="text:firstname,click:$parent.openPersonTab"></td>
      </tr>
    </table>
    </div>
  </script>

demo
Person widget
This widget must show the person details and be able to add this person to friends
array:

Model:


var personViewModel=function(firstname,lastname){
  var that=this;
  this.firstname=ko.observable(firstname);
  this.lastname=ko.observable(lastname);


  this.templName="personTmpl";  
}
View:


  <script id="personTmpl" type="text/html">
    <section class="person"> <img src="img/placeholder.png" alt="pic" />
      <div> <b>first name:</b>
        <label data-bind="text:firstname"></label>
      </div>
      <div> <b>last name:</b>
        <label data-bind="text:lastname"></label>
      </div>
      <button data-bind="click:addFriend">add as friend</button>
    </section>
  </script>
  <!--ko template: { name: 'personTmpl', data: person }-->
  <!--/ko-->
demo
you can download source from github
Now that all 'insider' widgets are ready - we can proceed to the next step

Dynamic tabs with knockoutjs - part 1

Create a simple tabs widget with knockoutjs
Since we dealing with OOP javascript application we gonna use the knockoutjs library which is a very good framework for representing business objects in HTML.

First lets take care of html (view):

<script id="tabLinkTmpl" type="text/html">
<li data-bind="attr:{class:active},text:title,click:$parent.makeTabActive"></li>
</script>
<script id="tabPanelTmpl" type="text/html">
    <div class="content" tab="tab1" data-bind="visible: active()!='',html:content">
     
    </div>
</script>


  <ul class="tabs">
        <!--ko template: { name: 'tabLinkTmpl', foreach: tabsArr }--><!--/ko-->
  </ul>
<div class="contentWrap">
<!--ko template: { name: 'tabPanelTmpl', foreach: tabsArr }--><!--/ko-->
  </div>
<button data-bind="click:function(){ addTab('tab'+tabsArr().length,'','tabs count:'+tabsArr().length) }">Add New Tab</button>
   

Explanation:

For loading tab links and panels i'm using here templates. Templates are HTML code which is rendered by javascript and populated with data from business objects- View Models.
Speaking about View Models - here is the code of tabsViewModel:


var tabViewModel=function(title, active, content){ 
 this.title=ko.observable(title);
 this.active=ko.observable(active);
 this.content=ko.observable(content);
};
var tabsViewModel=function(){  
  this.tabsArr=ko.observableArray([]); 
  this.makeTabActive=function(data){
     //make all the tabs unactive
     $.each(this.tabsArr(),function(ind,item){
       item.active('');
  });
     //activate current tab  
     data.active('active');  
  }.bind(this);
  
  
  this.addTab=function(title, active, content){
    var nt=new tabViewModel(title, active, content);
 this.tabsArr.push(nt);
    this.makeTabActive(nt);
  }.bind(this);
};
var tabs=new tabsViewModel();
tabs.addTab('home','active', 'This is a home tab')
ko.applyBindings(tabs);


Explanation:
The tabs functionality includes two View Models: The tabViewModel - which represents the single tab entity, and tabsViewModel which responsible of managing all the tabs (like activating, creating etc)

At the result you get the knockoutjs based tabs widget:

you can download source from github
or see demo
Now, after we created functioning tabs widget -we ready to go to the next step

Dynamic tabs with knockoutjs - introduction

There is many beautifull javascript tabs-widget examples across the web, many of them fit perfectly for the regular scenarios of displaying static or dynamic content. When talking about dynamic content - it usually means that objects which showed in each tab is from the same type, only the quantity of tabs may vary. (like this example - each tab must have the same structure of header and content).
In this post i will demonstrate how to create tabs widget where each tab is represent a different kind of javascript module, which act independently and in coexistence with other tabs-modules.



i decided to divide this post to 4 parts (so it will be more convenient to read) 
introduction - the Social Netwok app 
part 1 - create a simple tabs widget with knockoutjs (next post)
part 2 - create  3 viewModels for each kind of tabs (next next post)
part 3 - get all the stuff to work together (next next next post)

So here is the first part:


the Social Netwok app - the spec
Lest say we want to create social network, with 'search-firends' page, 'display person details' page and 'home' page, only instead of pages - each view will be displayed on different tab panel:
home:

search:

person details:
To achieve this goal we will need to define 3 kind of different javascript functionalities - one for each page/tab:  'Search' -must be able to perform the  persons search, 'home' -must display the friends list, and 'person details' -must display details of person and to add this person to the friend list which appears on the 'home' page.
Expect of acting independently each functionality must at the same time to connect with other 'brothers' (like add person to friends list)   and to connect with the tabs widget - (like open search view inside tab).

Note that in some cases tabs should be able to contain multiple instances of objects from same type- like two persons...

I hope you enjoyed reading this post so far
so if you want to continue - here is the next part 

Getting started with docker

It is very simple to get started usig docker. All you need to do-is download the docker desktop for your system Once you get docker syste...