Using @input Observable in the view but the pipe function in the component is ignored

Viewed 731

In my component i'm using an @Input that is type of Observable. In the view I'm using the | async pipe to handle the subscribe and unsubscribe of the Observable stream. In the component I'm trying to pipe this Observable to perform some mutation on the stream. However the map function inside the pipe never gets triggered.

data.component.ts:

@Component({
 selector: 'data',
 template: `<div *ngFor="let item of (data$ | async)">{{item}}</div>`
})
export class DataComponent implements OnInit {
  @Input()
  data$: Observable<IData>;

  constructor() {}

  ngOnInit() {
    this.data$
      .pipe(
        map(next => {
          // do some data mutation
          // never getting in here
        })
      )
  }
}

Right now the map function inside the pipe never gets triggered and thus does not mutate the data as desired. The desired outcome would be that the data mutation happens inside the map function.

2 Answers

You need to subscribe to this.data$ inside the component.

this.data$.subscribe((data) => {
   //do data mutation
   // assign it to a local variable, say this.data
})

Along with it, if you want the mutation to be used in template, you can also assign the mutated data to a local variable and use that in the template.

Here's how it would look:

@Component({
 selector: 'data',
 template: `<div *ngFor="let item of mutatedData">{{item}}</div>`
})
export class DataComponent implements OnInit {
  @Input()
  data$: Observable<IData>;
  mutatedData: IData;

  constructor() {}

  ngOnInit() {
    this.data$.subscribe((data) => {
       //do mutatation
       //assign mutated data to this.mutatedData
   });
  }
}

Update: As OP intends to avoid subscribe in the component, here's an idea: Note: you can also write a custom pipe to do this, but for now, consider this:

@Component({
  selector: 'data',
  template: `<div *ngFor="let item of mutatedData">{{mutate(item)}}</div>`
 })
 export class DataComponent implements OnInit {
   @Input()
   data$: Observable<IData>;

   mutate(data: any): any{
     //data mutation
     //return updated data
   }
 }

To avoid subscribing and unsubscribing on hot observables the following approach can be used:

@Component({
 selector: 'data',
 template: `<div *ngFor="let item of (dataMutate$ | async)">{{item}}</div>`
})
export class DataComponent implements OnInit {
  @Input()
  data$: Observable<IData>;

  dataMutate$: Observable<IData>;

  constructor() {}

  ngOnInit() {
    this.dataMutate$ = this.data$
      .pipe(
        map(next => {
          // do some data mutation
          // never getting in here
        })
      )
  }
}

This avoids unsubscribing in the component and calling any mutating function that will be evaluated on every change detection.

Related