【问题标题】:Knockout JS foreach binding overwrite values with last index valueKnockout JS foreach 绑定用最后一个索引值覆盖值
【发布时间】:2022-01-03 00:04:38
【问题描述】:

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

这是我使用的视图。 Foreach 部门显示得很好,但 coaOperasional 和 detailOperasional 不是。

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>

【问题讨论】:

  • 抱歉,刚刚更新了
  • 你还能添加一个(最小的!)data 的样本吗?
  • 添加数据样本
  • 我已经编辑了您的问题,以便代码示例停止抛出错误。对于您的下一个问题,请开始这样。没有人可以仅仅猜测您的数据,然后想象您的问题来自哪里。实际重现您的问题的代码是 Stack Overflow 的基本要求。现在请进行最后的更改,以使代码示例真正按照您描述的方式运行。

标签: javascript html knockout.js


【解决方案1】:

您的方法中的一个根本错误是您尝试在视图模型中格式化数据,例如格式化财务价值或使事情变得粗体。

永远不要那样做。

所有的格式化都应该发生在视图中,要么通过 CSS,要么通过绑定。 创建一个custom binding handler 来格式化财务值以供显示,比在模型中像您目前所做的那样不断格式化和取消格式化数字要容易得多。

其他说明:

  • 最好在视图中根据某些条件(例如 parent 或不)显示或隐藏某些内容(例如 code 用于某个详细记录)。使用 if:ifnot: 很容易。
  • 我完全不明白您的visible: $parentContext.$index() == 0 检查的目的。你这样做的方式,$index always 是 0,所以这些什么都不做。我已将它们从下面的视图中删除。
  • 保持属性名称一致,例如在您的视图模型中调用属性 codeop 是不必要且令人困惑的,而实际上它在您的模型中称为 code
  • 制作采用单个 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>

【讨论】:

  • 谢谢你让这个变得更干净,我会尝试根据这个来改变我的——除了操作性之外,我还有 2 个具有相似属性的数据,这就是为什么我通过使用 codeop、codenop 等来区分它们,但由于它会很长,我只在这里显示其中的 1 个——关于可见的父索引,我使用它是因为我想显示像部门标题这样只显示一次的东西(如仅适用于索引 0),对不起如果它令人困惑
  • 预期结果是这样的ibb.co/dPJJhWc
  • @DewiGriselda 我的结果看起来和那张图片一模一样。有没有按上面的“运行代码sn-p”查看一下?
  • 是的,我已经看过了。我在这里举例说明ibb.co/ZVH7VTK
  • 非常感谢您一直对此做出回应
猜你喜欢
  • 2014-02-21
  • 1970-01-01
  • 2012-07-07
  • 2013-10-03
  • 1970-01-01
  • 1970-01-01
  • 2020-11-17
  • 1970-01-01
  • 2010-09-29
相关资源
最近更新 更多