作者: Jim Wang 公众号: 巴博萨船长

摘要:本文主要介绍如何使用Angular三个常用的内置结构型指令 —— NgIf、NgFor和NgSwitch之一ngFor历遍一个json对象。Ngfor历遍列表很简单,ngfor历遍json实例又该如何操作呢?

Abstract: This article mainly introduces how to in Angular use ngFor, one of Three of the common, built-in structural directives—NgIf, NgFor, and NgSwitch, to traverse a json object. Ngfor traverses the array is very simple, how should ngfor traverse the json instance?

作者: Jim Wang 公众号: 巴博萨船长

概述

NgFor 会需要一个循环变量和一个列表。通过Pipe, PipeTransform来完成。也就是说ngfor主要是历遍列表的。一般情况下,遇到此类问题,首先想到的就是获取所有json对象中的键名,并定义一个类的公共属性,并将列表传入该属性,就可以在前端html代码中使用ngFor历遍键名并根据键值访问json对象,从而完成json对象的历遍。那有没有更加简单与直接的方法呢?本文就讨论一下我在项目中遇到类似的问题的解决方法。

方法一,管道Pipe

管道的方法也是Angular官方比较推荐的,也是遇到类似问题应该想到并能够使用解决问题的方法。管道可以理解为一些简单的函数,其可以在模板表达式中用来接受输入值并返回一个转换后的值。管道的导入和实现代码如下。管道的类使用@Pipe装饰器。在自定义管道类中实现 PipeTransform 接口来执行转换。Angular 调用 transform 方法,该方法使用绑定的值作为第一个参数,即下方代码中的value,把其它任何参数(如下方代码中的args)都以列表的形式作为第二个参数,并返回转换后的值。通过管道中的数据绑定来检测变更,你可以通过带有管道的数据绑定来显示值并响应用户操作。如果是原始类型的输入值,比如 StringNumber ,或者是对象引用型的输入值,比如 DateArray ,那么每当 Angular 检测到输入值或引用有变化时,就会执行该输入管道。

1
2
3
4
5
6
7
8
9
10
11
12
import { Pipe, PipeTransform } from '@angular/core';

@Pipe({name: 'keys'})
export class KeysPipe implements PipeTransform {
transform(value, args:string[]) : any {
let keys = [];
for (let key in value) {
keys.push({key: key, value: value[key]});
}
return keys;
}
}

前端HTML模版,使用管道的示例代码如下,

1
2
3
<span *ngFor="#entry of content | keys">           
Key: {{entry.key}}, value: {{entry.value}}
</span>

由名为keys的管道转换得来的值,每个元素entry,都包含key和value。使用管道的还有另一个好处,在使用管道的时候除了穿入需要转换的值以外还可以穿入其他的参数,书写方式如下。下方模版中param即为参数,在上面管道定义的代码中可以通过args[0]获取该参数。进而在转换json对象时,可以根据穿入的参数进行诸如“修改”,“过滤”,“添加”与“删除”等操作。

1
2
3
<span *ngFor="#entry of content | keys:'param'">           
Key: {{entry.key}}, value: {{entry.value}}
</span>

在Angular中,管道Pipe分为 pure pipe (纯管道) 和 impure pipe (非纯管道)。纯管道和非纯管道是相对于管道所传的参数(如上例的 param)而言的。如果管道是纯管道,则管道的触发只会针对基本类型的参数的变化或者引用类型引用的变化( a primitive input value (String, Number, Boolean, Symbol) or a changed object reference (Date, Array, Function, Object));然而, 对于非纯管道,不管是基本类型参数的改变还是引用类型内部数据变化(而非引用变化)都可以触发管道。管道时纯管道还是非纯管道的设置时通过把 pure 标志设置来实现的。当为 false时就可以来把管道设置成非纯。

1
2
3
4
@Pipe({
name: 'keys',
pure: true // default
})

本文示例使用的默认值,即纯管道。

方法二,使用Object.keys方法

JSON 作为存储和传输数据的一种格式。本身是没有类似于Python中Dictionary数据类型的诸如keys()和values()的内置方法的。ES5中内置了一个名为Object.keys的方法,用于返回一个对象的属性名称数组,其使用方法如下。

1
2
3
4
5
6
7
const object1 = {
a: 'somestring',
b: 42,
c: false
};

console.log(Object.keys(object1));

虽然有了获取json对象属性(键)名称数组的方法。但是在Angular的HTML模版中是不能直接使用该方法是非法的。既然如此就可以换个思路,由于在模版中,使用组件的公共属性是合法的,那么就可以先定义一个组件的公共属性,并将方法Object.keys以别名的方法赋值至该公共属性。具体代码实现如下。代码中objectKeys = Object.keys;就是为了完成上述操作的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import { Component, ElementRef, OnInit } from '@angular/core';

@Component({
selector: 'app-demo',
templateUrl: './demo.component.html',
styleUrls: ['./demo.component.scss']
})
export class DemoComponent implements OnInit {

public objectKeys = Object.keys;
public datas = {
'Block1': {
'data1' : "Block 1 data 1",
'data1-pop': .035,
'data2' : "Block 1 data 1",
'data2-pop': .2,
'data3' : "Block 1 data 1",
'data3-pop': .35,
'data4' : "Block 1 data 1",
'data4-pop': .5
},
'Block2': {
...
}
};

constructor() { }

ngOnInit(): void {

}
}

前端模版的书写方式如下。

1
2
3
4
5
6
7
8
9
<ng-container *ngFor="let itemkey of objectKeys(datas[entry])" >
<div *ngIf="!itemkey.includes('pop')" fxLayout="column" class="signleitem" fxFlex=50>
<label>{{itemkey}}</label>
<div fxLayout="row" fxLayoutAlign="start none" class="item-content" fxFill>
<input placeholder="" [(ngModel)]=datas[entry][itemkey]>
<span>{{ (datas[entry][itemkey+'-pop'] * 100) | number:'1.0-0'}} %</span>
</div>
</div>
</ng-container>

与方法一不同的是,此时历遍的数组中的元素仅为json对象的键名。此种方法也不能在历遍完成对键值的过滤。但在一些应用场景下,该方法要比方法一更加直接和高效。

小结

两种方法都能解决问题,在使用中可以根据实际需要选的最合适的自己项目的方法。文中内容就是对这个两种方法进行简单对比。Angular相关内容为自己新学知识,文章有不足之处也实属正常。见解不同,诚切赐教,关注微信公众号,一起讨论学习。


版权声明:
文章首发于 Jim Wang's blog , 转载文章请务必以超链接形式标明文章出处,作者信息及本版权声明。