web-dev-qa-db-fra.com

Angular2 rxjs sort la liste (observable) d'objets par un champ observable

Je veux trier une liste de choses en fonction d'un champ observable, mais je ne peux pas comprendre les observables pour que cela fonctionne. Quelqu'un a-t-il une idée de comment y parvenir?

La situation initiale ressemble à ce qui suit:

Thing[] things;

interface Thing {
  name: Observable<string>
}
<ul>
  <li *ngFor="const thing for things">
    {{thing.name | async}}
  </li>
</ul>

De toute évidence, je n'ai pas bien décrit mon problème: le champ sur lequel je souhaite trier la liste des éléments est un observable, pas une chaîne simple. Je souhaite que le champ reste à jour via Websockets afin de pouvoir détecter correctement les modifications, je dois utiliser un champ Observable sur lequel je peux m'abonner.

7
sclausen

Merci d'avoir clarifié la question, Phosphoros. :)

Voici comment vous pouvez faire ce que vous avez demandé:

// Function to compare two objects by comparing their `unwrappedName` property.
const compareFn = (a, b) => {
  if (a.unwrappedName < b.unwrappedName)
    return -1;
  if (a.unwrappedName > b.unwrappedName)
    return 1;
  return 0;
};

// Array of Thing objects wrapped in an observable.
// NB. The `thing.name` property is itself an observable.
const thingsObs = Observable.from([
  { id: 1, name: Observable.of('foo') },
  { id: 2, name: Observable.of('bar') },
  { id: 3, name: Observable.of('jazz') }
]);

// Now transform and subscribe to the observable.
thingsObs

  // Unwrap `thing.name` for each object and store it under `thing.unwrappedName`.
  .mergeMap(thing =>
    thing.name.map(unwrappedName => Object.assign(thing, {unwrappedName: unwrappedName}))
  )

  // Gather all things in a SINGLE array to sort them.
  .toArray()

  // Sort the array of things by `unwrappedName`.
  .map(things => things.sort(compareFn))

  .subscribe();

La journalisation des valeurs émises sur la console affiche un tableau d'objets Thing triés par leur propriété unwrappedName:

[
  { id: 2, name: ScalarObservable, unwrappedName: "bar" },
  { id: 1, name: ScalarObservable, unwrappedName: "foo" },
  { id: 3, name: ScalarObservable, unwrappedName: "jazz" }
]

S'il vous plaît laissez-moi savoir si vous avez des questions sur ce code.

13
AngularChef

Si je vous comprends bien, vous voulez un objet qui ressemble à ceci:

Thing {
   name: string;
}

Vous devez ensuite avoir un observable contenant un tableau de Thing:

things$: Observable<Thing[]>;

Vous voulez ensuite trier vos objets dans le thing array par une propriété, dans ce cas name. Cela pourrait être fait comme ça:

...

let sorted$: Observable<Thing[]> = things$.map(items => items.sort(this.sortByName))

...

sortByName(a,b) {
  if (a.name < b.name)
    return -1;
  if (a.name > b.name)
    return 1;
  return 0;
}

...

Et enfin, comme Toung Le a montré dans sa réponse, changez votre modèle comme ceci:

<ul>
  <li *ngFor="let thing of sorted$ | async">
    {{thing.name}} <!--No need async pipe here. -->
  </li>
</ul>
6
Fredrik Lundin

Vous pouvez utiliser Observable.map. Par exemple:

Observable<Thing[]> things;
sortedThings$ = things.map(items => items.sort()) // Use your own sort function here.

Dans votre modèle:

<ul>
  <li *ngFor="let thing of sortedThings$ | async">
    {{thing.name}} <!--No need async pipe here. -->
  </li>
</ul>
3
Tuong Le

Vous pouvez utiliser Observable.map puis sort() avec localeCompare qui ressemblerait à ceci:

.map(data => ({
        label: data.name
}))
.sort((a, b) => a.label.localeCompare(b.label));
1
Laurie Clark

Utilisez l'opérateur groupby (jouez avec lui ):

const $things = getThings();

$things.pipe(
    groupBy(thing => thing.id),
    mergeMap(group$ => group$.pipe(
        reduce((acc, cur) =>[...acc, cur], [])
    ))
)
.subscribe(console.log)

Groupby docs .

0
Luillyfe