Ajax Loader
×

Knockout Tab Control with Templates

shows a knockout tab binding that uses templates for the tab and the content

HTML
<html>
1
<html>
2
  <head>
3
    <script src="//cdnjs.cloudflare.com/ajax/libs/knockout/2.2.1/knockout-min.js"></script>
4
  </head>
5
  <body>
6
    <div data-bind="tabs: { foreach: tabs, 'tab-template': 'tab-template', 'content-template': 'content-template' }">    
7
    
8
</div>
9
    <script type="text/html" id="tab-template">
10
    <div class="tab" data-bind="css: {selected: $parent.selectedTab() == $data}, text: name, click: $parent.selectedTab.bind($parent, $data)"></div>
11
</script>
12
    <script type="text/html" id="content-template">
13
    <div data-bind="if: $parent.selectedTab() == $data">
14
        <div data-bind="text: text"></div>
15
        
16
        <div data-bind="if: tabs().length > 0">
17
    <div data-bind="tabs: { foreach: tabs, 'tab-template': 'tab-template', 'content-template': 'content-template' }"></div>
18
    </div>
19
    </div>
20
</script>
21
  </body>
22
</html>
 
CSS
body { padding: 10px; background-image: linear-gradient(#eee, #aaa); }
1
body { padding: 10px; background-image: linear-gradient(#eee, #aaa); }
2
.tabBar { 
3
    border: 1px solid black;
4
    border-radius: 5px 5px 0 0;
5
    padding: 3px 3px 1px 3px;
6
    background: linear-gradient(silver, white 20%, silver 50%, grey 80%);
7
}
8
.tab {  
9
  cursor: default;
10
    border: 1px solid grey;
11
    border-bottom: none;
12
    border-radius: 4px 4px 0 0;
13
    padding: 1px 10px 0 10px;
14
    background: linear-gradient(silver 30%, grey 100%);
15
  color: white;
16
    display: inline-block;
17
    margin-top: 4px;
18
}
19
.tab.selected {
20
    font-weight: bold;
21
    padding-top: 3px;
22
  margin-top: 2px;
23
    background: linear-gradient(white 30%, silver 70%, grey 100%);
24
  color: black;
25
}
26
.tab:hover {
27
    background: linear-gradient(white 30%, silver 70%, grey 100%);
28
  color: black;
29
}
30
 
31
.tabContent {
32
    border: 1px solid black;
33
    border-top: none;
34
    height: 50%;
35
  padding: 10px;
36
    background: whitesmoke;
37
}
 
JavaScript
ko.bindingHandlers.tabs = {
1
ko.bindingHandlers.tabs = {
2
    init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
3
        // This will be called when the binding is first applied to an element
4
        // Set up any initial state, event handlers, etc. here
5
      // First get the latest data that we're bound to
6
        var value = valueAccessor();
7
      
8
      var tabBar = document.createElement('div');
9
      tabBar.className = 'tabBar';
10
      element.appendChild(tabBar);
11
      
12
      var headerValue = { name: value['tab-template'], foreach: value.foreach };
13
      ko.bindingHandlers.template.init(tabBar, function(){return headerValue;}, allBindingsAccessor, viewModel, bindingContext);
14
      
15
      var tabContent = document.createElement('div');
16
      tabContent.className = 'tabContent';
17
      element.appendChild(tabContent);  
18
      
19
      var contentValue = { name: value['content-template'], foreach: value.foreach };
20
      ko.bindingHandlers.template.init(tabContent, function(){return contentValue;}, allBindingsAccessor, viewModel, bindingContext);
21
    return { 'controlsDescendantBindings': true };
22
    },
23
    update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
24
        // This will be called once when the binding is first applied to an element,
25
        // and again whenever the associated observable changes value.
26
        // Update the DOM element based on the supplied values here.
27
      // First get the latest data that we're bound to
28
        var value = valueAccessor();
29
      
30
      var headerValue = { name: value['tab-template'], foreach: value.foreach };  
31
      
32
      var contentValue = { name: value['content-template'], foreach: value.foreach };
33
      
34
      for (var i = 0; i < element.childNodes.length; i++) {
35
    if (element.childNodes[i].className == "tabBar") {
36
      ko.bindingHandlers.template.update(element.childNodes[i], function(){return headerValue;}, allBindingsAccessor, viewModel, bindingContext);
37
    }   
38
        else if (element.childNodes[i].className == "tabContent"){
39
          ko.bindingHandlers.template.update(element.childNodes[i], function(){return contentValue;}, allBindingsAccessor, viewModel, bindingContext);
40
        }
41
}
42
    }
43
};
44
 
45
function ViewModel(){
46
    var self = this;
47
    self.Tab = function(id, name, text, selected){
48
        var tab = new ViewModel();
49
        tab.id = ko.observable(id);
50
        tab.name = ko.observable(name);
51
        tab.text = ko.observable(text);
52
        return tab;
53
    };
54
        self.id = ko.observable();
55
        self.name = ko.observable();
56
        self.text = ko.observable();
57
        self.tabs = ko.observableArray();
58
    self.selectedTab = ko.observable();
59
    self.tabs = ko.observableArray();
60
  self.addTab = function(tab){
61
    self.tabs.push(tab);
62
    if(!self.selectedTab()){
63
      self.selectedTab(tab);
64
    }
65
  };
66
    return self;
67
}
68
 
69
var vm = new ViewModel();
70
var tab1 = new vm.Tab(1, 'Tab 1', 'I have sub tabs!');
71
 
72
    tab1.addTab(new vm.Tab(11, 'Subtab 1', 'I\'m a sub-tab!'));
73
    tab1.addTab(new vm.Tab(12, 'Subtab 2', 'Here are some details...'));
74
    tab1.addTab(new vm.Tab(13, 'Subtab 3', 'Hello World!'));
75
 
76
    vm.addTab(tab1);
77
    vm.addTab(new vm.Tab(2, 'Tab 2', 'This is Tab 2...'));
78
 
79
var tab3 = new vm.Tab(3, 'Tab 3', 'I\'m tab 3');
80
var tab5 = new vm.Tab(31, 'Subtab 4', 'I\'m a sub-tab!');
81
    tab5.addTab(new vm.Tab(311, 'Subtab 7', 'I\'m a sub-tab!'));
82
    tab5.addTab(new vm.Tab(312, 'Subtab 8', 'Here are some details...'));
83
    tab5.addTab(new vm.Tab(313, 'Subtab 9', 'Hello World!'));
84
 
85
    tab3.addTab(tab5);
86
    tab3.addTab(new vm.Tab(32, 'Subtab 5', 'Here are some details...'));
87
    tab3.addTab(new vm.Tab(33, 'Subtab 6', 'Hello World!'));
88
    vm.addTab(tab3);
89
 
90
    vm.addTab(new vm.Tab(4, 'Tab 4', 'Hello World!'));
91
 
92
ko.applyBindings(vm);
 

Knockout Tab Control with Templates

CSSDeck G+