Knockout JS foreach绑定用最后一个索引值覆盖值



我想显示从可观察数组到视图的数据。当我使用foreach绑定时,显示的值全部被最后一个索引值替换,就像在中我想显示数据A、B、C,但它显示了C、C、C如果数据给出值A,B,则显示B,B我不知道出了什么问题,因为这是我第一次在使用foreach 时遇到这个问题

这是我使用的观点。Foreach部门显示不错,但联合操作和详细操作则不然。

function AddDepartmentModel(deptname, deptcode, coaop) {
var self = this;
self.deptname = ko.observable(deptname);
self.deptcode = ko.observable(deptcode);
self.coaOperasional = ko.observableArray([]);
$(coaop).each(function(key, item) {
self.coaOperasional.push(new AddCoaOperasional(item.type, item.detail));
});
self.totalOperasional = ko.computed(function() {
var total = 0;
for (var i = 0; i < self.coaOperasional().length; i++) {
if (!jQuery.isEmptyObject(self.coaOperasional()[i])) {
for (var j = 0; j < self.coaOperasional()[i].detailOperasional().length; j++) {
total += parseFloat(removePeriod(self.coaOperasional()[i].detailOperasional()[j].totalop(), ","));
}
}
}
return addPeriod(total, ",")
});
}
function AddCoaOperasional(type, detail) {
var self = this;
self.optype = ko.observable(type);
self.detailOperasional = ko.observableArray([]);
$(detail).each(function(key, item) {
self.detailOperasional.push(new AddDetailOperasional(item.code, item.coa, item.total, item.parent));
});
self.optypetotal = ko.computed(function() {
var total = 0;
for (var i = 0; i < self.detailOperasional().length; i++) {
var tamptot = self.detailOperasional()[i].totalop();
tamptot = tamptot.replace("<b>", "");
tamptot = tamptot.replace("</b>", "");
total += parseFloat(removePeriod(tamptot, ","));
}
return addPeriod(total, ",")
});
}
function AddDetailOperasional(code, coa, total, parent) {
var self = this;
if (parent == "yes") {
self.codeop = ko.observable("");
self.coaop = ko.observable("<b>" + coa + "</b>");
self.totalop = ko.observable("<b>" + addPeriod(Math.abs(total), ",") + "</b>");
} else {
self.codeop = ko.observable(code);
self.coaop = ko.observable(coa);
self.totalop = ko.observable(addPeriod(Math.abs(total), ","));
}
}
function MainModel() {
var self = this;
var listdept = getData();
self.departments = ko.observableArray();
listdept.map(function(i) {
self.departments.push(new AddDepartmentModel(i.deptname, i.deptcode, i.operasional));
});
}
var vm = new MainModel();
ko.applyBindings(vm);
function addPeriod(num, period) {
return num.toFixed(2).replace('.', period);
}
function removePeriod(str, period) {
return +str.replace(period, '.');
}

