1

I have the following component and service:
Component

@Component({
  selector: 'app-root',
  template: '{{user.name}}',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {

  user: User;

  constructor(private userService: UserService) {
    this.userService.get().subscribe(user => {
      this.user = user;
    });
  }

}

Service

@Injectable()
export class UserService {
  constructor(private http: HttpClient) {
  }

  get(): Observable<User> {
    return this.http.get<User>('http://www.mocky.io/v2/5cc0af9a310000440a0364b6');
  }
}

User

export class User {
  constructor(
    public name: string
  ) {
  }
}

As you can see, I'm just trying to print the user's name. It works, but before it prints the value I receive Cannot read property 'name' of undefined error several times.
I know the reason — I load the user asynchronously, but how to deal with it?
I found the safe operator:

{{user?.name}}

And it works. But in my real project this approach will not work everywhere.
I also tried to use ngIf:

<div *ngIf="user">
  {{user.name}}
</div>

But this will break another part of my app.
Is there a way to render a component after an HTTP call, so that I don't have to use Safe Operator or ngIf? If not, then what approach do you use? How do you deal with this problem?

Boris
  • 4,944
  • 7
  • 36
  • 69
  • 2
    Put the service subscription inside ngOnInit lifecycle [Reserve the constructor for simple initialization such as wiring constructor parameters to properties. The constructor shouldn't do anything. It certainly shouldn't call a function that makes HTTP requests to a remote server as a real data service would.](https://angular.io/tutorial/toh-pt4) – Sourav Dutta Apr 24 '19 at 19:10
  • Those are 2 common ways to deal with your "issue". I am not understanding why they do not work for you though. Why can't you use the safe navigation operator? Why does `*ngIf="user"` "break" part of your app? Focus on these questions and fix your app instead of trying to find some magic bullet that you think will work because there isn't one. – Igor Apr 24 '19 at 19:18
  • 2
    As a side note you should not implement any logic in your constructor. Implement the `OnInit` interface instead and make your calls there (like the call to get the user from the service). – Igor Apr 24 '19 at 19:20
  • You should call services in `ngOnInit` method – Asif Apr 24 '19 at 19:27
  • 1
    Possible duplicate of [Angular2: Cannot read property 'name' of undefined](https://stackoverflow.com/questions/39755336/angular2-cannot-read-property-name-of-undefined) – Igor Apr 24 '19 at 19:31

4 Answers4

1

You can prevent it by create default User object

export class AppComponent {

  user = new User('');

}
Hien Nguyen
  • 24,551
  • 7
  • 52
  • 62
0

If you mean to say that by introducing another extra div in your template will break the CSS, you can use ng-container element and have *ngIf in that as

<ng-container *ngIf="user">{{user}}</ng-container>

According to the definition

The Angular is a grouping element that doesn't interfere with styles or layout because Angular doesn't put it in the DOM.

Saksham
  • 9,037
  • 7
  • 45
  • 73
0

Your code looks correct, change your user class to interface:

export interface User {
  name: string;
}

Or if you want to use the class, change it to:

export class User {
  name: string;
}

You current code:

export class User {
  constructor( public name: string) {
  }
}

Means property will be created only when the constructor will be called, and that will only be called when you will try to create the object by using new:

user = new User();
Ali Adravi
  • 21,707
  • 9
  • 87
  • 85
0

You don't need create a DTO for this case:

  get(): any{
return this.http.get<User>('http://www.mocky.io/v2/5cc0af9a310000440a0364b6');

}

NhutLe
  • 95
  • 8