【问题标题】:Angular calculations ngfor cumulative value and total via parent通过父级计算累积值和总计的角度计算
【发布时间】:2020-05-02 15:02:33
【问题描述】:

我试图在 *ngFor 循环中获取值列表的累积总数,但该值必须返回给父级,然后传递给 for 循环中的下一项,因此父级会知道总也。代码沙箱可以在:https://codesandbox.io/s/angular-test-01-n0hme?fontsize=14&hidenavigation=1&theme=dark

这些值不是在事件上计算的,而是在 ngFor 期间,没有用户交互。

  1. 标题“list no {{ count }}”的 ngFor 计数(不使用索引)

  2. ngFor 的累计总数以及 for 循环中的下一项都可以访问此值。

最好的方法是什么?

谢谢

D

文件:app.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  title = 'test-app';

  listNo = 0;
  totalPriceOfAllLists: number = 0;

  lists = [
    {
      name: "item list 1",
      items: [{ name: "item 1", value: 10 }, { name: "item 2", value: 10 }]
    },
    {
      name: "item list 2",
      items: [
        { name: "item 1", value: 10 },
        { name: "item 2", value: 10 },
        { name: "item 3", value: 10 }
      ]
    },
    {
      name: "item list 3",
      items: [
        { name: "item 1", value: 10 },
        { name: "item 2", value: 10 },
        { name: "item 3", value: 10 },
        { name: "item 4", value: 10 }
      ]
    }
  ];

}

文件:app.component.html

<h2>Test App</h2>

<app-item
  *ngFor="let list of lists; let i = index;"
  [listIndex]="i"
  [list]="list"
></app-item>

<b>Total £ {{totalPriceOfAllLists}}</b>

<hr />
<div class="">
  list no 1 cumulativeTotal should be £20<br />
  list no 1 cumulativeTotal should be £50<br />
  list no 1 cumulativeTotal should be £90<br />
  Total should be £160
</div>

文件:item.component.ts

import { Component, OnInit, Input, AfterViewChecked } from "@angular/core";

@Component({
  selector: "app-item",
  templateUrl: "./item.component.html",
  styleUrls: ["./item.component.css"]
})
export class ItemComponent implements OnInit {
  @Input() listIndex: number; // shouldn't use index as it should be a counter increment from parent
  @Input() list: { name: string; value: number }[];

  listNo = 0;
  cumaliativeTotal = 0;

  constructor() {}
  ngOnInit() {
    console.log(this.list);
    this.getListTotal();
  }
  ngAfterViewChecked() {}

  getListTotal() {
    let total: number = 0;
    this.list.items.forEach(item => {
      total += item.value;
    });
    this.listTotal = total;
  }
}

文件:item.component.html

<div class="item">
  <!-- do not use index which is available as i -->
  <h4>List no {{ listNo }}</h4>

  <div *ngFor="let item of list.items;">
    {{ item.name }} - £{{ item.value }}
  </div>

  <p><b>List total £{{ listTotal }}</b></p>
  <p><b>Cumulative total £{{ cumulativeTotal }}</b></p>
</div>

【问题讨论】:

  • 链接失效了。
  • 我已经在我没有登录的浏览器上测试了链接,并且还启用了 vpn,它工作正常,试试这个链接:codesandbox.io/s/…
  • 那是在用户交互上使用 EventEmitter,我想做的是在 for 循环本身,没有用户交互。除非每次运行孩子时我都必须手动推送自定义事件,但是不使用事件发射器不应该有更好/更好的方法吗?输出?也许?

标签: angular


【解决方案1】:

对我来说似乎更容易一次在代码中计算所有这些:

在应用组件中:

setListTotals(list) {

  const listWithTotals = list.reduce((acc, val) => {
    const listTotal = val.items.reduce((a, v) => a += v.value, 0)
    const cumulativeTotal = listTotal + acc.reduce((a, v) => a += v.cumulativeTotal, 0)
    return acc.concat([{...val, listTotal, cumulativeTotal}])
  }, [])

  return {listWithTotals, totalPrice: listWithTotals.reduce((a,v) => a += v.listTotal, 0)}
}

vm;
ngOnInit() {
  this.vm = this.setListTotals(this.lists);
}

在模板中:

<h2>Test App</h2>

<app-item
  *ngFor="let list of vm.listWithTotals; let i = index;"
  [listIndex]="i"
  [list]="list"
></app-item>

<b>Total £ {{vm.totalPrice}}</b>

和孩子:

<div class="item">
  <!-- do not use index which is available as i -->
  <h4>List no {{ listNo }}</h4>

  <div *ngFor="let item of list.items;">
    {{ item.name }} - £{{ item.value }}
  </div>

  <p><b>List total £{{ list.listTotal }}</b></p>
  <p><b>Cumulative total £{{ list.cumulativeTotal }}</b></p>
</div>

只要列表发生变化并完成,就重新运行 set list totals 函数。

分叉沙箱:https://codesandbox.io/s/angular-test-01-70lw3

