2

I have an "appService" which i Use to handle the interaction between all my components (forms, navigations, etc).

My service has many event emitters to which the components subscribe (results showing, element selected, form posted, and so on).

When a new view is displayed, it subscribes to the appService events it needs to. Now the problem is when i navigate to another view (form), the no longer needed view is destroyed, but its subscriptions still get called...! I find referencing all subscriptions and unsubscribing explicitly a massive boilerplate for a simple task. What am I doing wrong? how to siplify this?

Example code:

export class VendorsForm {
    constructor(private appService: AppServiceMain)
    {
            this.appService.onSearchResultSelected$.subscribe((selectedEntity) => {
            //detailed view of this item
            this.model = selectedEntity;
            this.appService.setFormIsShowingDetail();
        })
    });

    }
}

now in another component:

export class CustomersForm {
    constructor(private appService: AppServiceMain)
    {
            this.appService.onSearchResultSelected$.subscribe((selectedEntity) => {
            //detailed view of this item
            this.model = selectedEntity; this.appService.setFormIsShowingDetail();
        })
    });

    }
}

Just as a reference, here's the service and the event invoking components:

export class AppServiceMain {
    public onSearchResultSelected$: EventEmitter<IEntity>;
        setSelectedEntity(ent: IEntity)
        {
            this.formstate = states.EntitySelected;
            this.onSearchResultSelected$.emit(ent);
        }

event invoking component

export class ResultsComponent { 
    rowDblClick(ev$)
    {
        this.appService.setSelectedEntity(ev$.data);
    }

I DO understand how to remove these suscriptions if I want to. The problem is my forms are quite big and functional and I have tons of non standard references, Isnt there a way to automatically stop listening when the view gets destroyed? I find manually destroyng all these to be quite error prone.

Cœur
  • 37,241
  • 25
  • 195
  • 267
user5328504
  • 734
  • 6
  • 21
  • small remark: my ResultsComponent and appService are both always alive and sigle instance – user5328504 Aug 01 '16 at 18:09
  • Could you clearify why implementing `ngOnDestroy` method does not work for you? In case you missed: [OnDestroy](https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html) – leovrf Aug 01 '16 at 18:21

3 Answers3

4

If you subscribe imperatively, then also unsubscribe imperatively

 this.someSubscription = this.appService.onSearchResultSelected$.subscribe((selectedEntity) =>

ngOnDestroy() {
  this.someSubscription.unsubscribe();
}

See also how to unsubscribe several subscriber in angular 2

Community
  • 1
  • 1
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • 2
    thanks Günter, actually that is what i am doing right now.... i get a handle of all subscriptions in an array..but I hate to do it that way... I've seen also there's a way to automate it with "async pipes", but is awful as well... I feel like angular2 is pushing the weirdest patterns all over... how do YOU handle this stuff? maybe this approach is no good for app-flow-management? – user5328504 Aug 01 '16 at 18:44
  • I don't see a problem with `| async` nor with unsubscribe. – Günter Zöchbauer Aug 01 '16 at 18:45
  • I'll mark yours as answer - just saying this syntax is CODE SMELL – user5328504 Aug 02 '16 at 22:15
4

You don't need to have bunch of subscriptions. Use RxJS.Subject and takeUntil combo to handle subscriptions like a boss:

import {Subject} from "rxjs/Subject";

@Component(
    {
        moduleId: __moduleName,
        selector: 'my-view',
        templateUrl: '../views/view-route.view.html',
    }
)
export class ViewRouteComponent implements OnDestroy
{
    componentDestroyed$: Subject<boolean> = new Subject();

    constructor(protected titleService: TitleService)
    {
        this.titleService.emitter1$
            .takeUntil(this.componentDestroyed$)
            .subscribe(
            (data: any) =>
            {
                // ... do something 1
            }
        );

        this.titleService.emitter2$
            .takeUntil(this.componentDestroyed$)
            .subscribe(
            (data: any) =>
            {
                // ... do something 2
            }
        );

        // ...

        this.titleService.emitterN$
            .takeUntil(this.componentDestroyed$)
            .subscribe(
            (data: any) =>
            {
                // ... do something N
            }
        );
    }

    ngOnDestroy()
    {
        this.componentDestroyed$.next(true);
        this.componentDestroyed$.complete();
    }
}
metamaker
  • 2,307
  • 2
  • 19
  • 18
0

the way I am doing it is:

this.subscriptions.push( this.appService.onSearchResultSelected$.subscribe((selectedEntity) => {}));

ngOnDestroy() {
  this.subscriptions.forEach((subscription) => {
        subscription.unsubscribe();
    });
}

You can handle n number of subscriptions using this way, you just need to push every time you use the subscription.

This is the simplest and easiest way to perform.

Chintan Thummar
  • 1,462
  • 19
  • 32