Edit: From what i've understood reading over and over, the issue is that i can't access the properties from currentPerson. And this is where all errors come from. But i compared to other component file and they were almost the same.
First of all, i've checked the links when typing my question title. While the error code is the same, unfortunately i wasn't able to find an proper answer.
I'm totally new to angular\typescript, and i've been following a tutorial i've found on this link: https://www.techiediaries.com/angular-11-crud-rest-api-tutorial/
In order to understand how things works, i've changed product to person (and added more details like surname, address, etc). Everything was going fine, until i've got the part of updating a person details.
When i reached that point (editing person-details-components.ts), when i try to change the method 'updatePerson(): void {}' i'm having the error:
Object is possibly null . ts(2531).
I've tried turning off strictNullCheck on tsconfig.app.json , nothing changed. I've also tried to add the ? operator on the line showing the error, didn't worked also (the error changes, property doesn't exist on type 'never').
Since i'm new to angular, i'm only following the tutorial and trying to undersatnd where the error comes from. I noticed i can access currentperson, but none of its properties (name, address, etc).
I've also compared it to other component.ts files, and there i was able to access the properties i mentioned. What am i doing wrong? And sorry for the bad english.
Thanks.
Heres my 'person-details-components.ts' file:
import { Component, OnInit } from '@angular/core';
import { PersonService } from 'src/app/services/person.service';
import { ActivatedRoute, Router } from '@angular/router';
@Component({
selector: 'app-person-details',
templateUrl: './person-details.component.html',
styleUrls: ['./person-details.component.css']
})
export class PersonDetailsComponent implements OnInit {
currentperson = null;
message = '';
constructor(
private personService: PersonService,
private route: ActivatedRoute,
private router: Router) { }
ngOnInit(): void {
this.message = '';
this.getPerson(this.route.snapshot.paramMap.get('id'));
}
getPerson(id: any): void {
this.personService.read(id)
.subscribe(
person => {
this.currentperson = person;
console.log(person);
},
error => {
console.log(error);
});
}
//All errors start here. I can't access this.currentperson.name and so on
setAvailableStatus(status: any): void {
const data = {
name: this.currentperson.name,
description: this.currentperson.description,
available: status
};
this.personService.update(this.currentperson.id, data)
.subscribe(
response => {
this.currentProduct.available = status;
console.log(response);
},
error => {
console.log(error);
});
}
updateProduct(): void {
this.productService.update(this.currentProduct.id, this.currentProduct)
.subscribe(
response => {
console.log(response);
this.message = 'The product was updated!';
},
error => {
console.log(error);
});
}
deleteProduct(): void {
this.productService.delete(this.currentProduct.id)
.subscribe(
response => {
console.log(response);
this.router.navigate(['/products']);
},
error => {
console.log(error);
});
}
}
CodePudding user response:
You should always use type
or interface
for your model. In this case, we create the Person
type with id
, name
, and description
.
person.service.ts:
//... other import stuff
export type Person = {
readonly id: number;
readonly name: string;
readonly description: string;
};
@Injectable({
providedIn: 'root'
})
export class PersonService {
//...
}
Then you can import that Person
type and use it. Notice that we use a question mark ?
next to the private _currentperson?
. This tells the compiler that _currentperson
is undefined
. In other words, it is not set (initialized).
Then whenever you want to access this._currentperson
, you always need to check if it's not undefined
. In this case, we do it in setAvailableStatus
function.
It's a good habit to avoid using any
type. We use TypeScript because it enables us to use type system. Using any
defeats that purpose. In short, avoid any
at all cost. You will need it only when you have to deal with low level advanced stuff.
person-details.component.ts
import { Component, OnInit } from '@angular/core';
import { PersonService, Person } from 'src/app/services/person.service';
import { ActivatedRoute, Router } from '@angular/router';
@Component({
selector: 'app-person-details',
templateUrl: './person-details.component.html',
styleUrls: ['./person-details.component.css']
})
export class PersonDetailsComponent implements OnInit {
private _currentperson?: Person;
message = '';
public get currentPerson() {
return this._currentPerson;
}
constructor(
private personService: PersonService,
private route: ActivatedRoute,
private router: Router) { }
ngOnInit(): void {
//...
}
getPerson(id: number): void {
this.personService.read(id)
.subscribe(
//...
);
}
setAvailableStatus(status: boolean): void {
if(this._currentperson === undefined)
throw error('Person does not exist');
const data = {
name: this.currentperson.name,
description: this.currentperson.description,
available: status
};
this.personService.update(this.currentperson.id, data)
.subscribe(
//...
);
}
// ...
}
It's best to show some sort of error message in the HTML when the id
doesn't exist and hide the page. It's not productive to show the page and buttons when the consumers can't even use it.
person-details.component.html
<div *ngIf="currentperson">
<!-- Show person detail stuff -->
</div>
<div *ngIf="!currentperson">
<!-- Show error message or something here because id is invalid -->
</div>
CodePudding user response:
I would like to thank the user @skouch2022 for the clarifications. I understood that instead of passing any, i can pass the variable type. I find it better to read also (passing any was like passing null to an method in any other language).
I got that we created the type Person so it could be used around the project. On my next projects i'll surely do that. Like i mentioned, i'm totally new to angular (i'm just good at coding(generally speaking)).
Point: inside person-service.ts i created also the type available: boolean - so i could access it inside person-details-component.ts
However, now i've got an error that is way beyond my capabilities.
ngOnInit(): void {
this.message = '';
this.getPerson(this.route.snapshot.paramMap.get('id'))
//this.getPerson(this.route.snapshot.paramMap.get(id || ''));
}
The error says "Argument of type 'string | null' is not assignable to parameter of type 'number'. Okay i got it, paramMap returns me a number and i'm passing a type null. However, i need id being a number so i can search a person by its id later - right?
Anyway, i feel like i'm way better than before. Really thank you for your help.
person-details-components.ts
import { Component, OnInit } from '@angular/core';
import { Person, PersonService } from 'src/app/services/person.service';
import { ActivatedRoute, Router } from '@angular/router';
@Component({
selector: 'app-person-details',
templateUrl: './person-details.component.html',
styleUrls: ['./person-details.component.css']
})
export class PersonDetailsComponent implements OnInit {
private _currentPerson?: Person;
message = '';
public get currentPerson(){
return this._currentPerson
}
constructor(
private personService: PersonService,
private route: ActivatedRoute,
private router: Router) { }
ngOnInit(): void {
this.message = '';
this.getPerson(this.route.snapshot.paramMap.get('id'))
//this.getPerson(this.route.snapshot.paramMap.get(id || ''));
}
getPerson(id: number): void {
this.personService.read(id)
.subscribe(
person => {
this._currentPerson = person;
console.log(person);
},
error => {
console.log(error);
});
}
setAvailableStatus(status: boolean): void {
if(this._currentPerson == undefined)
throw Error ('Person does not exist');
const data = {
name: this._currentPerson.name,
surname: this._currentPerson.surname,
address: this._currentPerson.address,
phone: this._currentPerson.phone,
whatsapp: this._currentPerson.whatsapp,
instagram: this._currentPerson.instagram,
available: status
};
this.personService.update(this._currentPerson.id, data)
.subscribe(
response => {
if(this._currentPerson == undefined)
throw Error ('Error while updating person');
else
this._currentPerson.available = status;
console.log(response);
},
error => {
console.log(error);
});
}
updatePerson(): void {
this.personService.update(this.currentPerson?.id, this.currentPerson)
.subscribe(
response => {
console.log(response);
this.message = 'The person was updated!';
},
error => {
console.log(error);
});
}
deleteProduct(): void {
this.personService.delete(this.currentPerson?.id)
.subscribe(
response => {
console.log(response);
this.router.navigate(['/persons']);
},
error => {
console.log(error);
});
}
};
person-service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
const baseURL = 'http://localhost:8080/api/persons';
export type Person =
{
available: boolean;
readonly id: number;
readonly name: string;
readonly surname: string;
readonly address: string;
readonly phone: string;
readonly whatsapp: string;
readonly instagram: string;
};
@Injectable({
providedIn: 'root'
})
export class PersonService {
constructor(private httpClient: HttpClient) { }
readAll(): Observable<any> {
return this.httpClient.get(baseURL);
}
read(id: number): Observable<any> {
return this.httpClient.get(`${baseURL}/${id}`);
}
create(data: any): Observable<any> {
return this.httpClient.post(baseURL, data);
}
update(id: number, data: any): Observable<any> {
return this.httpClient.put(`${baseURL}/${id}`, data);
}
delete(id: number): Observable<any> {
return this.httpClient.delete(`${baseURL}/${id}`);
}
deleteAll(): Observable<any> {
return this.httpClient.delete(baseURL);
}
searchByName(name: string): Observable<any> {
return this.httpClient.get(`${baseURL}?name=${name}`);
}
}