angularangular-routingangular-servicesangular-ngmodel

Angular:: error TS2532: Object is possibly 'undefined'


I was doing the angular Tour-hero project(replaced Hero with 'User'). There when I made the Hero(User) and Hero-detail(User-detail) separate and when I tried to access the details, it is not showing and the update function also doesn't work. It is showing this error:

error TS2532: Object is possibly 'undefined'.

6 <input id="user-name" [(ngModel)]="user.name" placeholder="name">

The user object is,I think, giving the problem. But when I tried to do it exactly like the tutorial with the addition of MessageService and all,it works. But when I removed all that,it is giving this error. Thanks in advance for the help.

user-detail.component.html:

<div>
  <h2>{{user.name | uppercase}} Details</h2>
  <div><span>id: </span>{{user.id}}</div>
  <div>
        <label for="user-name">User name: </label>
        <input id="user-name" [(ngModel)]="user.name" placeholder="name">
    </div>
    <button (click)="goBack()">Back</button>
    <button (click)="save()">Save</button>
</div>

user-detail.component.ts:

import { Component, OnInit } from '@angular/core';
import { User } from 'src/app/model/user';
import { UserService } from 'src/app/services/user/user.service';
import { Location } from '@angular/common';
import { ActivatedRoute } from '@angular/router';


@Component({
  selector: 'app-user-detail',
  templateUrl: './user-detail.component.html',
  styleUrls: ['./user-detail.component.scss']
})
export class UserDetailComponent implements OnInit {
  user?: User

  constructor(
    private userService: UserService,
    private location: Location,
    private route: ActivatedRoute
  ) { }

  ngOnInit(): void {
    this.getUser();
  }

  getUser(): void {
    const id =
     parseInt(this.route.snapshot.paramMap.get('id')!, 1);
    this.userService.getUser(id)
      .subscribe(user => this.user = user);
  }

  goBack(): void {
    this.location.back();
  }

  save():void {
    if(this.user){
      this.userService.updateUser(this.user)
        .subscribe(() => this.goBack())
    }
  }

}

user.service.ts:

import { Injectable } from '@angular/core';
import { User } from 'src/app/model/user';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { catchError } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class UserService {
  
  private usersUrl = 'api/users/';

  httpOptions = {
    headers: new HttpHeaders({ 'Content-Type': 'application/json'})
  };

  getUsers(): Observable<User[]> {
    return this.http.get<User[]>(this.usersUrl).pipe(
      catchError(this.handleError<User[]>('getUsers',[]))
    );
  }

  /** GET hero by id. Will 404 if id not found */
  getUser(id: number): Observable<User> {
    const url = `${this.usersUrl}/${id}`;
    return this.http.get<User>(url).pipe(
      catchError(this.handleError<User>(`getUser id=${id}`))
    );
  }

  updateUser(user: User): Observable<any> {
    return this.http.put(this.usersUrl, user, this.httpOptions)
    .pipe(
        catchError(this.handleError<User[]>('getUsers',[]))
    );
  }

  addUser(user: User): Observable<User>{
    return this.http.post<User>(this.usersUrl, user, this.httpOptions)
      .pipe(
        catchError(this.handleError<User>('addUser'))
      )
  }

  deleteUser(id: number): Observable<User>{
    const url = `${this.usersUrl}/${id}`;

    return this.http.delete<User>(url, this.httpOptions).pipe(
      catchError(this.handleError<User>('deleteUser'))
    )
  }

  constructor(private http: HttpClient) { }

  private handleError<T>(operation = 'operation', result?:T){
    return (error: any): Observable<T> => {
      console.error(error);
      return of(result as T);
    }
  }
}

Solution

  • I Believe this error is occurred due to typescript version 2.1 or later, your code is perfect! There is no any issue, but the way of deceleration of variable without initialized may cause of this error,

    Now If you the know the user value or you want to initialized with any default values then please initialized your user variable inside constructor like below

    user: User;
    
    constructor() {
      this.user = {
        id: 0,
        name: ''
      };
    }
    

    Another solution is to make use of 'Definite Assignment Assertion' to tell typescript that this variable will have a value at runtime, like below

    component.ts

    user!: User;
    

    Also make change with component.html file like below

    <div>
      <h2>{{user?.name! | uppercase}} Details</h2>
      <div><span>id: </span>{{user?.id!}}</div>
      <div>
        <label for="user-name">User name: </label>
        <input id="user-name" [(ngModel)]="user?.name!" placeholder="name">
      </div>
      <button (click)="goBack()">Back</button>
      <button (click)="save()">Save</button>
    </div>