如何在视图中仅循环一次对象(单个 *ngFor)?
Posted
技术标签:
【中文标题】如何在视图中仅循环一次对象(单个 *ngFor)?【英文标题】:How to loop through an object only once in the view (single *ngFor)? 【发布时间】:2021-06-10 22:09:54 【问题描述】:我有这个对象,每个属性都有一个数组作为它的值。我想只循环一次这个对象,而不是使用 2 个 *ngFor 指令,因为它会导致我的应用程序运行缓慢。我也愿意用这个对象创建一个数组,但我仍然想为每个部分添加一个部分 id,如下例所示。谁能指出我正确的方向?提前非常感谢!这是我的代码。
LIVE DEMO
<div *ngFor="let group of dataObject | keyvalue">
group.key
<div *ngFor="let item of group.value; let i=index;">
item.name
</div>
</div>
注意:我的最终目标是将每个人包装到他们所属的部分中,并为每个部分添加一个 ID。 示例:
<div id = "parentId">
<div id = "section-a">
list of all users that belong in section a
</div>
<div id = "section-b">
list of all users that belong in section b
</div>
<div id = "section-c">
list of all users that belong in section c
</div>
<div>
......
......
</div>
【问题讨论】:
【参考方案1】:使用*ngFor
无法做到这一点。您可以在组件中准备数据的平面版本,并提供一个 1-depth 数组以仅循环一次。
示例应用组件:
import Component from "@angular/core";
@Component(
selector: "app-root",
templateUrl: "./app.component.html"
)
export class AppComponent
dataObject =
A: [
id: 1, name: "Mike", lastname: "asmith" ,
id: 2, name: "Mary", lastname: "alters" ,
id: 3, name: "Boris", lastname: "al"
],
B: [
id: 1, name: "Mike", lastname: "bmith" ,
id: 2, name: "Mary", lastname: "balters" ,
id: 3, name: "Boris", lastname: "bl"
],
C: [
id: 1, name: "Mike", lastname: "cmith" ,
id: 2, name: "Mary", lastname: "calters" ,
id: 3, name: "Boris", lastname: "dl"
],
D: [
id: 1, name: "Mike", lastname: "dmith" ,
id: 2, name: "Mary", lastname: "dalters" ,
id: 3, name: "Boris", lastname: "dl"
],
E: [
id: 1, name: "Mike", lastname: "emith" ,
id: 2, name: "Mary", lastname: "ealters" ,
id: 3, name: "Boris", lastname: "el"
]
;
flatDataObject = [];
constructor()
// Loop through dataObject
Object.keys(this.dataObject).forEach(k =>
// Assign key as a `key` attribute to first object in array.
this.dataObject[k][0].key = k;
// Push the arrays into one
this.flatDataObject.push(...this.dataObject[k]);
);
示例 HTML:
<div *ngFor="let group of flatDataObject">
<!-- Check if the object has `key` attribute -->
<div *ngIf="group.key">
<!-- If it has `key` attribute: -->
group.key
</div>
<div>
group.lastname
</div>
</div>
这里是演示:https://stackblitz.com/edit/angular-6-template-sk8tkm?file=src/app/app.component.html
这是我能想到的最佳解决方案。它仍然总共有两个循环,但至少其中一个没有使用*ngFor
。
【讨论】:
如何为每个部分添加一个带有 id 的 div?例如,对于“A”部分及其用户,您如何将其包装在 id =“section-a”的部分中,对于“B”部分,如何将其包装在 id =“section-b”的 div 中,等等开吗? @progx 您无法使用我的解决方案为整个组分配 div id,因为它的设计目的是遍历数据集的扁平版本。但是您可以根据自己的解决方案设置 div id。示例: 我的解决方案?你的意思是回去使用 2 *ngFor 指令?我只想使用一个 *ngFor。 @progx 我回答了你的问题,而不是你的评论。当涉及到您的第二个问题时,您基本上是在创建一个 1 深度的 div 循环,例如:section-a-1, section-a-2, section-a-3, section-b-1 .... 你可以不要将它们分组并用组 ID 包装它们,因为没有父 div。如果您想这样做,请保留您的代码并使用 更改第一个 div。 【参考方案2】:您有一个 Dictionary
或 Array
s(作为数据结构),因此对于必要的迭代,您有以下时间复杂度:
O(n)
: n
代表字典的长度
O(m)
: m
代表值(Array
类型)的长度
您执行迭代的总复杂度为O(mn)
或O(m*n)
任何地方。例如,下面的代码具有相同的时间复杂度:
Object.entries(this.dataObject).reduce((result, entry) =>
const [key, value] = entry;
value.forEach((v, i) => (result[`$key_$i`] = v.name));
return result;
,);
如果你有一个限制,我认为你可以降低时间复杂度在语言方法和方便的技巧的帮助下,如下所示:
Object.values(this.dataObject).map(m => m).flat().map(m => m.name);
然后,将每个 3 项拆分:
<div *ngFor="let item of simplified2; let i = index" [id]="findId(i)">
findId(i) item
</div>
findId(index: number)
return Math.floor(index / 3); //if you are sure about the length of values
我在stackblitz 中提供了上述所有内容。你可以了解更多关于Data Structures in javascript: Arrays, HashMaps, and Lists
【讨论】:
我想要与我相同的输出(标题 - 例如“A”及其子元素下方),然后我想为每个部分添加 div id,例如“section-a”对于 A 部分,对于部分“B”,“section-b”,依此类推。你知道怎么做吗? @progx 正如我之前所描述的,您最终将拥有与您自己的代码相同的时间复杂度来满足您的目的,即O(nm)
和性能问题 基于必要的迭代。想到的唯一方法是分页技术来控制性能并按需加载请求的部分。
@progx 你想要超越计算机算法的东西!【参考方案3】:
您可以生成减少的新属性并使用此新属性
在你的组件中
dataObject =
A: [
id: 1, name: 'Mike', lastname: 'asmith',
id: 2, name: 'Mary', lastname: 'alters',
id: 3, name: 'Boris', lastname: 'al'
],
B: [
id: 1, name: 'Lauren', lastname: 'bmith',
id: 2, name: 'Travis', lastname: 'balters',
id: 3, name: 'Barn', lastname: 'bl'
],
...
dataObjectDisp = Object.entries(this.dataObject).reduce((prev, [key, value]) =>
return [...prev, ...value.map((user, index) => (
...user, parent: key, index ))]
, [])
以上代码将结构简化为
[
"id": 1,
"name": "Mike",
"lastname": "asmith",
"parent": "A",
"index": 0
,
"id": 2,
"name": "Mary",
"lastname": "alters",
"parent": "A",
"index": 1
,
...
]
现在我们有一种方法可以确定新部分何时开始。它的索引为零
要显示 html,您可以执行以下操作
<h1>New</h1>
<div *ngFor="let item of dataObjectDisp; let i = index" [attr.data-article]='"section" + item.parent'>
<ng-container *ngIf="item.index === 0">
item.parent
</ng-container>
<div>
item.lastname
</div>
</div>
注意属性[attr.data-article]='"section" + item.parent'
。这可以用来识别不同的部分
【讨论】:
您知道您的解决方案的时间复杂度是多少吗? OP 存在性能问题。 @Owen Kelvin 你能提供像 stackblitz 这样的现场演示吗? 我想用 id 将所有部分包装在一个 div 中。例如对于 A 部分,我想添加一个 id = "sectionA" 并且它的所有子元素都应该在里面。您的解决方案为每个孩子(每个人)添加了一个“”sectionA。希望这是有道理的 stackblitz.com/edit/angular-6-template-sdebr2 @Owen Kelvin 我试图坚持使用单 *ngFor,而您的新解决方案使用双 *ngFor。但是,我非常感谢您的工作和努力,所以我给您额外的 100 点声望点。谢谢老哥!以上是关于如何在视图中仅循环一次对象(单个 *ngFor)?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 1 个单个 div 中生成 *ngFor 循环项,而不是每个项目周围都有一个 div 容器