mtyson
mtyson

Reputation: 8550

Knockout.js - How to toggle between views

Alright, newbie Knockout question here.

In this example: http://learn.knockoutjs.com/WebmailExampleStandalone.html#Inbox

How does the mail detail view replace the folder list view?

That is, what feature causes the divs to be toggled? In inspecting the dom, I see that what happens is the div's are actually rendered empty when not displayed.

Can someone enlighten me? I know this is rather basic, but its the last piece I need to click into place for my understanding.

Just to be 100% clear: when you click on a row in the folder list, what causes the folder view to be emptied and the mail detail to display? Is it the with binding? (That doesn't seem right.)

Upvotes: 1

Views: 433

Answers (2)

Anders
Anders

Reputation: 17554

That is just a Demo on a light weight SPA scenario, with binding is just a inline template binding. Not very useful for a dynamic SPA. Like Nemesv suggests use the template binding.

The problem with the template binding is that its very verbose to use, I have addressed this in my Binding convention library (One of many features)

Instead of doing

<div id="mainView" data-bind="{template: {name: templateName, data: activeView}}">
</div>

You do

<div id="mainView" data-name="activeView">
</div>

My library will do the rest, check out the wiki on templates here

https://github.com/AndersMalmgren/Knockout.BindingConventions/wiki/Template-convention

And a little fiddle http://jsfiddle.net/xJL7u/11/

Upvotes: 1

nemesv
nemesv

Reputation: 139758

You are on the right track with the with binding: in this example the views are changed using the with binding relaying on this feature of the binding:

The with binding will dynamically add or remove descendant elements depending on whether the associated value is null/undefined or not

So in the viewmodel code you will see something like this:

this.get('#:folder', function () {
    self.chosenFolderId(this.params.folder);
    self.chosenMailData(null);
    $.get("/mail", { folder: this.params.folder }, self.chosenFolderData);
});

this.get('#:folder/:mailId', function () {
    self.chosenFolderId(this.params.folder);
    self.chosenFolderData(null);
    $.get("/mail", { mailId: this.params.mailId }, self.chosenMailData);
});

So the functions which are "chaining" the view nulls out one of the properties while filling in the other which toggles the views defined as:

<!-- Chosen mail -->
    <div class="viewMail" data-bind="with: chosenMailData">
    ...
    <div/>

<!-- Mails grid -->
    <table class="mails" data-bind="with: chosenFolderData">
    </table>

This is not the nicest solution but don't forget that Knockout is a Databind/MVVM library and not a full blown SPA framework so it does not have concepts for layouting and higher level view composition.

However this could be made nicer with using the template binding:

<div id="mainView" data-bind="{template: {name: templateName, data: activeView}}">
</div>

And turning the views into templates:

<script type="text/html" id="ChosenMail">
    <div class="viewMail">
    ...
    <div/>
</script>

<script type="text/html" id="MailsGrid">
    <table class="mails">
     ...
    </table>
</script>

And in the routing only set the activeView property and lookup the corresponding template name for it:

this.get('#:folder', function () {
    $.get("/mail", { folder: this.params.folder }, function(data) {
       self.activeView(data);
       self.templateName('ChosenMail');
    });;
});

this.get('#:folder/:mailId', function () {
    $.get("/mail", { mailId: this.params.mailId }, function(data) {
         self.activeView(data);
         self.templateName('MailsGrid');
    });
});

But because this is quite much manual and error prone work I would use something like Durandal.js which is a real SPA framework and it was designed for this kind scenarios.

Upvotes: 1

Related Questions