【问题标题】:Rxjs/Ngrx Filtering Stream based on a property from another stream基于来自另一个流的属性的 Rxjs/Ngrx 过滤流
【发布时间】:2018-10-11 06:37:06
【问题描述】:

我在理解和使用 RxJS Observables 和 ngrx Store 时遇到问题。

我尝试了combineLatest、过滤器、连接数组等,但似乎无法找到一个有效的无错误解决方案。

我将不胜感激任何有关哪种技术最适合实现此结果的 cmets / 反馈

要求

  1. 从 ngrx 存储中获取 2 个对象,并通过 a 过滤第一个对象 第二个对象中的属性
    • 人物 - 人物的第一个对象列表
    • 公司 - 第二个目标公司列表
    • PeopleFromSelectedCompanies - 过滤第一个对象以仅显示 在第二个对象 d 中与公司 ID 匹配的人。如果不 公司存在于第 2 个对象中,然后我想显示第 01 个对象中的所有人
  2. 将 PeopleFromSelectedCompanies 分配给 Angular 材料数据表的数据源
  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
}

模板

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

【问题讨论】:

    标签: angular rxjs ngrx


    【解决方案1】:

    如果其他人试图做类似的事情,我有一个可行的解决方案。下面更新了代码,可能不是最好或最干净的方法,但效果很好:

    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>
    

    【讨论】:

      【解决方案2】:

      我做了一个基本示例,说明如何通过使用 rx 流使其更简洁。

      我做了一个基本的独立示例,其中我创建了两个源(customers$ 和 Companies$),它们将在 0.8 - 1 秒内全部发出。这只是为了让我可以在本地进行测试。您应该能够使用 people$ 和 selectedCompanies$ 可观察对象来做同样的事情。

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

      【讨论】:

        猜你喜欢
        • 2022-11-03
        • 1970-01-01
        • 1970-01-01
        • 2017-02-17
        • 1970-01-01
        • 2019-10-05
        • 2016-07-14
        相关资源
        最近更新 更多