Фильтрация потока Rxjs / Ngrx на основе свойства из другого потока - PullRequest
0 голосов
/ 01 мая 2018

У меня проблемы с пониманием и получением чего-то для работы с RxJS Observables и ngrx Store.

Я пробовал combineLatest, фильтры, объединение массивов и т. Д., Но, похоже, не могу найти работающее безошибочное решение.

Буду признателен за любые комментарии / отзывы о том, какие методы лучше всего подходят для достижения этого результата

Требование

  1. Возьмите 2 объекта из магазина ngrx и отфильтруйте первый объект по недвижимость во втором объекте
    • Люди - 1-й объект Список людей
    • Компании - 2-й объект Список компаний
    • PeopleFromSelectedCompanies - Фильтр 1-го объекта, чтобы показать только люди, которые соответствуют идентификаторам компании во втором объекте d. Если нет во втором объекте существуют компании, тогда я хочу показать всех людей из первого объекта
  2. Назначение PeopleFromSelectedCompanies источнику данных для углового материала DataTable
  3. Принять строковый фильтр для фильтрации PeopleFromSelectedCompanies для любых свойств, содержащих строку

Весь код ngOnInit работает нормально. Я получаю доступ ко всем необходимым спискам, и каждый раз, когда я выбираю другого клиента, запускается getSelectedCustomersPeople. Текущий код спагетти ? если вы понимаете, что я пытаюсь сделать

Компонент

ngOnInit() {
  this.peopleStore.dispatch(new fromPeopleStore.LoadPeople());

  this.people$ = this.peopleStore.select(fromPeopleStore.getAllPeople);

  this.selectedCustomers$ = this.toolbarStore
    .select(fromToolbarStore.getSelectedCustomers);

  this.selectedCustomers$.subscribe(selected => {
    this.selectedCustomersPeople$ = this.getSelectedCustomersPeople();
  });
}

getSelectedCustomersPeople(): Observable<Person[]> {
  return combineLatest(this.selectedCustomers$, this.people$, (customers, people) => {
    const allSelectedPeople = customers.map(
      customer => Object.assign(people.filter(
        person => person.company === customer.id
      ))
    );

    const flattenSelectedPeople = [].concat.apply([], allSelectedPeople);

    return flattenSelectedPeople;
  });
}

applyFilter(filterValue = ' ') {
  filterValue = filterValue.trim();
  filterValue = filterValue.toLowerCase();
  this.selectedCustomersPeople$ = filterValue;
  // Would like to filter here not sure how
}

Template

<mat-table #table [dataSource]="selectedCustomersPeople$ | async"
  matSort
  [@animateStagger]="{ value: '50' }">
  <!-- Name Column -->
  <ng-container cdkColumnDef="firstName">
    <mat-header-cell *cdkHeaderCellDef mat-sort-header>First Name</mat-header-cell>
    <mat-cell *cdkCellDef="let person">
      <p class="text-truncate font-weight-600">
        {{ person.firstName }} {{ person.familyName }}
      </p>
    </mat-cell>
  </ng-container>

  <mat-header-row *cdkHeaderRowDef="displayedColumns"></mat-header-row>
  <mat-row *cdkRowDef="let person; columns: displayedColumns;"
    class="person"
    (click)="editPerson(person)"
    [ngClass]="{'mat-light-blue-50-bg': checkboxes[person.id]}"
    matRipple
    [@animate]="{ value: '*', params: { y: '100%' } }">
  </mat-row>
</mat-table>

Ответы [ 2 ]

0 голосов
/ 02 мая 2018

Я сделал базовый пример того, как сделать это немного чище, работая с потоками rx.

Я сделал простой автономный пример, в котором я создал два источника (customer $ и companies $), которые будут излучать все 0,8 - 1 секунды. Это было так, чтобы я мог проверить это на месте. Вы должны быть в состоянии сделать то же самое, используя people $ и selectedCompanies $ observables.

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/observable/interval';
import 'rxjs/add/observable/combineLatest';

interface Customer {
    id: number;
    company: number;
}

interface Company {
    id: number;
}

