Kasper Juner
Kasper Juner

Reputation: 952

Getting undefined in BehaviourSubject subscribe

I'm trying to subscribe to a BehaviourSubject that was set by parent component and get it in the child one, i'm trying to make the following in my component:

export class ModuliComponent implements OnInit {
  public profilo: Profilo;

  constructor(public profiloService: ProfiloService) { }

  ngOnInit(): void {
    this.profiloService.dataProfilo.subscribe(data => this.profilo = data); // setting profilo
  }

}

But when i try to use profilo in HTML i just get undefined on asporto or other *ngIf

enter image description here

What i'm doing wrong? should i initialize first the profilo? if so even by setting onInit or in constructor the profilo = new Profilo(); i get the same error of undefined...

If i set a console.log to subscribe it return the right data, so asporto and other stuff from subscribe are not undefined.

As requested here is ProfiloService

@Injectable({
  providedIn: 'root'
})
export class ProfiloService {

  public dataProfilo = new BehaviorSubject<any>([]);

  constructor(private http: HttpClient, private adapter: ProfiloAdapter) { }

  profilo(idNegozio: string): Observable<Profilo>{
    return this.http
    .get(`${Globals.API_URL}/profilo/${idNegozio}`)
    .pipe(map((data: any) => this.adapter.adapt(data)));
  }
}

And here is the component where i'm setting the dataProfilo:

this.profiloService.dataProfilo.next(profilo);

Console.Log from subscribe data

enter image description here

EDIT:

If i add on parent div *ngIf="profilo.moduli" it will works fine, but at this point if profilo is undefined why it is not enough to do a simple *ngIf="profilo"?

Upvotes: 0

Views: 471

Answers (3)

Barremian
Barremian

Reputation: 31135

If you're going to have an empty array by default and check if it's undefined in the template, I'd recommend you to use ReplaySubject with buffer 1 instead. It's similar to BehaviorSubject but doesn't need a default value.

@Injectable({
  providedIn: 'root'
})
export class ProfiloService {
  public dataProfilo = new ReplaySubject<any>(1);

  constructor(private http: HttpClient, private adapter: ProfiloAdapter) { }

  profilo(idNegozio: string): Observable<Profilo>{
    return this.http
    .get(`${Globals.API_URL}/profilo/${idNegozio}`)
    .pipe(map((data: any) => this.adapter.adapt(data)));
  }
}

Furthermore, if you were to subscribe to an observable in the controller only to use it directly in the template, you might as well use the async pipe instead. At the least, it takes care of the unsubscription issue.

Child component

export class ModuliComponent implements OnInit {
  public profilo$: Observable<any>;

  constructor(public profiloService: ProfiloService) { }

  ngOnInit(): void {
    this.profilo$ = this.profiloService.dataProfilo;
  }

}

Template

<ng-container *ngIf="(profilo$ | async) as profilo">
  <section>
    ...
  </section>
</ng-container>

The template within the <section> tags doesn't have to be changed since we are using *ngIf directive's as signature to denote the current values from the observable.

Upvotes: 1

dien luc
dien luc

Reputation: 26

When init child component then the profilo var is undefine and the profilo var just have data when the API return data. Therefore, u can use "?" operator or check the profilo var as below.

<div *ngIf="profilo?.moduli?.ticketing" ></div>

OR

<div *ngIf="profile && profile.moduli && profile.profile.ticketing" ></div>

Upvotes: 0

Jobelle
Jobelle

Reputation: 2834

In html use ? operator

<div *ngIf="profilo?.moduli?.ticketing" ></div>

Upvotes: 2

Related Questions