After I read Alexanders suggestions, I updated the code and got no error back. But Angular doesn't do a request to the server anymore, which make me curious. And also the pageTitle
does not update.
{{appointmentDetail.time}}
import { Component, OnInit, OnDestroy, Injectable } from '@angular/core';
import { ActivatedRoute, ParamMap } from '@angular/router';
import { Title } from '@angular/platform-browser';
import { APIService } from './../../../api.service';
import { Observable } from 'rxjs';
import { switchMap, tap } from 'rxjs/operators';
@Component({
selector: 'app-appointmentdetail',
templateUrl: './appointmentDetail.component.html',
styleUrls: ['./appointmentDetail.component.scss']
})
export class AppointmentDetailComponent implements OnInit {
id: any;
appointmentDetail$: Observable<Object>; // I'd really create an interface for appointment or whatever instead of object or any
pageTitle = 'Some Default Title Maybe';
constructor(
private route: ActivatedRoute,
private title: Title,
private apiService: APIService
) {}
ngOnInit() {
this.appointmentDetail$ = this.route.paramMap.pipe(
tap((params: ParamMap) => {
this.id = params.get('id');
// Or this.id = +params.get('id'); to coerce to number maybe
this.pageTitle = 'Termin' + this.id;
this.title.setTitle(this.pageTitle);
}),
switchMap(() => this.apiService.getAppointmentDetailsById(this.id))
);
}
public getData() {
this.apiService
.getAppointmentDetailsById(this.id)
.subscribe((data: Observable<Object>) => {
this.appointmentDetail$ = data;
console.log(data);
});
}
}
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class APIService {
API_URL = 'http://localhost:5000';
constructor(private httpClient: HttpClient) {}
getAppointments() {
return this.httpClient.get(`${this.API_URL}/appointments/`);
}
getAppointmentDetailsById(id) {
return this.httpClient.get(`${this.API_URL}/appointments/${id}`);
}
getAppointmentsByUser(email) {
return this.httpClient.get(`${this.API_URL}/user/${email}/appointments`);
}
getCertificatesByUser(email) {
return this.httpClient.get(`${this.API_URL}/user/${email}/certificates`);
}
}
As you can see, I want to grab that parameter id
from the router parameters and want to pass it into my API call, which will do a Angular HTTP request. Hope I'm right, haha.
Currently, I ran into a nasty problem. The thing is, I want to read the params, which are given to me by ActivatedRouter
and the Angular OnInit
function. I subscribe them params and log them in the console. Until here, everything is working fine. But I want to access " this.id
" outside from my OnInit
function, so I can use it on pageTitle for example.
But, this.id is undefined. So the page title is Termineundefined.
Source code:
import { Component, OnInit, OnDestroy, Injectable } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Title } from '@angular/platform-browser';
import { APIService } from './../../api.service';
@Component({
selector: 'app-appointment-details',
templateUrl: './appointment-details.component.html',
styleUrls: ['./appointment-details.component.scss']
})
@Injectable()
export class AppointmentDetailsComponent implements OnInit, OnDestroy {
private routeSub: any;
id: any;
private appointmentDetail: Array<object> = [];
constructor(
private route: ActivatedRoute,
private title: Title,
private apiService: APIService
) {}
pageTitle = 'Termin' + this.id;
ngOnInit() {
this.title.setTitle(this.pageTitle);
this.getData();
this.routeSub = this.route.params.subscribe(params => {
console.log(params);
this.id = params['id'];
});
}
ngOnDestroy() {
this.routeSub.unsubscribe();
}
public getData() {
this.apiService
.getAppointmentDetailsById(this.id)
.subscribe((data: Array<object>) => {
this.appointmentDetail = data;
console.log(data);
});
}
}
The issue here really comes down to async availability of route params and observable streams. You simply cannot use the value until it has resolved for all practical purposes. You can use RxJS operators such as switchMap
and tap
in line with the official Routing & Navigation documentation to ensure route param id
is available prior to use. tap
can be used to introduce side effects such as setting class id
property from route params and/or setting title. You could even create a class property of an Observable<YourObject[]>
and utilize Angular Async Pipe to avoid subscribing and unsubscribing to display the data.
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Title } from '@angular/platform-browser';
import { APIService, MyFancyInterface } from './../../api.service';
import { Observable } from 'rxjs';
import { switchMap, tap } from 'rxjs/operators';
@Component({
selector: 'app-appointment-details',
templateUrl: './appointment-details.component.html',
styleUrls: ['./appointment-details.component.scss']
})
export class AppointmentDetailsComponent implements OnInit {
id: any;
appointmentDetail$: Observable<MyFancyInterface>;
appointmentDetail: MyFancyInterface;
pageTitle = 'Some Default Title Maybe';
constructor(
private route: ActivatedRoute,
private title: Title,
private apiService: APIService
) {}
ngOnInit() {
this.appointmentDetail$ = this.route.paramMap.pipe(
tap((params: ParamMap) => {
this.id = params.get('id')
// Or this.id = +params.get('id'); to coerce to type number maybe
this.pageTitle = 'Termin' + this.id;
this.title.setTitle(this.pageTitle);
}),
switchMap(() => this.apiService.getAppointmentDetailsById(this.id))
);
/* Or
this.route.paramMap.pipe(
tap((params: ParamMap) => {
this.id = params.get('id')
// Or this.id = +params.get('id'); to coerce to type number maybe
this.pageTitle = 'Termin' + this.id;
this.title.setTitle(this.pageTitle);
}),
switchMap(() => this.apiService.getAppointmentDetailsById(this.id))
).subscribe((data: MyFancyInterface) => {
this.appointmentDetail = data;
});
*/
}
}
Template:
<div>{{(appointmentDetail | async)?.id}}</div>
I'd recommend to create an interface to represent your data model and type the return of your api service method:
import { Observable } from 'rxjs';
// maybe put this into another file
export interface MyFancyInterface {
id: number;
someProperty: string;
...
}
export class APIService {
...
getAppointmentDetailsById(id): Observable<MyFancyInterface> {
return this.httpClient.get<MyFancyInterface>(`${this.API_URL}/appointments/${id}`);
}
...
}
If you really must, you can save the observable as you do now for the route params and subscribe as needed in the various parts of the class, but this demonstrated way you almost absolutely know that route param id
will be available for use and can explicitly set the things you need to set.
I'd also remove @Injectable()
as there is no reason to have it here with a @Component()
decorator.
Note* the async pipe operator in this example ensures the Http call is executed. Otherwise a subscribe() is needed (search SO for Angular http not executing to see similar issues)
Hopefully that helps!
Instead of
id: any;
You could try using a getter, like so
public get id(): any {
this.route.params.subscribe(params => {
return params['id'];
}
}
In your template, just
{{ id }}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.