在观看Knockout.JS video with Steve Sanderson之后,我认为这对于复杂的UI页面非常有用。
我完成了the live examples并阅读了the documentation。 然后我找到了Ryan Niemeyer's great article。所以我想建立一个类似于Ryan的例子。
它会显示一个表格。表格的每一行都有预算 用户可以输入每个季度的值 预算减去季度总和将给出剩余金额 如果剩余金额不为零,则该行将应用一个类。 该课程将背景颜色变为红色 如果任何行的剩余大约不等于零,则将禁用保存按钮。
我开始工作的是数据的初始显示。
什么是无效的:
代码可以在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"> </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);
答案 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函数。
希望这有帮助。