题
看完之后 与史蒂夫·桑德森(Steve Sanderson)的淘汰赛。 我认为这对于复杂的UI页面非常好。
我工作了 现场示例 并通读 文档. 。然后我找到了 瑞安·尼梅耶(Ryan Niemeyer)的伟大文章. 。因此,我想建立一个类似于瑞安(Ryan)的示例。
它将显示一个表。桌子的每一行都会有预算。
用户可以输入每个季度的值。
预算减去宿舍的总和将提供剩余的金额。
如果剩余金额不为零,则行将应用于其分类。该类将使背景颜色变成红色。
如果任何行的剩余时间不等于零,则将禁用保存按钮。
我开始工作的是数据的初始显示。
不起作用的是:
- 所有的行都被读取,甚至剩余零。
- 当季度变化时,剩余值不会改变。
- 保存按钮永远不会启用。
- 保存的JSON是不正确的。
代码可以在 这个小提琴 以及下面。
首先,一些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);
解决方案
看看这个: http://jsfiddle.net/rniemeyer/qmxwe/
主要问题是您如何访问观察值。如果您只将可观察到的 data-bind
, ,然后为您解开它。但是,如果您传递了表达式,则需要访问可观察到的功能(例如在实际的JavaScript中)。
因此,您必须进行的更改是:
data-bind="css: { RowError: Remaining() != 0 }"
data-bind="enable: totalRemaining() == 0"
在您的全部依赖性观点中,您需要访问剩余的时间:
var value = parseInt(item.Remaining(), 10);
最后,对于您的JSON问题,我认为处理它的一种很好的方法是向您的 oneCat
类型。在那里您可以删除不需要的属性。看起来像:
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时,它将在看到A时使用您的tojson函数 oneCat
.
希望这可以帮助。