eta32carinae
eta32carinae

Reputation: 611

How to initialize a Subscription object

I'm using Subscription to get a parameter from the route in angular. Here is the code:

import { Component, OnInit, OnDestroy } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Subscription} from 'rxjs';


@Component({
    selector: 'farm-house',
    templateUrl: './house.component.html',
    styleUrls: ['./house.component.scss']
})
export class GreenhouseComponent implements OnInit, OnDestroy {
    
    private routeSub: Subscription;
    id: string;

    constructor(private route: ActivatedRoute) {
        this.id = "";
    }

    ngOnInit(): void {
        this.routeSub = this.route.params.subscribe(params => {
            this.id = params['id'];
        });
    }

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

But the problem is that the compiler says:

Property 'routeSub' has no initializer and is not definitely assigned in the constructor.

My question is, what is the best way to initialize a Subscription object?

Upvotes: 2

Views: 8216

Answers (8)

Melroy van den Berg
Melroy van den Berg

Reputation: 3175

I just use:

private subscriptions: Subscription = new Subscription()

During your code, you can then use:

this.subscriptions.add()

And don't forget to unsubscribe:

ngOnDestroy() {
    this.subscriptions.unsubscribe()
}

Upvotes: 0

Timtest
Timtest

Reputation: 400

Note that you can remove this error by adding "strictPropertyInitialization": false in you tsconfig.json

{
   "compilerOptions": {
      "strictPropertyInitialization": false,
   }
}

Then you don't need to do this proper initialization, or add a second type undefined: private routeSub: Subscription | undefined

Upvotes: -1

KenF
KenF

Reputation: 624

Way 1:

Where I have a single subscription.

private routeSub?: Subscription;
    ngOnInit(): void {
        this.routeSub = this.route.params.subscribe(params => {
            this.id = params['id'];
        });
    }

    
    ngOnDestroy(): void {
       // no need to do a separate if, use ? it is more readable
        this.routeSub?.unsubscribe();
    }

Way 2 where I have multiple subscriptions, I clean them all up with a single clean up:

    private destroyed$ = new Subject<boolean>();

Somewhere() {
combineLatest([userService.spaceName$, userService.userName$])
      .pipe(takeUntil(this.destroyed$))
      .subscribe(([space, username]) => {do something...}
    }

ngOnDestroy() {
    this.destroyed$.next(true);
    this.destroyed$.complete();
  }

third way - mostly the best way

myObservable$: Observable<myInterface>;

ngOnInit() {
   this.myObservable$ = this.myservice.someObservable$;
}

...    html ...
<ng-container *ngIf='myObservable$ | async as list; else simpleInput'>

This last process works with onPush and you never forget to unsubscribe.

Note the first way is really handy when you subscribe and unsubscribe as part of the call, even combined with the second way:

pollUser(): void {
    // if it has been hear before unsubcribe, otherwise skip it.
    this.subscription?.unsubscribe();

    this.subscription= this.someservice(serviceParameters)
      .pipe(takeUntil(this.destroyed$))
      .subscribe(result => {
            this.useResult = result;
        });
    }

Upvotes: 0

Erik
Erik

Reputation: 161

Unsubscribe is not needed here because Angular destroys this particular subscription for you.

Upvotes: -1

eta32carinae
eta32carinae

Reputation: 611

I've come up with another solution which is using Subscription.EMPTY from this question.

import { Component, OnInit, OnDestroy } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Subscription} from 'rxjs';


@Component({
    selector: 'farm-house',
    templateUrl: './house.component.html',
    styleUrls: ['./house.component.scss']
})
export class GreenhouseComponent implements OnInit, OnDestroy {
    
    private routeSub: Subscription;
    id: string;

    constructor(private route: ActivatedRoute) {
        this.routeSub = Subscription.EMPTY;
        this.id = "";
    }

    ngOnInit(): void {
        this.routeSub = this.route.params.subscribe(params => {
            this.id = params['id'];
        });
    }

    ngOnDestroy(): void {
        if(this.routeSub) {
            this.routeSub.unsubscribe();
        }
    }
}

Upvotes: 8

ntgCleaner
ntgCleaner

Reputation: 5985

Should just be

private routeSub: Subscription = new Subscription

This is what worked for me.

Upvotes: 4

Ax123
Ax123

Reputation: 1

If you declare routeSub: any; the compiler shouldn't complain. source: saw it done in this post and i worked for me

Upvotes: -1

Ganesh
Ganesh

Reputation: 6016

Most of the cases it's should be enough to check the subscription before unsubscribe.

 ngOnDestroy() {
     if(this.routeSub) {
       this.routeSub.unsubscribe();
     }
 }

In your case, it's not required to initialize subscription because you already called subscribe method in ngOnInit(). Error might come because you are calling unsubscribe() directly on Subscription without checking it's initialized or not.

Upvotes: 1

Related Questions