function getData() {
return [{
"deptname": "LEARNING JOURNEY",
"deptcode": "1",
"operasional": [
{
"type": "PENDAPATAN OPERASIONAL",
"detail": [
{
"coa": "PENDAPATAN UANG SEKOLAH BRUTO",
"code": "4.1.01.01",
"total": "0.00",
"parent": "no"
},
{
"coa": "POTONGAN UANG SEKOLAH",
"code": "4.1.01.91",
"total": "0.00",
"parent": "no"
},
{
"coa": "PENDAPATAN UANG SEKOLAH NETO",
"code": "4.1.01",
"total": "0.00",
"parent": "yes"
},
{
"coa": "PENDAPATAN UANG KEGIATAN BRUTO",
"code": "4.1.02.01",
"total": "0.00",
"parent": "no"
},
{
"coa": "POTONGAN UANG KEGIATAN",
"code": "4.1.02.91",
"total": "0.00",
"parent": "no"
},
{
"coa": "PENDAPATAN UANG KEGIATAN NETO",
"code": "4.1.02",
"total": "0.00",
"parent": "yes"
},
]
},
{
"type": "BEBAN OPERASIONAL",
"detail": [
{
"coa": "BEBAN REMUNERASI",
"code": "5.1.01.01",
"total": "0.00",
"parent": "no"
},
{
"coa": "BEBAN SEKOLAH",
"code": "5.1.01",
"total": "0.00",
"parent": "yes"
},
{
"coa": "BEBAN ADMINISTRASI KESISWAAN",
"code": "5.1.02.01",
"total": "0.00",
"parent": "no"
},
{
"coa": "BEBAN ASURANSI SISWA",
"code": "5.1.02.02",
"total": "0.00",
"parent": "no"
},
{
"coa": "BEBAN PENGHAPUSAN",
"code": "5.2.91",
"total": "0.00",
"parent": "yes"
}
]
}
],
"pajak": "0.00"
}];
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<table>
<tbody>
<tr data-bind="foreach: departments">
<td style="vertical-align:top">
<table class='table table-striped nowrap'>
<thead>
<tr>
<th colspan="2" data-bind="visible: $index() == 0"></th>
<th style="width:400px"><span data-bind="text: deptname">Nama Dept</span></th>
</tr>
</thead>
<tbody>
<tr>
<td colspan="3">
<table class='table table-striped nowrap' data-bind="foreach: coaOperasional" style="width: 100%">
<tbody>
<tr>
<td colspan="2" data-bind="visible: $parentContext.$index() == 0">
<b><span data-bind="text: optype">Tipe</span></b>
</td>
<td><b>&nbsp;</b></td>
</tr>
</tbody>
<tbody data-bind="foreach: detailOperasional">
<tr>
<td data-bind="text: codeop,visible: $parentContext.$parentContext.$index() == 0">### code ###</td>
<td data-bind="html: coaop,visible: $parentContext.$parentContext.$index() == 0">### coa ###</td>
<td data-bind="html: totalop">0</td>
</tr>
</tbody>
<tbody>
<tr>
<td data-bind="visible: $parentContext.$index() == 0"></td>
<td data-bind="visible: $parentContext.$index() == 0"><b>JUMLAH <span data-bind="text: optype">Tipe</span></b></td>
<td><b><span data-bind="text: optypetotal"></span></b></td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr>
<td data-bind="visible: $index() == 0" colspan="2"><b>SURPLUS (DEFISIT) OPERASIONAL</b></td>
<td><b><span data-bind="text: totalOperasional"></span></b></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>

方法中的一个根本错误是,您试图在视图模型中格式化数据,例如格式化财务价值或将其加粗。

永远不要那样做。

所有格式化都应该通过CSS或绑定在视图中进行。创建一个自定义绑定处理程序来格式化财务值以供显示,比像现在这样不断格式化和取消格式化模型中的数字要容易得多。

其他备注:

  • 基于某些条件(例如parent与否(显示或隐藏某些内容(如某个详细记录的code(也是视图中最好的做法。使用if:ifnot:很容易
  • 我根本不明白你的visible: $parentContext.$index() == 0支票的目的。按照这种方式,$index总是为0,所以这些完全不起作用。我已将它们从下面的视图中删除
  • 保持属性名称的一致性,例如,当视图模型中的属性实际上被称为code时,在视图模型中调用属性codeop是不必要的,也是令人困惑的
  • 使视图模型构造函数采用一个data参数,而不是3个或多个单独的参数,也有助于保持代码的整洁
  • 您不需要jQuery来执行循环。JavaScript本身可以很好地执行循环。我已经删除了所有jQuery引用
  • 在现代JS中,您不再真正需要var self = this;。当您在视图模型中使用箭头函数时,this将在方法和辅助函数中保留其含义

比较当你在应该做的地方做事情时,视图模型和视图变得有多容易(我无法重现你所说的foreach:问题(:

ko.bindingHandlers.currency = {
// display numbers with 2 decimal digits, comma and parentheses if negative
update: (element, valueAccessor) => {
const value = ko.unwrap(valueAccessor());
const formatted = Math.abs(value).toFixed(2).replace('.', ',');
element.textContent = value < 0 ? '(' + formatted + ')' : formatted;
}
};
// helper to calculate a grand total across array items
const sumProperty = (array, prop) => array.reduce((total, item) => total + ko.unwrap(item[prop]), 0);
function MainModel(data) {
this.departments = ko.observableArray(data.departments.map(item => new Department(item)));
}
function Department(data) {
this.deptname = ko.observable(data.deptname);
this.deptcode = ko.observable(data.deptcode);
this.operasional = ko.observableArray(data.operasional.map(item => new Operasional(item)));
this.total = ko.pureComputed(() => sumProperty(this.operasional(), 'total'));
}
function Operasional(data) {
this.type = ko.observable(data.type);
this.detail = ko.observableArray(data.detail.map(item => new DetailOperasional(item)));
this.total = ko.pureComputed(() => sumProperty(this.detail(), 'total'));
}
function DetailOperasional(data) {
this.code = ko.observable(data.code);
this.coa = ko.observable(data.coa);
this.parent = ko.observable(data.parent === 'yes');
this.total = ko.observable(+data.total);
}
getData().then(data => {
ko.applyBindings(new MainModel(data));
});
function getData() {
return Promise.resolve({
"departments": [{
"deptname": "LEARNING JOURNEY",
"deptcode": "1",
"operasional": [
{
"type": "PENDAPATAN OPERASIONAL",
"detail": [
{
"coa": "PENDAPATAN UANG SEKOLAH BRUTO",
"code": "4.1.01.01",
"total": "100.00",
"parent": "no"
},
{
"coa": "POTONGAN UANG SEKOLAH",
"code": "4.1.01.91",
"total": "10.00",
"parent": "no"
},
{
"coa": "PENDAPATAN UANG SEKOLAH NETO",
"code": "4.1.01",
"total": "200.00",
"parent": "yes"
},
{
"coa": "PENDAPATAN UANG KEGIATAN BRUTO",
"code": "4.1.02.01",
"total": "220.00",
"parent": "no"
},
{
"coa": "POTONGAN UANG KEGIATAN",
"code": "4.1.02.91",
"total": "30.00",
"parent": "no"
},
{
"coa": "PENDAPATAN UANG KEGIATAN NETO",
"code": "4.1.02",
"total": "125.00",
"parent": "yes"
},
]
},
{
"type": "BEBAN OPERASIONAL",
"detail": [
{
"coa": "BEBAN REMUNERASI",
"code": "5.1.01.01",
"total": "-150.00",
"parent": "no"
},
{
"coa": "BEBAN SEKOLAH",
"code": "5.1.01",
"total": "-100.00",
"parent": "yes"
},
{
"coa": "BEBAN ADMINISTRASI KESISWAAN",
"code": "5.1.02.01",
"total": "-50.00",
"parent": "no"
},
{
"coa": "BEBAN ASURANSI SISWA",
"code": "5.1.02.02",
"total": "-25.00",
"parent": "no"
},
{
"coa": "BEBAN PENGHAPUSAN",
"code": "5.2.91",
"total": "-300.00",
"parent": "yes"
}
]
}
],
"pajak": "0.00"
}
]
});
}
.table {
empty-cells: show;
}
.top {
vertical-align: top;
}
.r {
text-align: right;
}
.b {
font-weight: bold;
}
.nowrap {
white-space: nowrap;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.2/knockout-min.js"></script>
<table width="100%">
<tbody>
<tr data-bind="foreach: departments">
<td class="top">
<table class="table table-striped nowrap" width="100%">
<thead>
<tr>
<th colspan="3"><span data-bind="text: deptname"></span></th>
</tr>
</thead>
<tbody>
<tr>
<td colspan="3">
<table class='table table-striped nowrap' data-bind="foreach: operasional" width="100%">
<tbody>
<tr class="b">
<td colspan="2"><span data-bind="text: type"></span></td>
<td></td>
</tr>
</tbody>
<tbody data-bind="foreach: detail">
<tr data-bind="css: {b: parent}">
<td data-bind="ifnot: parent"><span data-bind="text: code"></span></td>
<td><span data-bind="text: coa"></span></td>
<td class="r"><span data-bind="currency: total"></span></td>
</tr>
</tbody>
<tbody>
<tr class="b">
<td></td>
<td>JUMLAH <span data-bind="text: type"></span></td>
<td class="r"><span data-bind="currency: total"></span></td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr class="b">
<td colspan="2">SURPLUS (DEFISIT) OPERASIONAL</td>
<td class="r"><span data-bind="currency: total"></span></td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>

最新更新