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

angular keyvalue pipe sort properties / iterate in order

当使用 Angular keyvalue 管道迭代对象的属性时,如下所示:

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

我遇到了属性未按预期顺序迭代的问题。此评论表明我不是唯一遇到此问题的人:

How to loop over object properties with ngFor in Angular

有人可以告诉我在使用键值管道时决定迭代顺序的因素以及如何强制执行特定的迭代顺序吗?我理想的迭代顺序是添加属性的顺序。

谢谢

是的,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>

根据 Angular documentationkeyvalue 管道默认按 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>

有关演示,请参阅 this stackblitz


提供常量或 null 而不是有效的比较器函数可以保留对象属性的输入顺序,就像 originalOrder 那样,但它会导致异常(参见 this stackblitz) :

<!-- 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 值排序。

这与接受的答案相同,但它有更复杂的对象,因此它可以帮助某人如何按自定义索引字段排序和使用键值管道。

在 angular 组件中:

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#*
}

如果您使用键值,您将按键的字母顺序排列

w 0#*
x 789
y 456
z 123

应用 :keepOrder 保持对象顺序

<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>

制作管道以将默认设置为无序:

// 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)
  }
}

链接:

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>

在module.ts

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

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

为了保持未排序修改为:

keyvalue: 0

Final code:

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

But for angular 7 or above, you need to put an empty function to keep data unsorted.

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

在您的 .ts 文件中,只需放入这个空函数。

  unsorted() { }

希望对您有所帮助。

我认为这是一个更简洁的解决方案。取消注释需要给出正确顺序的 return 语句:

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);
  }

}

与Angular13.

<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() { }