#knockout.js
#knockout.js
Вопрос:
После просмотра нокаута.JS видео со Стивом Сандерсоном Я решил, что это было бы здорово для сложных страниц пользовательского интерфейса.
Я проработал живые примеры и прочитал документацию. Затем я нашел отличную статью Райана Нимейера. Итак, я хотел создать пример, подобный примеру Райана.
Это отобразило бы таблицу. Каждая строка таблицы будет иметь бюджет.
Пользователь может вводить значения для каждой четверти.
Бюджет за вычетом суммы кварталов даст оставшуюся сумму.
Если бы оставшаяся сумма не была равна нулю, к строке был бы применен класс. Класс изменил бы цвет фона на красный.
Если бы в каких-либо строках осталось примерно не равное нулю, кнопка сохранения была бы отключена.
То, что у меня получилось, — это начальное отображение данных.
Что не работает, так это:
- Все строки считываются, даже одна с оставшимся нулем.
- При изменении значений квартала оставшееся значение не изменяется.
- Кнопка сохранения никогда не включена.
- 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">amp;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:
Взгляните на этот пример: http://jsfiddle.net/rniemeyer/qmXWE /
Основная проблема заключается в том, как вы получаете доступ к своим наблюдаемым. Если вы только передаете наблюдаемое в data-bind
, то оно разворачивает его для вас. Однако, если вы передаете выражение, то вам необходимо получить доступ к наблюдаемому как к функции (как в вашем реальном JavaScript).
Итак, изменения, которые вам нужно будет внести, это:
data-bind="css: { RowError: Remaining() != 0 }"
data-bind="enable: totalRemaining() == 0"
и в вашем totalRemaining зависимом Observable вам нужно получить доступ к оставшимся подобным:
var value = parseInt(item.Remaining(), 10);
Наконец, что касается вашей проблемы с JSON, я думаю, что хороший способ справиться с ней — добавить метод toJSON к вашему 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
.
Надеюсь, это поможет.