nested foreach

https://groups.google.com/d/topic/knockoutjs/J_PgZU2L5Nk/discussion

by gene

HTML

<script src="http://rniemeyer.github.com/KnockMeOut/Scripts/jquery.tmpl.js"></script>
<script src="https://github.com/SteveSanderson/knockout/raw/master/build/output/knockout-latest.debug.js"></script>
<div>Searcher: <span data-bind="text: searcher().name"></span></div>
<div>Topic: <span data-bind="text: activeTopic().title">&nbsp;</span></div>
<div>Topics: <span data-bind="text: topics().length">&nbsp;</span></div>
<hr />

<div class="topicMenu" data-bind='template: { name: "topicMenuTemplate", foreach: topics }'> </div>
<script type="text/html" id="topicMenuTemplate">
    <a href="#" data-bind="click: select, css: {active: selected()}"><span class="menuitem" data-bind="text: title()">&nbsp;</span></a>
</script>

<hr />

<div class="topicDetail" data-bind='template: { name: "topicTemplate", data: activeTopic}'> </div>
<script type="text/html" id="topicTemplate">
<div>
    <span data-bind="text: queries().length">&nbsp;</span> queries:
    <div class="queries">
        <div data-bind='template: {name: "queryTemplate", foreach: queries}'></div>    
    </div>      
</div> 
</script>


<script type="text/html" id ="queryTemplate">
    <div class="query" data-bind="css: { active: selected()}">
        <span data-bind="click: select">{{= text}}</span>
        <span>by</span> <span data-bind='template: {name: "personTemplate", data: searcher }'>&nbsp;</span>
    </div>
</script>

<script type="text/html" id ="personTemplate">
    <span class="person" data-bind="style: { color: color() }">{{= name()}}</span>
</script>

CSS

body {
        font-family: sans-serif;
        font-color: black;
    }
    a {
        text-decoration: none;
    }
    span.menuitem {
        display: inline-block;
        margin: 10px;
        font-weight: bold;
    }
    
    .active {
        background: lightgrey;
    }

JavaScript

var topic = function(id, title, queries, viewModel) {
    this.id = ko.observable(id);
    this.title = ko.observable(title);
    this.queries = ko.observableArray(queries);
    this.currentQuery = ko.observable();
    this.viewModel = viewModel;
    
    this.selected = function() {
        return this.viewModel.activeTopic() == this;
    };
    
    this.select = function() {
        this.viewModel.activeTopic(this);
    }
    
    this.toString = function() { return "[" + this.id() + "] topic"; };
    
    return this;
};
    
    
var query = function(id, text, searcher ) {
    this.id = ko.observable(id);
    this.text = ko.observable(text);
    this.topic = topic;
    this.searcher = ko.observable(searcher);
    
    this.selected = function() {
        return this.topic.currentQuery() == this;
    };
    
    this.select = function() {
        this.topic.currentQuery(this);
    };
    
    return this;
};

var searcher = function(id, name, color) {
    this.id = ko.observable(id);
    this.name = ko.observable(name);
    this.color = ko.observable(color);
    
    return this;
};
    
    
var people = [new searcher(1, 'Joe Searcher', 'red'), new searcher(2, 'Library Loon', 'blue')];
    
var viewModel = {
    searcher: ko.observable(people[0]),
    topics: ko.observableArray([]),
    activeTopic: ko.observable()
};

var queries = [new query(1, 'first query', people[0]), new query(2, 'second query', people[1]), 
               new query(3, 'third query', people[0]), 
               new query(4, 'fourth query', people[1]), new query(5, 'fifth query', people[1]), 
               new query(6, 'last query', people[1])];
var topics = [
    new topic(1, 'first', [queries[0], queries[1], queries[2]], viewModel),
    new topic(2, 'second topic', [queries[3], queries[4]], viewModel)
];
    queries[0].topic = topics[0];
    queries[1].topic = topics[0];
    queries[2].topic = topics[0];
    queries[3].topic = topics[1];
    queries[4].topic =...