让Knockout示例正常工作

时间:2011-04-27 16:04:15

标签: knockout.js

在观看Knockout.JS video with Steve Sanderson之后,我认为这对于复杂的UI页面非常有用。

我完成了the live examples并阅读了the documentation。 然后我找到了Ryan Niemeyer's great article。所以我想建立一个类似于Ryan的例子。

它会显示一个表格。表格的每一行都有预算 用户可以输入每个季度的值 预算减去季度总和将给出剩余金额 如果剩余金额不为零,则该行将应用一个类。 该课程将背景颜色变为红色 如果任何行的剩余大约不等于零,则将禁用保存按钮。

我开始工作的是数据的初始显示。

什么是无效的:

  • 即使是剩余零的一行,也会读取所有行。
  • 当季度值更改时,剩余值不会更改。
  • 永远不会启用“保存”按钮。
  • Json for save不正确。

代码可以在this fiddle以及下方找到。

首先,一些CSS使事情看起来正确:

   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;  
    }  

然后是视图:

<br /><br />  
<p>  
   There are <span data-bind="text: catagoryDetails().length">&nbsp;</span> rows in array<br />  
   I am flexible on changing the structure of data from server.<br />  
   I am flexible on how viewModel is built as long as it can be loaded from server <br />  
   I am flexible on how table is built.  
</p>  
<p>
   As Q1-Q4 values change the Remaining for row changes <br />  
   Row turns red if Remaining != 0 <br />  
   Unable to Save until all rows have a remaining of 0.<br>  
</p>  

<table id="pretty" > 
   <thead>
       <tr>
           <th>CatName</th>
           <th>Budget</th>
           <th>Q1Amt</th>
           <th>Q2Amt</th>
           <th>Q3Amt</th>
           <th>Q4Amt</th>
           <th>Remaining</th>
       </tr>
   </thead>
   <tbody data-bind="template: { name: 'catagoryDetailRowTemplate', foreach: catagoryDetails }"></tbody> 
</table>  

<script type="text/html" id="catagoryDetailRowTemplate"> 
   <tr data-bind="css: { RowError: Remaining !=  0 }">
       <td>
           <input type="hidden" data-bind="value: CatId"/>
           <span data-bind="text: CatName"> </span>
       </td>

       <td><span data-bind="text: BudgetAmt"> </span></td>
       <td><input data-bind="value: Q1Amt"/></td>
       <td><input data-bind="value: Q2Amt"/></td>
       <td><input data-bind="value: Q3Amt"/></td>
       <td><input data-bind="value: Q4Amt"/></td>
       <td><span data-bind="text: Remaining"> </span></td>
   </tr> 
</script>
<form action="ActionOnServer" >
   <input type="hidden" value="Not Set" id="ForServer" name="ForServer"/>
   <input type="submit" onclick="SendDataToServer()" value="Save" data-bind="enable: totalRemaining = 0" />
   <input type="button" onclick="alert('I would do cancel action')" value="Cancel" />
</form>

和Javascript:

   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 = <%= new JavaScriptSerializer().Serialize(Model) %>; 

   // 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);

1 个答案:

答案 0 :(得分:4)

看一下这个:http://jsfiddle.net/rniemeyer/qmXWE/

主要问题是如何访问您的observable。如果您只是将观察者传递给data-bind,那么它会为您解开它。但是,如果要传递表达式,则需要将observable作为函数访问(就像在实际的JavaScript中一样)。

因此,您必须做出的更改是:

data-bind="css: { RowError: Remaining() != 0 }"

data-bind="enable: totalRemaining() == 0"

在您的totalRemaining dependentObservable中,您需要访问剩余的:

var value = parseInt(item.Remaining(), 10);

最后,对于您的JSON问题,我认为处理它的一个好方法是向您的oneCat类型添加toJSON方法。在那里,您可以删除不需要的属性。它看起来像是:

oneCat.prototype.toJSON = function() {
    var copy = ko.toJS(this);  //easy way to get a copy
    delete copy.CatName;
    delete copy.Remaining;
    return copy;
}

然后,您可以在对象上使用ko.toJSON,并且在调用JSON.stringify时,只要看到oneCat,它就会使用您的toJSON函数。

希望这有帮助。