Свойства сортировки трубы углового значения ключа / итерация в порядке

При использовании канала Angular keyvalue для перебора свойств объекта следующим образом:

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

Я столкнулся с проблемой, когда свойства не повторялись в ожидаемом порядке. И этот комментарий говорит о том, что я не единственный, кто испытал эту проблему:

Как перебрать свойства объекта с помощью ngFor в Angular

Может кто-нибудь посоветовать, что определяет порядок итераций при использовании канала значения ключа, пожалуйста, и как заставить определенный порядок итераций? Мой идеальный порядок итераций - это порядок добавления свойств.

Спасибо

12 ответов

Решение

Согласно угловой документации, keyvalue труба сортирует предметы по key заказ по умолчанию. Вы можете предоставить функцию сравнения для изменения порядка сортировки, но она учитывает только key а также value свойства, а не порядок входа.

Например, следующие функции сравнения сортируют элементы в порядке возрастания значения и в порядке обратного ключа соответственно:

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

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: valueAscOrder">
  {{item.key}} : {{item.value}}
</div>

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

Смотрите этот стек для демонстрации.

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

напрямую напишите, и вы получите данные в том же порядке, что и в json

keyvalue: 0

Да, по умолчанию пара "ключ-значение" сортирует данные в порядке возрастания.

Чтобы оставить его несортированным, измените его как:

keyvalue: 0

Окончательный код:

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

Но для angular 7 или выше вам нужно поставить пустую функцию, чтобы данные оставались несортированными.

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

В твоем .ts файл просто поместите эту пустую функцию.

  unsorted() { }

Надеюсь, это поможет.

Чтобы сохранить порядок объектов вы можете использовать

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

Допустим, у вас есть

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

Если вы используете значение ключа, вы получите алфавитный порядок по ключу

w 0#*
x 789
y 456
z 123

apply:keepOrder вы сохраняете порядок объектов

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

Это то же самое, что и принятый ответ, но у него более сложный объект, поэтому он может помочь кому-то Как сортировать по пользовательскому полю индекса и использовать keyval pipe.

В угловом компоненте:

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>

Я предпочитаю отменять значение по умолчанию 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);
  }

}

Попробуйте фрагмент кода ниже:

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

Я думаю, что это более чистое решение. Раскомментируйте оператор 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>

Да, 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>

Сделайте канал, чтобы установить по умолчанию неупорядоченный:

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

Использование шаблона:

// 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,
    ...
  ]
...
})

Просто реализуйте свой собственный канал (с правильной типизацией), который принимает карту в качестве входных данных и возвращает массив в том же порядке:

      @Pipe({
  name: 'keepOrderKeyValue'
})
export class KeepOrderKeyValuePipe {

  transform<K, V>(input: ReadonlyMap<K, V>): Array<KeyValue<K, V>> {
    return Array.from(input, ([key, value]) => ({ key, value }))
  }
}

Или, если вы хотите поддерживать несколько типов, таких как Record/Map или handle undefined, вы можете реализовать это следующим образом:

      @Pipe({
  name: 'keepOrderKeyValue'
})
export class KeepOrderKeyValuePipe {

  transform<K extends keyof any, V>(input: ReadonlyMap<K, V> | Record<K, V> | undefined): Array<KeyValue<K, V>> {
    if (input instanceof Map) {
      return Array.from(input, ([key, value]) => ({ key, value }))
    }

    return input
        ? Object.entries(input).map(([key, value]) => ({ key: key as K, value }))
        : []
  }
}

Использование:

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

С Ангуляром 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() { }
Другие вопросы по тегам