【讨论】:

  • 不确定这有什么帮助,请分叉沙箱,但如果它做了我认为它做的事情,这只是传递给孩子的列表的总和,父母不知道累计总数。孩子需要计算总数(它已经做了),然后一旦计算了这个总数,它就需要将它传递给父母。 for 循环的下一次迭代使用父类的cumulativeTotal 变量添加到它计算的listTotal。冲洗并重复。
  • 我详细说明了它的工作原理。这整个孩子回到父母的废话是浪费时间和过于复杂。只需让父级运行计算来构建虚拟机并将完整的虚拟机传递给子级
  • 啊,您改变数据并将其输入 ngfor 而不是进行所有数据传递等。但是有没有办法可以在子节点中计算它并将该值传递给父节点并在每次迭代时将父级的 ngfor [cumulative]=cumulative 传递给子级?对我来说,这更多是关于孩子在迭代中处理事情和父子之间的数据同步的练习,而不是将数据突变为新的格式化对象/vo,因此孩子除了转储值外无事可做。
  • 架构很差。角度数据应该在树上向上或向下流动,而不是同时双向流动。一般来说,你应该有智能容器(或者最好是服务),它们知道如何做一些事情,比如计算或构建一个虚拟机和只显示数据的哑子。
  • 在某些情况下,孩子与父母的沟通不是更好吗,例如。假设您查询一个数据库并获得 50,000 个列表项的返回,每个列表项在其中包含 50 到 500 个项目。您是否仍然会改变 50,000 个项目的对象,或者您是否会使用子级仅在 1 次迭代节省上进行计算,并创建一个新的 50,000 个项目的对象/vo,其中包含一些额外的数据对象/值?
【解决方案2】:

希望这是你想要的。如果不让我知道。我会相应地更新答案。 https://codesandbox.io/s/angular-test-01-gxif7

既然您的父组件中有整个列表,为什么不在父组件中计算累积总数并将其传递给子组件。将名为 cumulativeTotal 的新属性添加到您的列表对象中。

那么你的列表应该如下所示。

lists = [
    {
      name: "item list 1",
      items: [{ name: "item 1", value: 10 }, { name: "item 2", value: 10 }],
      cumulativeTotal:0 
    },
    {
      name: "item list 2",
      items: [
        { name: "item 1", value: 10 },
        { name: "item 2", value: 10 },
        { name: "item 3", value: 10 }
      ],
     cumulativeTotal:0
    },
    {
      name: "item list 3",
      items: [
        { name: "item 1", value: 10 },
        { name: "item 2", value: 10 },
        { name: "item 3", value: 10 },
        { name: "item 4", value: 10 }
      ],
     cumulativeTotal:0
    }
  ];

你应该有一个新的方法来计算每个列表的累计总价和所有列表的总价格。

 calculateCumulativeTotal(list) {
    list.forEach(item => {
      item.cumulativeTotal = item.items.reduce(
        (cumulativeTotal, k) => cumulativeTotal + k.value,
        0
      );
    });

    this.totalPriceOfAllLists = list.reduce(
      (cumulativeTotal, k) => cumulativeTotal + k.cumulativeTotal,
      0
    );
  }

这是你的 app.component.html

<h2>Test App</h2>

<app-item
  *ngFor="let list of lists; let i = index;"
  [listIndex]="i"
  [list]="list"
></app-item>

<b>Total £ {{totalPriceOfAllLists}}</b>

<hr />
<div class="">
  <span *ngFor="let list of lists; let i = index;">
    list no {{i}} cumulativeTotal should be £{{list.cumulativeTotal}}<br />
  </span>
  Total should be £{{totalPriceOfAllLists}}
</div>

这是你的 app.component.ts

import { Component, Oninit } from "@angular/core";

@Component({
  selector: "app-root",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent implements Oninit {
  title = "test-app";
  lists: any = [];
  listNo = 0;
  totalPriceOfAllLists: number = 0;

  ngOnInit() {
    this.lists = [
      {
        name: "item list 1",
        items: [{ name: "item 1", value: 10 }, { name: "item 2", value: 10 }],
        cumulativeTotal: 0
      },
      {
        name: "item list 2",
        items: [
          { name: "item 1", value: 10 },
          { name: "item 2", value: 10 },
          { name: "item 3", value: 10 }
        ],
        cumulativeTotal: 0
      },
      {
        name: "item list 3",
        items: [
          { name: "item 1", value: 10 },
          { name: "item 2", value: 10 },
          { name: "item 3", value: 10 },
          { name: "item 4", value: 10 }
        ],
        cumulativeTotal: 0
      }
    ];

    this.calculateCumulativeTotal(this.lists);
  }

  calculateCumulativeTotal(list) {
    list.forEach(item => {
      item.cumulativeTotal = item.items.reduce(
        (cumulativeTotal, k) => cumulativeTotal + k.value,
        0
      );
    });

    this.totalPriceOfAllLists = list.reduce(
      (cumulativeTotal, k) => cumulativeTotal + k.cumulativeTotal,
      0
    );
  }
}

这是你的 item.component.html

<div class="item">
  <!-- do not use index which is available as i -->
  <h4>List no {{ listIndex }}</h4>

  <div *ngFor="let item of list.items;">
    {{ item.name }} - £{{ item.value }}
  </div>

  <p><b>List total £{{ listTotal }}</b></p>
  <p><b>Cumulative total £{{ list.cumulativeTotal }}</b></p>
</div>

这是你的 item.component.ts

import { Component, OnInit, Input } from "@angular/core";

@Component({
  selector: "app-item",
  templateUrl: "./item.component.html",
  styleUrls: ["./item.component.css"]
})
export class ItemComponent implements OnInit {
  @Input() listIndex: number; // shouldn't use index as it should be a counter increment from parent
  @Input() list: { name: string; value: number }[];

  ngOnInit() {
    console.log(this.list);
  }
}

【讨论】:

  • item.component.ts 中的listNocumulativeTotal 属性是干什么用的?
  • @Drenai 我们不需要这些。我已经删除了。感谢您指出。
猜你喜欢
  • 2015-09-07
  • 1970-01-01
  • 2019-06-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-12-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多