質問
見た後 Steve Sandersonとのknockout.jsビデオ これは、複雑なUIページに最適だと判断しました。
私は働きました ライブの例 そして読み通します ドキュメント. 。それから私は見つけました ライアン・ニーマイヤーの素晴らしい記事. 。だから私はライアンのような例を作りたかった。
テーブルが表示されます。テーブルの各行には予算があります。
ユーザーは、各四半期の値を入力できます。
予算から四半期の合計を差し引いて、残りの金額が得られます。
残りの金額がゼロでない場合、行にクラスが適用されます。クラスは背景色を赤に変えます。
ゼロに等しくない行が残っている場合、保存ボタンは無効になります。
私が働いたのは、データの最初の表示でした。
機能していないのは次のとおりです。
- すべての行は、残りがゼロの1つでも読み取られます。
- 四半期の値が変更された場合、残りの値は変更されません。
- 保存ボタンが有効になることはありません。
- 保存のための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/
主な問題は、観察可能なものにどのようにアクセスするかです。あなたが観察可能なものをaに渡すだけの場合 data-bind
, 、それからそれはあなたのためにそれを解き放ちます。ただし、式を渡す場合は、関数として観測可能なものにアクセスする必要があります(実際のJavaScriptなど)。
したがって、あなたがしなければならない変更は次のとおりです。
data-bind="css: { RowError: Remaining() != 0 }"
data-bind="enable: totalRemaining() == 0"
そして、あなたのtotallemaining depentententobservableでは、次のように残ることにアクセスする必要があります。
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を呼び出すと、Tojson関数が表示されるたびに使用できます。 oneCat
.
お役に立てれば。