4
votes

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
You have to subscribe, otherwise the you won't receive any items.martin
The stream is already subscribe in the view. If I subscribe in there i'm mutating a different instance. This will have no effect on the view.Nephi
In your view you subscribe to data$? pipe() method doesn't modify the chain "in-place" so you have to assign it to a different variable and then use | async on that.martin
You mean something like this? this.mutatedData$ = this.data$ and then this.mutatedData$.pipe(map()). In the view do this.mutatedData$ | async? Because that seems not to work either.Nephi

2 Answers

2
votes

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
   }
 }
2
votes

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.