{"id":136,"date":"2015-03-25T22:12:08","date_gmt":"2015-03-25T21:12:08","guid":{"rendered":"http:\/\/www.vosseburcht.com\/?p=136"},"modified":"2015-03-25T22:12:08","modified_gmt":"2015-03-25T21:12:08","slug":"winjs-navigation-and-knockoutjs-expanding-the-to-do-sample","status":"publish","type":"post","link":"https:\/\/vosseburchttechblog.azurewebsites.net\/index.php\/2015\/03\/25\/winjs-navigation-and-knockoutjs-expanding-the-to-do-sample\/","title":{"rendered":"WInJS Navigation and KnockoutJS, expanding the to-do sample"},"content":{"rendered":"<p>I was planning to expand my to-do sample with some more WinJS controls to weed out any bugs that might still be present in the knockout-winjs integration library. After thinking of some controls I would like to test out I figured I would need more than just the single page currently in the sample app. And that of course means some way to navigate between them.<\/p>\n<p>WinJS comes with a navigation stack built-in, and it would be ridiculous not to use that. However a &#8220;simple&#8221; sample I found online (<a href=\"https:\/\/code.msdn.microsoft.com\/Navigation-sample-cf242faa\">this one<\/a>) turned out less simple than I expected. After some digging through the code however I found that for my simple purpose I could strip most of the plumbing and get something actually simple. So first we will set up some plumbing and then add a to-do item edit page.<\/p>\n<p>I&#8217;m building on the outcome of my last blog post: <a href=\"http:\/\/www.vosseburcht.com\/wp-content\/uploads\/2015\/03\/ToDoApp2.zip\">ToDoApp2<\/a><\/p>\n<h2>Setting up the navigation plumbing<\/h2>\n<p>A few changes to the way we initialize the application:<\/p>\n<pre class=\"EnlighterJSRAW\" style=\"width: 778px; height: 691px;\" data-enlighter-language=\"js\">function onDeviceReady() {\n  \/\/ Handle the Cordova pause and resume events\n  document.addEventListener('pause', onPause, false);\n  document.addEventListener('resume', onResume, false);\n\n  ConcreteCoding.KnockoutToWinJS.addBindings(ConcreteCoding.KnockoutToWinJS.controls);\n\n  \/\/ When we navigate to a page, this event listener will render it, and apply our knockoutjs bindings.\n  WinJS.Navigation.addEventListener(\"navigated\", (eventObject) =&gt; {\n    var url = eventObject.detail.location;\n    var host = document.getElementById(\"pageContent\");\n\n    \/\/ Unload the previous page if needed.\n    if (host.winControl &amp;&amp; host.winControl.unload) {\n      host.winControl.unload();\n    }\n    WinJS.Utilities.empty(host);\n\n    eventObject.detail.setPromise(WinJS.UI.Pages.render(url, host, eventObject.detail.state).then(function () {\n      ko.applyBindings(eventObject.detail.state, host);\n    }));\n  });\n\n  WinJS.UI.processAll().then(() =&gt; {\n    \/\/ Navigate to the initial page and supply the original view model as the state parameter.\n    return WinJS.Navigation.navigate(\"\/html\/ToDoView.html\", new ToDoViewModel());\n  });            \n}<\/pre>\n<p>And we also have to extract the page from the index.html into its own ToDoView.html.<\/p>\n<p>Index.html:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"html\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;meta charset=\"utf-8\" \/&gt;\n    &lt;title&gt;ToDoApp&lt;\/title&gt;\n\n    &lt;link href=\"WinJS\/css\/ui-light.css\" rel=\"stylesheet\" \/&gt;\n    &lt;script src=\"WinJS\/js\/WinJS.js\"&gt;&lt;\/script&gt;\n\n    &lt;link href=\"css\/index.css\" rel=\"stylesheet\" \/&gt;\n\n    &lt;script src=\"scripts\/platformOverrides.js\"&gt;&lt;\/script&gt;\n    &lt;script src=\"scripts\/knockout-3.3.0.debug.js\"&gt;&lt;\/script&gt;\n    &lt;script src=\"scripts\/knockout-winjs.js\"&gt;&lt;\/script&gt;\n    &lt;script src=\"scripts\/index.js\"&gt;&lt;\/script&gt;\n\n    &lt;script src=\"cordova.js\"&gt;&lt;\/script&gt;    \n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;div id=\"pageContent\"&gt;&lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p>ToDoView.html:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"html\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;input type=\"text\" data-bind=\"textInput: newDescription\" \/&gt;\n    &lt;br \/&gt;\n    &lt;div data-bind=\"foreach: toDoItems\"&gt;\n        &lt;input type=\"checkbox\" data-bind=\"checked: done\" \/&gt;\n        &lt;span data-bind=\"text: description\"&gt;&lt;\/span&gt;\n        &lt;a href=\"#\" data-bind=\"click: $parent.removeItem.bind($parent, $data)\"&gt;x&lt;\/a&gt;\n        &lt;br \/&gt;\n    &lt;\/div&gt;\n\n    &lt;div data-bind=\"winAppBar: {placement: 'bottom'}\"&gt;\n        &lt;button data-bind=\"winAppBarCommand: {label: 'Add', type: 'button', icon:'add', onclick: addNewItem }\"&gt;&lt;\/button&gt;\n    &lt;\/div&gt;    \n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<h2>Adding the new page with the navigation button<\/h2>\n<p>So ofcourse we create a new file containing the HTML for the new page. Very simple but using the WinJS BackButton control, because we are using the WinJS navigation stack. I also changed some properties in the ToDoItem class so we have a bit more to fill the page with.<\/p>\n<p>The new HTML page:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"html\">&lt;!DOCTYPE html&gt;\n&lt;html&gt;\n&lt;head&gt;\n    &lt;title&gt;&lt;\/title&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;button data-bind=\"winBackButton\"&gt;&lt;\/button&gt;\n    &lt;h1 class=\"win-type-xx-large\"&gt;Edit item&lt;\/h1&gt;\n    &lt;br \/&gt;\n    &lt;label&gt;Title&lt;\/label&gt;\n    &lt;br \/&gt;\n    &lt;input type=\"text\" data-bind=\"textInput: toDoItem().title\" \/&gt;\n    &lt;br \/&gt;\n    &lt;label&gt;Done?&lt;\/label&gt;\n    &lt;input type=\"checkbox\" data-bind=\"checked: toDoItem().done\" \/&gt;\n    &lt;br \/&gt;\n    &lt;label&gt;Description&lt;\/label&gt;\n    &lt;br \/&gt;\n    &lt;textarea data-bind=\"textInput: toDoItem().description\"&gt;&lt;\/textarea&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;<\/pre>\n<p>The new view model class for the above view, and the new ToDoItem class:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">class EditToDoItemViewModel {\n    toDoItem: KnockoutObservable&lt;ToDoItem&gt;;\n\n    constructor(item: ToDoItem) {\n        this.toDoItem = ko.observable(item);\n    }\n}\n\nclass ToDoItem {\n    title: KnockoutObservable&lt;string&gt;;\n    description: KnockoutObservable&lt;string&gt;;\n    done: KnockoutObservable&lt;boolean&gt;;\n\n    constructor(title: string) {\n        this.title = ko.observable(title);\n        this.description = ko.observable(\"\");\n        this.done = ko.observable(false);\n    }\n}<\/pre>\n<p>Finally we add an editItem method that handles navigating to our new page, we place it in the ToDoViewModel class. This method should also be data bound on a button or link for each to-do item in the main page, but I&#8217;ll leave that as an exercise for the reader (or you can just download the complete project at the end of the post):<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"js\">editItem(item: ToDoItem) {\n    WinJS.Navigation.navigate(\"\/html\/EditToDoItemView.html\", new EditToDoItemViewModel(item));\n}<\/pre>\n<p>Now just run it, and you should be able to add items, edit them in a separate edit page, and navigate back to the original page. The complete project can be found here: <a href=\"http:\/\/www.vosseburcht.com\/wp-content\/uploads\/2015\/03\/ToDoApp3.zip\">ToDoApp3<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>I was planning to expand my to-do sample with some more WinJS controls to weed out any bugs that might still be present in the knockout-winjs integration library. After thinking of some controls I would like to test out I figured I would need more than just the single page currently in the sample app. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[],"_links":{"self":[{"href":"https:\/\/vosseburchttechblog.azurewebsites.net\/index.php\/wp-json\/wp\/v2\/posts\/136"}],"collection":[{"href":"https:\/\/vosseburchttechblog.azurewebsites.net\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/vosseburchttechblog.azurewebsites.net\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/vosseburchttechblog.azurewebsites.net\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/vosseburchttechblog.azurewebsites.net\/index.php\/wp-json\/wp\/v2\/comments?post=136"}],"version-history":[{"count":0,"href":"https:\/\/vosseburchttechblog.azurewebsites.net\/index.php\/wp-json\/wp\/v2\/posts\/136\/revisions"}],"wp:attachment":[{"href":"https:\/\/vosseburchttechblog.azurewebsites.net\/index.php\/wp-json\/wp\/v2\/media?parent=136"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/vosseburchttechblog.azurewebsites.net\/index.php\/wp-json\/wp\/v2\/categories?post=136"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/vosseburchttechblog.azurewebsites.net\/index.php\/wp-json\/wp\/v2\/tags?post=136"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}