角键值管道排序属性/按顺序迭代

当使用角度 键值管道迭代一个对象的属性时,如下所示:

<div *ngFor="let item of object | keyvalue">
{{item.key}}:{{item.value}}
</div>

我曾经遇到过这样一个问题: 属性没有按照预期的顺序迭代。这一评论表明,我不是唯一一个遇到这个问题的人:

如何循环对象属性与 ngFor 在角度

当使用键值管道时,是什么决定了迭代的顺序? 如何强制执行特定的迭代顺序?我的理想迭代顺序是添加属性的顺序。

谢谢

98128 次浏览

是的,Object属性是随机迭代的,因为它不会以 array的形式存储在内存中。

如果希望按插入位置进行迭代,则需要创建一个额外的属性(如 index) ,并设置 timestamp并按 index进行排序。

下面是您可以用来控制排序和迭代的管道

烟斗

import {Pipe, PipeTransform} from 'angular2/core';


@Pipe({name: 'values'})
export class ValuesPipe implements PipeTransform {
transform(value: any, args?: any[]): Object[] {
let keyArr: any[] = Object.keys(value),
dataArr = [],
keyName = args[0];


keyArr.forEach((key: any) => {
value[key][keyName] = key;
dataArr.push(value[key])
});


if(args[1]) {
dataArr.sort((a: Object, b: Object): number => {
return a[keyName] > b[keyName] ? 1 : -1;
});
}


return dataArr;
}
}

用法

<div *ngFor='#item in object | values:"keyName":true'>...</div>

根据角度 文件keyvalue管道排序的项目按 key顺序默认。您可以提供一个比较器函数来更改该顺序,并根据 keyvalue或对象中属性的输入顺序对项进行排序。

下面的比较器函数按不同顺序对项进行排序:

// Preserve original property order
originalOrder = (a: KeyValue<number,string>, b: KeyValue<number,string>): number => {
return 0;
}


// Order by ascending property value
valueAscOrder = (a: KeyValue<number,string>, b: KeyValue<number,string>): number => {
return a.value.localeCompare(b.value);
}


// Order by descending property key
keyDescOrder = (a: KeyValue<number,string>, b: KeyValue<number,string>): number => {
return a.key > b.key ? -1 : (b.key > a.key ? 1 : 0);
}

当应用于 keyvalue管道时:

<div *ngFor="let item of object | keyvalue: originalOrder">
\{\{item.key}} : \{\{item.value}}
</div>


<div *ngFor="let item of object | keyvalue: valueAscOrder">
\{\{item.key}} : \{\{item.value}}
</div>


<div *ngFor="let item of object | keyvalue: keyDescOrder">
\{\{item.key}} : \{\{item.value}}
</div>

有关演示,请参见 这一堆闪电战


提供一个常量或者 null而不是一个有效的比较器函数可以保留对象属性的入口顺序,就像 originalOrder一样,但是它会导致一个异常(参见 这一堆闪电战) :

<!-- The following syntaxes preserve the original property order -->
<div *ngFor="let item of object | keyvalue: 0">
<div *ngFor="let item of object | keyvalue: 374">
<div *ngFor="let item of object | keyvalue: null">


<!-- but they cause an exception (visible in the console) -->
ERROR TypeError: The comparison function must be either a function or undefined

此外,在模板中两次使用该语法根本不会显示项。因此,我不建议这样做。请注意,提供 undefined作为比较器函数不会导致任何异常,但不会改变默认行为: 项目按 key值排序。

这与公认的答案相同,但它有更复杂的对象,所以它可以帮助人们如何排序自定义索引字段和使用键盘管道。

角度分量:

myObject = {
"key1": { val:"whateverVal1", code:"whateverCode1", index: 1},
"key2": { val:"whateverVal2", code:"whateverCode2", index: 0},
"key3": { val:"whateverVal3", code:"whateverCode3", index: 2}
}

组件中的排序功能:

indexOrderAsc = (akv: KeyValue<string, any>, bkv: KeyValue<string, any>): number => {
const a = akv.value.index;
const b = bkv.value.index;


return a > b ? 1 : (b > a ? -1 : 0);
};

模板:

<div *ngFor="let x of myObject | keyvalue:indexOrderAsc">
...
</div>

保持可以使用的对象顺序

