Does there exist something equivalent to the async
pipe that I could use inside a component like this
@Component({
selector: \'my-component
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.
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();
}
}
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
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()
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.