Nachdem ich die Knockout.JS-Video mit Steve Sanderson Ich habe beschlossen, dass dies für komplizierte UI-Seiten hervorragend geeignet ist.
Ich arbeitete durch die Live-Beispiele und lesen Sie durch die Dokumentation . Ich fand dann Ryan Niemeyers großartiger Artikel . Deshalb wollte ich ein ähnliches Beispiel wie das von Ryan bauen.
Es würde eine Tabelle angezeigt. Jede Zeile der Tabelle würde ein Budget enthalten.
Der Benutzer kann für jedes Quartal Werte eingeben.
Das Budget abzüglich der Summe der Quartale würde den Restbetrag ergeben.
Wenn der verbleibende Betrag nicht Null ist, wird die Zeile mit einer Klasse versehen. Die Klasse würde die Hintergrundfarbe in Rot ändern.
Wenn eine Zeile einen Restwert ungleich Null hätte, wäre die Schaltfläche "Speichern" deaktiviert.
Was ich zum Laufen gebracht habe, war die anfängliche Anzeige der Daten.
Was nicht funktioniert, ist:
- Alle Zeilen werden gelesen, auch die mit der verbleibenden Null.
- Wenn sich Viertelwerte ändern, ändert sich der Restwert nicht.
- Die Schaltfläche Speichern ist nie aktiviert.
- Json zum Speichern ist nicht korrekt.
Der Code ist zu finden in diese Fiedel als auch unten.
Zuerst etwas CSS, damit alles richtig aussieht:
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;
}
Dann die Ansicht:
<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>
Und das 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);