export function test() {
    // Emits a customer list all 1000 milliseconds
    const customers$: Observable<Customer[]> = Observable
        .interval(1000)
        .map(() => <Customer[]>[{ id: 1, company: 1}, {id: 2, company: 1}, {id: 3, company: 5}]);
    // Emits a company list all 800 milliseconds
    const companies$: Observable<Customer[]> = Observable
        .interval(800)
        .map(() => <Customer[]>[{ id: 1, company: 1}, {id: 2, company: 1}]);

    // Create a stream that combines the latest value of both lists
    const both$ = Observable.combineLatest(customers$, companies$);

    // Create a new observable which will emit the filtered customers
    const filteredCustomers$ = both$.map((data: any) => {
        const customers: Customer[] = data[0];
        const companies: Company[] = data[1];
        // We did not receive any companies, so we can return the full customer list
        if (!companies || companies.length === 0) {
            return customers;
        }

        // Create an object that will have keys of existing companies as id
        const companyIds = {};
        companies.forEach(c => companyIds[c.id] = true);

        // Return the filtered version of the customers
        return customers
            // In the filter statement, we check if the company id exists
            // in our helper object. You could leave out the "=== true" part.
            .filter(customer => companyIds[customer.company] === true);
    });

    // Now we have a filteredCustomers$ observable where we can subscribe:
    filteredCustomers$.subscribe(customers => console.log('Filtered Customers: ', customers));
}
0 голосов
/ 01 мая 2018

В случае, если кто-то еще пытается сделать что-то подобное, я получил рабочее решение. Обновленный код ниже, возможно, не лучший или самый чистый способ сделать это, но он работает хорошо:

constructor(
    private peopleStore: Store<fromPeopleStore.PeopleState>,
    private toolbarStore: Store<fromToolbarStore.CustomerState>,
    public dialog: MatDialog
  ) {
    this.peopleStore.dispatch(new fromPeopleStore.LoadPeople());
    this.people$ = this.peopleStore.select(fromPeopleStore.getAllPeople);
    this.selectedCustomers$ = this.toolbarStore.select(fromToolbarStore.getSelectedCustomers);
  }

  ngOnInit() {
    this.selectedCustomers$.subscribe(selected => {
      if (selected.length !== 0) {
        this.selectedCustomersPeople$ = this.getSelectedCustomersPeople();
        this.selectedCustomersPeople$.subscribe(people => {
          this.dataSource = new MatTableDataSource(people);
          this.dataSource.paginator = this.paginator;
          this.dataSource.sort = this.sort;
        });
      } else {
        this.people$.subscribe(people => {
          this.dataSource = new MatTableDataSource(people);
          this.dataSource.paginator = this.paginator;
          this.dataSource.sort = this.sort;
        });
      }
    });
  }

  getSelectedCustomersPeople(): Observable<Person[]> {
    return combineLatest(this.selectedCustomers$, this.people$, (customers, people) => {
      const selectedCustomersPeople = customers.map(customer =>
        Object.assign(people.filter(person => person.companyId === customer.id))
      );
      const flattenSelectedPeople = [].concat.apply([], selectedCustomersPeople);

      return flattenSelectedPeople;
    });
  }

  applyFilter(filterValue = ' ') {
    filterValue = filterValue.trim();
    filterValue = filterValue.toLowerCase();
    this.dataSource.filter = filterValue;
  }

<mat-table #table [dataSource]="dataSource" matSort [@animateStagger]="{value:'50'}">

    <!-- Name Column -->
    <ng-container cdkColumnDef="firstName">
        <mat-header-cell *cdkHeaderCellDef mat-sort-header>First Name</mat-header-cell>
        <mat-cell *cdkCellDef="let person">
            <p class="text-truncate font-weight-600">{{person.firstName}} {{person.familyName}}</p>
        </mat-cell>
    </ng-container>

    <mat-header-row *cdkHeaderRowDef="displayedColumns"></mat-header-row>
    <mat-row *cdkRowDef="let person; columns: displayedColumns;" class="person" (click)="editPerson(person)" [ngClass]="{'mat-light-blue-50-bg':checkboxes[person.id]}"
        matRipple [@animate]="{value:'*',params:{y:'100%'}}">
    </mat-row>
</mat-table>
...