Note that there are some explanatory texts on larger screens.

plurals
  1. POGetting the Knockout Example to work
    text
    copied!<p>After watching the <a href="http://channel9.msdn.com/Events/MIX/MIX11/FRM08" rel="nofollow">Knockout.JS video with Steve Sanderson</a> I decided this would be great for complicated UI pages.</p> <p>I worked through <a href="http://knockoutjs.com/examples/" rel="nofollow">the live examples</a> and read through <a href="http://knockoutjs.com/documentation/introduction.html" rel="nofollow">the documentation</a>. I then found <a href="http://www.knockmeout.net/2011/04/utility-functions-in-knockoutjs.html" rel="nofollow">Ryan Niemeyer's great article</a>. So I wanted to build an example similar to Ryan’s.</p> <p>It would display a table. Each row of the table would have a budget.<br> The user can enter values for each quarter.<br> The budget minus the sum of the quarters would give the remaining amount.<br> If the remaining amount was not zero the row would have a class applied to it. The class would turn the background color to red.<br> If any rows had a remaining about not equal to zero then the save button would be disabled. </p> <p>What I got working was the initial display of the data.</p> <p>What is not working is: </p> <ul> <li>All rows are read even one with zero remaining. </li> <li>When quarter values change the remaining value does not change. </li> <li>Save button is never enabled. </li> <li>Json for save is not correct. </li> </ul> <p>The code can be found in <a href="http://jsfiddle.net/YsYuw/8/" rel="nofollow">this fiddle</a> as well as below.</p> <p>First, some CSS to make things look right:</p> <pre class="lang-css prettyprint-override"><code> table.pretty { margin: 1em 1em 1em 2em; background: whitesmoke; border-collapse: collapse; } table.pretty th, table.pretty td { border: 1px silver solid; padding: 0.2em; } table.pretty th { background: gainsboro; text-align: left; } table.pretty caption { margin-left: inherit; margin-right: inherit; } .RowError { background-color: Red; color: White; } </code></pre> <p>Then the View:</p> <pre class="lang-html prettyprint-override"><code>&lt;br /&gt;&lt;br /&gt; &lt;p&gt; There are &lt;span data-bind="text: catagoryDetails().length"&gt;&amp;nbsp;&lt;/span&gt; rows in array&lt;br /&gt; I am flexible on changing the structure of data from server.&lt;br /&gt; I am flexible on how viewModel is built as long as it can be loaded from server &lt;br /&gt; I am flexible on how table is built. &lt;/p&gt; &lt;p&gt; As Q1-Q4 values change the Remaining for row changes &lt;br /&gt; Row turns red if Remaining != 0 &lt;br /&gt; Unable to Save until all rows have a remaining of 0.&lt;br&gt; &lt;/p&gt; &lt;table id="pretty" &gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;CatName&lt;/th&gt; &lt;th&gt;Budget&lt;/th&gt; &lt;th&gt;Q1Amt&lt;/th&gt; &lt;th&gt;Q2Amt&lt;/th&gt; &lt;th&gt;Q3Amt&lt;/th&gt; &lt;th&gt;Q4Amt&lt;/th&gt; &lt;th&gt;Remaining&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody data-bind="template: { name: 'catagoryDetailRowTemplate', foreach: catagoryDetails }"&gt;&lt;/tbody&gt; &lt;/table&gt; &lt;script type="text/html" id="catagoryDetailRowTemplate"&gt; &lt;tr data-bind="css: { RowError: Remaining != 0 }"&gt; &lt;td&gt; &lt;input type="hidden" data-bind="value: CatId"/&gt; &lt;span data-bind="text: CatName"&gt; &lt;/span&gt; &lt;/td&gt; &lt;td&gt;&lt;span data-bind="text: BudgetAmt"&gt; &lt;/span&gt;&lt;/td&gt; &lt;td&gt;&lt;input data-bind="value: Q1Amt"/&gt;&lt;/td&gt; &lt;td&gt;&lt;input data-bind="value: Q2Amt"/&gt;&lt;/td&gt; &lt;td&gt;&lt;input data-bind="value: Q3Amt"/&gt;&lt;/td&gt; &lt;td&gt;&lt;input data-bind="value: Q4Amt"/&gt;&lt;/td&gt; &lt;td&gt;&lt;span data-bind="text: Remaining"&gt; &lt;/span&gt;&lt;/td&gt; &lt;/tr&gt; &lt;/script&gt; &lt;form action="ActionOnServer" &gt; &lt;input type="hidden" value="Not Set" id="ForServer" name="ForServer"/&gt; &lt;input type="submit" onclick="SendDataToServer()" value="Save" data-bind="enable: totalRemaining = 0" /&gt; &lt;input type="button" onclick="alert('I would do cancel action')" value="Cancel" /&gt; &lt;/form&gt; </code></pre> <p>And the Javascript:</p> <pre><code> function SendDataToServer() { // build data to send via json var prepDataToSend = ko.toJS(viewModel.catagoryDetails); var mapDataForServer = ko.utils.arrayMap(prepDataToSend, function(item) { delete item.CatName; delete item.Remaining; return item; }); $("#ForServer").val(mapDataForServer); // if not debug return true and remove alert. alert(mapDataForServer); return false; } // data from the server // var dataFromServer = &lt;%= new JavaScriptSerializer().Serialize(Model) %&gt;; // Hard code for now var dataFromServer = [ { "CatId": 1000, "CatName": "Car wax", "Q1Amt": 50, "Q2Amt": 60, "Q3Amt": 90, "Q4Amt": 80, "BudgetAmt": 280 }, { "CatId": 2000, "CatName": "Car Wippers", "Q1Amt": 20, "Q2Amt": 40, "Q3Amt": 60, "Q4Amt": 80, "BudgetAmt": 200 }, { "CatId": 3333, "CatName": "Oil Change", "Q1Amt": 30, "Q2Amt": 70, "Q3Amt": 90, "Q4Amt": 10, "BudgetAmt": 200 }, { "CatId": 4040, "CatName": "Gas", "Q1Amt": 0, "Q2Amt": 0, "Q3Amt": 0, "Q4Amt": 0, "BudgetAmt": 3000 } ]; // constructor for each row of categories ( adds obserbale ) function oneCat(CatId, CatName, Q1Amt, Q2Amt, Q3Amt, Q4Amt, BudgetAmt) { this.CatId = CatId; this.CatName = CatName; this.Q1Amt = ko.observable(Q1Amt); this.Q2Amt = ko.observable(Q2Amt); this.Q3Amt = ko.observable(Q3Amt); this.Q4Amt = ko.observable(Q4Amt); this.BudgetAmt = ko.observable(BudgetAmt); this.Remaining = ko.dependentObservable(function () { var total = this.BudgetAmt(); total = total - this.Q1Amt(); total = total - this.Q2Amt(); total = total - this.Q3Amt(); total = total - this.Q4Amt(); return total; }, this); } var mappedFromServer = ko.utils.arrayMap(dataFromServer, function (item) { return new oneCat(item.CatId, item.CatName, item.Q1Amt, item.Q2Amt, item.Q3Amt, item.Q4Amt, item.BudgetAmt); }); // Here's my data model var viewModel = { catagoryDetails: ko.observableArray([]) }; // add total of remaining viewModel.totalRemaining = ko.dependentObservable(function () { var total = 0; ko.utils.arrayForEach(this.catagoryDetails(), function (item) { var value = parseInt(item.Remaining, 10); if (!isNaN(value)) { total += value; } }); return total; }, viewModel); viewModel.catagoryDetails(mappedFromServer); // turn on Knockout with the model viewModel ko.applyBindings(viewModel); </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload