How to filter an observable passed into an angular component?

时间:2019-01-18 18:55:35

标签: javascript angular rxjs ngrx

I'm new to RxJs operators and Observables in general and was not sure how to get this simple code to work.

I have created a component called 'family-list' that is bound to an observable of 'PlanMember[]'. This is passed in from a container component.

Inside of this component, I want to be able to filter out the plan members so that we leave out the member with userType 'Owner'. The code i have now fails because 'planMembers' is undefined.

Please help!

@Component({
  selector: 'pv-family-list',
  templateUrl: './family-list.component.html',
  styleUrls: ['./family-list.component.scss']
})
export class FamilyListComponent{
  @Input() planMembers: Observable<PlanMember[]>;
  @Output() remove: EventEmitter<string> = new EventEmitter<string>();

  columnsToDisplay = ['name', 'email', 'status', 'cycleEnd', 'actions'];

  constructor() { 
    this.planMembers.pipe(map( (members) =>  members.filter(m => m.userId !== 'Owner') )).subscribe();
  }
  removeUser (userId : string) {
    this.remove.emit(userId);
  }
}

EDIT: After using ngOnit to filter -- My page will load the component, but still getting errors. I'm also not even getting the member-list to filter properly -- still have all the members

So I have a "Subscription.container" component that holds this family-list component. I retrieve the list of family members from the store. this.familyPlanMembers$ = store.select(fromAccount.getPlanMembers); Then, i bind my family-list planMembers with an Async Pipe: <pv-family-list (remove)="cancelMemberSubscription($event)" [planMembers]="(familyPlanMembers$ | async)" > </pv-family-list> While the family-list loads, I'm getting this: TypeError: "this.planMembers is null"

Component Now:


@Component({
  selector: 'pv-family-list',
  templateUrl: './family-list.component.html',
  styleUrls: ['./family-list.component.scss']
})
export class FamilyListComponent implements OnChanges{
  @Input() planMembers: PlanMember[];
  @Output() remove: EventEmitter<string> = new EventEmitter<string>();

  columnsToDisplay = ['name', 'email', 'status', 'cycleEnd', 'actions'];

  ngOnChanges() { 
    this.planMembers = this.planMembers.filter(m => m.userType !== 'Owner');
  }

  removeUser (userId : string) {
    this.remove.emit(userId);
  }

3 个答案:

答案 0 :(得分:0)

It is undefined because it is not initialized. You should do filtering in ngOnInit and ngOnChanges hook or use set method

_planMembers:  Observable<PlanMember[]>;

@Input
set planMembers(planMembers: Observable<PlanMember[]]) {
    this._planMembers = planMembers;
    this._planMembers
        .pipe(map( (members) =>  members.filter(m => m.userId !== 'Owner') 
    )).subscribe();

}

This will be invoked every time when planMembers input would change.

答案 1 :(得分:0)

At constructor time, your @Input is not defined yet.

Move your code in ngOnInit:

@Component({
  selector: 'pv-family-list',
  templateUrl: './family-list.component.html',
  styleUrls: ['./family-list.component.scss']
})
export class FamilyListComponent implements OnInit{
  @Input() planMembers: Observable<PlanMember[]>;
  @Output() remove: EventEmitter<string> = new EventEmitter<string>();

  columnsToDisplay = ['name', 'email', 'status', 'cycleEnd', 'actions'];

  ngOnInit() { 
    this.planMembers.pipe(map( (members) =>  members.filter(m => m.userId !== 'Owner') )).subscribe();
  }
  removeUser (userId : string) {
    this.remove.emit(userId);
  }
}

Then it also depends how do you pass this input through this component, and how do you fetch the data, otherwise you should implement ngOnChanges rather than ngOnInit, or use a set on the @Input or a lot of other approaches are also possible

答案 2 :(得分:0)

Like the other answers already mentioned, the Observable isn't yet defined in the constructor. A constructor should only be used for DI or assignments imho.

I would suggest not to pass the observable as an input to your child components but to pass it as "just" an array. This will make your application faster, easier to work with and easier to test. To pass it as an array, use the async pipe.

<pv-family-list [planMembers]="planMembers$ | async"><pv-family-list/>