is there an equivalent of async pipe that you can use inside a component?

前端 未结 4 936
离开以前
离开以前 2021-02-13 03:52

Does there exist something equivalent to the async pipe that I could use inside a component like this

   @Component({
      selector: \'my-component         


        
相关标签:
4条回答
  • 2021-02-13 04:31

    If you're in a Component or something which has access to a ChangeDetectorRef, you can do the following little trick:

    @Component({
    ...
      providers: [AsyncPipe]
    })
    export class ExampleComponent {
      app$ = this.sandbox.allApps$;
      apps = this.pipeAsync.transform(this.app$);
    
      contructor(
        protected pipeAsync: AsyncPipe,
        protected sandbox: AppAppsSandbox
      ) {}
    }
    

    This assumes access to a service called AppAppsSandbox, but the Observable app$ can come from anywhere, this is a convenient way to unwrap it.

    0 讨论(0)
  • 2021-02-13 04:43

    No there's not. You need to manually subscribe and manually unsubscribe to avoid memory leaks.

    For a simple subscription you may be tempted to do :

    @Component({
      selector: 'my-component',
    })
    export class myComponent implements OnInit, OnDestroy {
      public myObservable$: Observable<string>;
      private myObservableSub: Subscription;
    
      ngOnInit() {
        this.myObservableSub = this
          .myObservable$
          .subscribe(_ => {
            // do something
          });
      }
    
      ngOnDestroy() {
        this.myObservableSub();
      }
    }
    

    But what if you have many subscribe ? Should you do something like :

    @Component({
      selector: 'my-component',
    })
    export class myComponent implements OnInit, OnDestroy {
      public myObservable1$: Observable<string>;
      private myObservableSub1: Subscription;
    
      public myObservable2$: Observable<string>;
      private myObservableSub2: Subscription;
    
      public myObservable3$: Observable<string>;
      private myObservableSub3: Subscription;
    
      ngOnInit() {
        this.myObservableSub1 = this
          .myObservable1$
          .subscribe(_ => {
            // do something
          });
    
        this.myObservableSub2 = this
          .myObservable2$
          .subscribe(_ => {
            // do something
          });
    
        this.myObservableSub3 = this
          .myObservable3$
          .subscribe(_ => {
            // do something
          });
      }
    
      ngOnDestroy() {
        this.myObservableSub1();
        this.myObservableSub2();
        this.myObservableSub3();
      }
    }
    

    **THE ANSWER IS NO**

    Upadted reply (16/11/20)

    In the original answer (see after this edit), I do avise to use take until but this forces you to create a subject and trigger an event into it when the component is destroyed. While the idea behind this is good, it's quite a lot of boilerplate to put in all the components where we subscribe to a stream.

    Instead of this, we can create a custom operator that we can call takeUntilDestroyed and do something like this:

    @Component({
      selector: 'my-component',
    })
    export class myComponent implements OnInit, OnDestroy {
      public myObservable1$: Observable<string>; // define your observable
    
      public myObservable2$: Observable<string>; // define your observable
    
      public myObservable3$: Observable<string>; // define your observable
    
      ngOnInit() {
        this
          .myObservable1$
          .pipe(takeUntilDestroyed(this))
          .subscribe(_ => {
            // do something
          });
    
        this.myObservableSub2 = this
          .myObservable2$
          .pipe(takeUntilDestroyed(this))
          .subscribe(_ => {
            // do something
          });
    
        this.myObservableSub3 = this
          .myObservable3$
          .pipe(takeUntilDestroyed(this))
          .subscribe(_ => {
            // do something
          });
      }
    
      ngOnDestroy() {}
    }
    

    The implementation of this custom operator can be found here: https://github.com/cloudnc/ngx-sub-form/blob/1115b21a007f72c54b521b3bed7c40051302145a/projects/ngx-sub-form/src/lib/shared/ngx-sub-form-utils.ts#L145-L148

    Original reply

    You should rather do the following :

    @Component({
      selector: 'my-component',
    })
    export class myComponent implements OnInit, OnDestroy {
      private componentDestroyed$ = new Subject<void>();
    
      public myObservable1$: Observable<string>;
      public myObservable2$: Observable<string>;
      public myObservable3$: Observable<string>;
    
      ngOnInit() {
        this
          .myObservable1$
          .takeUntil(componentDestroyed$)
          .subscribe(_ => {
            // do something
          });
    
        this
          .myObservable2$
          .takeUntil(componentDestroyed$)
          .subscribe(_ => {
            // do something
          });
    
        this
          .myObservable3$
          .takeUntil(componentDestroyed$)
          .subscribe(_ => {
            // do something
          });
      }
    
      ngOnDestroy() {
        this.componentDestroyed$.next();
        this.componentDestroyed$.complete();
      }
    }
    

    If you want to know more about that, here's an excellent article from Ben Lesh : https://medium.com/@benlesh/rxjs-dont-unsubscribe-6753ed4fda87

    EDIT :
    Thanks to @olsn, I edited my answer and added the line with the next because indeed a complete doesn't stop the others streams.

    I created a small Plunkr to demo that behaviour : https://plnkr.co/edit/dcueDqUgpqgYimxEAGUn?p=preview

    0 讨论(0)
  • 2021-02-13 04:54

    The only way you can your goal (not using subscription) is to use BehaviorSubject, it has a method getValue(). There are some differences between Observables and BehaviousSubjects (you can find them in documentation).

    var subject = new Rx.BehaviorSubject(current_subject_value);
    subject.getValue()
    

    Yet there are some stackoverflow debates why shoudnd't you use getValue method, but rather to use subscription... Yet there is observable take operator which will complete observable sequence (thus don't have to manage subscriptions)

    this.myObservable$.take(1).subscribe()
    
    0 讨论(0)
  • 2021-02-13 04:55

    You have to ask yourself: What do you want to achieve by avoiding the subscribe() call? My guess is that you want to prevent keeping the subscription around and having to unsubscribe manually.

    If this is the case, you just have to make sure you get an Observable that completes. Then there is no need to unsubscribe later. You can convert any Observable (finite or infinite) to one that will eventually complete.

    Here is an example for your case:

      method() {
        this.myObservable$.take(1).subscribe(
          val => doSomething(val)
        );
      }
    

    The correct way really depends on what your observable does and what you want to do with the value. Angular2 http calls for example complete on their own, no need to unsubscribe!

    Or if you want to call doSomething for every new value in your observable, the above solution won't fit because you only get the first value, then the observable completes. As I said, this comes down to the context of the problem.

    0 讨论(0)
提交回复
热议问题