keepOrder = (a, b) => {
return a;
}

假设你有

wbs = {
"z": 123,
"y": 456,
"x": 789,
"w": 0#*
}

如果你使用 keyvalue,你会按键字母顺序

w 0#*
x 789
y 456
z 123

你保持对象的顺序

<ion-item *ngFor="let w of wbs | keyvalue: keepOrder">
<ion-label>\{\{ w.key }}</ion-label>
<ion-label>\{\{ w.value }}</ion-label>
</ion-item>
<div *ngFor="let item of object | keyvalue: 0">
\{\{item.key}} : \{\{item.value}}
</div>

直接写入,您将获得与 json 中相同顺序的数据

keyvalue: 0

尝试下面的代码:

<div *ngFor="let item of object | keyvalue: 0">
\{\{item.key}} : \{\{item.value}}
</div>

创建一个管道,将默认值设置为 unorder:

// src/app/pipes/new-key-value.pipe.ts
import { Pipe, PipeTransform } from '@angular/core';
import { KeyValuePipe } from '@angular/common';
import { KeyValueDiffers } from '@angular/core';
const unordered = (a,b)=>0


@Pipe({
name: 'newkeyvalue'
})


// This pipe uses the angular keyvalue pipe. but defaults to unordered.
export class NewKeyValuePipe implements PipeTransform {
  

constructor(public differs: KeyValueDiffers){};
public transform (value, compareFn = unordered){
let pipe =  new KeyValuePipe(this.differs);
return pipe.transform(value, compareFn)
}
}

连结:

我能用管子叫 Angular2管子吗?

Https://github.com/angular/angular/blob/8.2.14/packages/common/src/pipes/keyvalue_pipe.ts#l25-l89

模板使用方法:

// src/app/some-component/some-component.html
<div *ngFor="let x of myObject | newkeyvalue>
</div>

在模块中注册

// src/app/app.module.ts
import { NewKeyValuePipe } from '@/pipes/new-key-value.pipe.ts'
...
@NgModule({
declarations: [
...
NewKeyValuePipe,
...
]
...
})

是的,默认情况下,键值对按升序排序数据。

使其保持未排序修改为:

keyvalue: 0

最终代码:

<div *ngFor="let item of object | keyvalue:0">
\{\{item.key}}:\{\{item.value}}
</div>

但是对于角度7或更高,你需要放一个空函数来保持 未分类的数据。

<div *ngFor="let item of object | keyvalue: unsorted">
\{\{item.key}}:\{\{item.value}}
</div>

.ts文件中只需放置这个空函数。

  unsorted() { }

希望能有帮助。

我认为这是一个更干净的解决方案。取消注释返回声明,需要给出正确的顺序:

interface KeyValue<K, V> {
key: K,
value: V
}


// Order by descending property key
keyOrder = (aA: KeyValue<string,any>, bB: KeyValue<string,any>): number => {
const a = aA.value.index;
const b = bB.value.index;
//return a > b ? 1 : (b > a ? -1 : 0); // ASCENDING
return a > b ? -1 : (b > a ? 1 : 0); // DESCENDING
}

用例

<div *ngFor="let x of y | keyvalue: keyOrder;">
\{\{ x.key  }} indexes: \{\{ x.value.index }}
</div>

我更喜欢通过在管道中扩展它来覆盖默认的 keyvalue行为。然后在模板中使用管道而不是 keyvalue

import {Pipe} from '@angular/core';
import {KeyValuePipe} from '@angular/common';


const keepOrder = (a, b) => a;


// This pipe uses the angular keyvalue pipe but doesn't change order.
@Pipe({
name: 'defaultOrderKeyvalue'
})
export class DefaultOrderKeyvaluePipe extends KeyValuePipe {


public transform(value, compareFn = keepOrder) {
return super.transform(value, compareFn);
}


}

角度13。

<div *ngFor="let item of object | keyvalue: 0">
\{\{item.key}} : \{\{item.value}}
</div>


keyvalu: 0

如果您不想对现有数组对象执行任何类型的操作,则空的未排序方法正在工作。

<div *ngFor="let item of object | keyvalue: unsorted">
\{\{item.key}}:\{\{item.value}}
</div>

在.ts 文件中只需放置这个空函数。

  unsorted() { }