这是我的问题,我正在努力。
1-我正在使用映射插件来创建Knockout ViewModel。从服务器端我有一个复杂的对象,它有几个属性(与这个问题无关)。除了其他属性,我有一个属性TotalofExpenses,还有一个名为Expenses的列表(费用项是一个有金额和描述的对象)。映射插件将此列表转换为ObservableArray,将TotalofExpenses转换为observalbe
2-在UI上我想实现以下
以下是我试图实现上述功能的代码。它没有按照预期的那样工作。 注意:我试图只添加与此问题相关的代码
var data = @Html.Raw(Model.ToJson());
//Add total expenses TotalShiftExpenses
var mappingOptionsForTotalShiftExpenses = {
create: function(options) {
return new ComputeTotalShiftExpenses(options.data);
}
};
var Expense = function(data) {
var self = this;
self.ShiftId = ko.observable(@Model.Shift.Id),
self.Id = ko.observable(0),
self.Amount = ko.observable(0),
self.Description = ko.observable("");
}
function ComputeTotalShiftExpenses(data) {
var self = this;
var model = ko.mapping.fromJS(data, {}, self);
model.TotalShiftExpenses = ko.computed(function() {
var sum = 0;
for (var i = 0; i < model.Shift.ShiftExpenses().length; i++) {
// if ((parseFloat(model.Shift.ShiftExpenses()[i].Amount)) > 0) {
sum += parseFloat(model.Shift.ShiftExpenses()[i].Amount());
}
//Add empty Expense to Observable array
if (parseFloat(model.Shift.ShiftExpenses()[ (model.Shift.ShiftExpenses().length - 1)].Amount()) > 0) {
model.Shift.ShiftExpenses
.push( new Expense({ "ShiftId": 0, "Id": 0, "Amount": 0, "Description": "" }));
}
return sum.toFixed(2);
});
return model;
}
//Apply MappingOptions
var AppViewModel = ko.mapping.fromJS(data, mappingOptionsForTotalShiftExpenses);
$(document)
.ready(function() {
ko.applyBindings(AppViewModel);
});
通过上面的代码,我能够得到 - 第一个费用项目的第一个输入框 - 一旦我添加费用项目,它将显示为下一个项目设置的另一个输入框,并显示总计。 - 问题是当我尝试添加第二个费用时它没有触发任何东西(似乎第二组输入框是不可观察的)。但是,如果我在第一个费用项目中进行金额更改,它将触发可观察行为,并将正确添加两个项目的项目..并添加第三个费用的UI输入框。由于某种原因,最后一个输入框不会触发可观察的行为,但是在最后一个输入框之前的任何一个框都会触发。 注意:我发布的代码只是试图增加费用。
迫不及待地想要听一些淘汰专家的消息......提前多多感谢。
答案 0 :(得分:0)
因此,根据您所说的内容,我创建了一个简单的小提琴,输入字段的数量由ko.observableArray
和foreach
绑定控制。当您从数组中添加或删除(不是小提琴中)项目时,视图会自动更新。在你的情况下,似乎你需要做更多的功能和其他特殊检查,但我相信这可以帮助你完成你想要完成的任务。
function TestVM() {
this.inputFields = ko.observableArray([
{ type: 'type1', displayName: 'name1' },
{ type: 'type2', displayName: 'name2' },
{ type: 'type3', displayName: 'name3' }
]);
}
var dynamic = new TestVM();
ko.applyBindings(dynamic);
$('#addInput').click(function(){
dynamic.inputFields.push({ type: 'newtype', displayName: 'newField' });
console.log(dynamic.inputFields());
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<div data-bind="foreach: inputFields">
<label data-bind="text: $data.displayName"></label><br>
<input data-bind="name: $data.type"><br>
</div>
</div>
<button id="addInput">Add</button>
答案 1 :(得分:0)
在我看来,你有一些过于复杂的东西。在数据中映射并创建跟踪总量的计算可观察量应该非常简单:
编辑:已更新,以包含动态输入框功能。您可以通过向计算的observable添加几行代码来完成此操作,以保证数组底部始终存在空白项:
// Data pulled in from wherever
var data = [{
ShiftId: 1,
Id: 1,
Amount: 25.00,
Description: 'Supplies'
}, {
ShiftId: 2,
Id: 2,
Amount: 20.00,
Description: 'Lunch'
}];
// Expense model
var Expense = function(shiftId, id, amount, desc) {
var self = this;
self.ShiftId = ko.observable(shiftId);
self.Id = ko.observable(id);
self.Amount = ko.observable(amount);
self.Description = ko.observable(desc);
};
// Main ViewModel
var AppViewModel = function() {
var self = this;
// Expenses array
self.Expenses = ko.mapping.fromJS(data);
// Compute the grand total of all expenses
self.TotalofExpenses = ko.computed(function() {
// First, ensure that a blank item remains at the bottom of array
self.Expenses.remove(function(item) {
return !item.Amount() && !item.Description();
});
self.Expenses.push(new Expense());
// Now calculate the sum of all expenses
var total = 0;
self.Expenses().forEach(function(item, index) {
total += +item.Amount() || 0;
});
return total;
});
};
ko.applyBindings(new AppViewModel());
编辑2:更新了JSFiddle,以便在用户为Description输入提供值之前禁用Amount输入框(请参阅JSFiddle链接中的HTML)。我这样做只需添加&#39;启用:说明&#39;到金额输入框的绑定。