rubenspessoa rubenspessoa - 1 month ago 35
AngularJS Question

Angular 2 attribute is undefined on ngOnInit

guys.

I'm new to web developing with AngularJs 2 and I don't have experience with AngularJs 1 too. So, I'm sorry if I'm asking something stupid. I'm stuck on a problem and I googled it but I couldn't find any solutions for it. :/

The problem is:

I'm trying to use in a function called at the end of ngOnInit an attribute of an object that I initialize on ngOnInit, but this object is still undefined, so I get the following exception:

Error: Uncaught (in p**strong text**romise): TypeError: Cannot read property 'secretariatId' of undefined


Here is my component:

import {Component, OnInit, AfterViewChecked} from '@angular/core';
import { ActivatedRoute, Router, Params } from '@angular/router';

import {StudentService} from './services/student.service';
import {Student} from './services/student';

import {SecretariatService} from './services/secretariat.service';

import {DisciplineService} from './services/discipline.service';
import {Discipline} from './services/discipline';


@Component({
selector: 'fountain-enrollstudent',
template: require('./templates/enrollstudent.component.html')
})
export class EnrollStudentComponent implements OnInit, AfterViewChecked {
public text: string;
selectedDiscipline: Discipline;
disciplines: Discipline[];
student: Student;

constructor(
private disciplineService: DisciplineService,
private studentService: StudentService,
private secretariatService: SecretariatService,
private router: Router,
private route: ActivatedRoute
) {
this.text = 'My brand new component!';
}

ngOnInit(): void {
this.route.params.forEach((params: Params) => {
console.log(params);
let id = +params['id'];
this.getStudent(id);
console.log(id);
console.log(this.student);
this.getDisciplines(this.student.secretariatId);
});

console.log(this.student);

}

// Understand the best moment to call it. Ps.: It doesn't work on ngOnInit
getDisciplines(secretariatId: number): void {
this.disciplineService.getDisciplinesBy(secretariatId)
.then(disciplines => this.disciplines = disciplines);
}

getStudent(id: number): void {
this.studentService.getStudent(id)
.then(student => this.student = student);
}

onSelect(discipline: Discipline): void {
this.selectedDiscipline = discipline;
}
}


Here is my template:

<div class="text-center">
<h4>Disciplines:</h4>
<ul class="list-unstyled">
<li *ngFor="let discipline of disciplines" (click)="onSelect(discipline)" [class.selected]="discipline === selectedDiscipline">
<h4>
<a>{{discipline?.name}}</a>
</h4>
</li>
</ul>
</div>


Here is the DisciplineService:

import { Injectable } from '@angular/core';

import { Discipline } from './discipline';
import { DISCIPLINES } from './mock-disciplines';

@Injectable()
export class DisciplineService {

getDisciplines(): Promise<Discipline[]> {
return Promise.resolve(DISCIPLINES);
}

getDisciplinesBy(secretariatId: number): Promise<Discipline[]> {
return this.getDisciplines()
.then(disciplines => disciplines.filter(discipline => discipline.secretariatId === secretariatId));
}
}


I was looking for another Lifecycle Hooks to call the function getDisciplines(id: number), but I tried them all and I had no good results. :/

Thanks in advance!

Answer

You need to chain async calls:

ngOnInit(): void {
  this.route.params.forEach((params: Params) => {
    console.log(params);
    let id = +params['id'];

    // vvvvvvvv use `.then(...)`
    this.getStudent(id).then(student => {  
      console.log(id);
        console.log(this.student);
        this.getDisciplines(this.student.secretariatId);
    });
  }
}

getStudent(id: number): void {
   // vvvvvvvv return the promise so callers can chain their code
   return this.studentService.getStudent(id)
     .then(student => this.student = student);
}