0

I have a problem to use data from API Calls: I want to use the retrieved data to set checked in a checkbox, but the component is initialized before the api response and I get some console error:

ERROR TypeError: Cannot read property 'id' of undefined

I test the behaviour with a resolver too, but I have the same problem, even if the response array is logged before initialization:

component.ts

...
export class RolesComponent implements OnInit {

  flag: boolean = false;
  simpleArr: object[] = [
    { userId: 1, id: 2 },
    { userId: 1, id: 3 },
    { userId: 1, id: 5 },
    { userId: 1, id: 7 }
  ]
  permArr: object[] = [];
  permArrResolved: object[] = [];

  levels = [
    { name: 0, description: 'Creazione nuovo utente' },
    { name: 1, description: 'Reset password di qualsiasi utente' },
    { name: 2, description: 'Eliminazione di qualsiasi utente' },
    { name: 3, description: 'Modifica livello di qualsiasi utente' },
    { name: 4, description: 'Rinnovo delle licenze' },
    { name: 5, description: 'Gestione completa delle licenze' },
    { name: 6, description: 'Gestione completa dei clienti' },
    { name: 7, description: 'Gestione completa dei PC' }
  ];

  constructor(private api: RolesApiService, private route: ActivatedRoute, ) {

    this.api.getKeys().subscribe(keys => {
      this.permArr = keys;
      console.log(this.permArr);
      this.flag = true
    });

    this.route.data.pipe(
      map(data => data.cres)).subscribe((key) => {
        this.permArrResolved = key;
        console.log(this.permArrResolved);
      });

  }

  ngOnInit() {
    console.log('component is initialized');
  }

}

component.html

<form *ngIf="flag">
  <h2>with permArr[0].id</h2>
  <ng-container *ngFor="let level of levels">
    <input type="checkbox" [checked]="level.name === permArr[0]?.id" />{{level.name}} - {{level.description}}
    <br>
  </ng-container>

<hr>

<h2>with simpleArr[level.name].id</h2>
  <ng-container *ngFor="let level of levels">
    <input type="checkbox" [checked]="level.name === simpleArr[level.name]?.id" />{{level.name}} - {{level.description}}
    <br>
  </ng-container>

<hr>

  <h2>with permArr[level.name].id</h2>
  <ng-container *ngFor="let level of levels">
    <input type="checkbox" [checked]="level.name == permArr[level.name]?.id" />{{level.name}} - {{level.description}}
    <br>
  </ng-container>

<hr>

  <h2>with permArr[level.name].id using resolver</h2>
  <ng-container *ngFor="let level of levels">
    <input type="checkbox" [checked]="level.name == permArrResolved[level.name]?.id" />{{level.name}} - {{level.description}}
    <br>
  </ng-container>

</form>

I've made a stackblitz demo to show the error, the api-service, the routing and the resolver I use. is here: https://stackblitz.com/edit/async-angular-resolver

How can I solve this problem?

EDIT: using safe oprators do not solve the bug EDIT2: using *ngIf in form tag do not solve the bug

ufollettu
  • 822
  • 3
  • 19
  • 45

4 Answers4

4

There are already several answers here that correctly answer the question you are asking - the reason they dont work for you is because the data you are binding does not matching your ids.

Your previous questions were both basically asking the same thing:

To prevent further wasted effort from the community, I've gone to the effort of writing the debug code you should be writing yourself. This shows how the safe navigation operator fixes the template issue, as several people have suggested already - wrapping with *ngIf would also solve the problem and the values I've added show why your tick boxes are not checked.

https://stackblitz.com/edit/async-angular-resolver-hxxyw4?file=src/app/roles/roles.component.html

Steve Land
  • 4,852
  • 2
  • 17
  • 36
2

You can use the safe navigation operator: ?.

The Angular safe navigation operator (?.) is a fluent and convenient way to guard against null and undefined values in property paths.

For example:

[checked]="level.name === permArr[0]?.id"

This is essentially equivalent to permArr[0] && permArr[0].id

bugs
  • 14,631
  • 5
  • 48
  • 52
  • I tried, but this not solve the problem inthe second loop: if I set `level.name == permArr[level.name]?.id` I do not have the checkbox checked – ufollettu Aug 02 '18 at 09:13
1

Load View after response using *ngIf:

<form *ngIf="flagpermArr && flagpermpermArrResolved">
    <h2>with permArr[0].id</h2>

    <ng-container *ngFor="let level of levels">
        <input type="checkbox" [checked]="level.name === permArr[0]?.id" />{{level.name}} - {{level.description}}
        <br>
    </ng-container>
    <hr>
    <h2>with simpleArr[level.name].id</h2>

    <ng-container *ngFor="let level of levels ; let i = index">

 <!-- <input *ngIf="" type="checkbox" [checked]="level.name === simpleArr[level.name].id" />{{level.name}} - {{level.description}} 
    -->
        <br>
    </ng-container> 
    <hr>
    <h2>with permArr[level.name].id</h2>
    <ng-container *ngFor="let level of levels">
        <input type="checkbox" [checked]="level.name == permArr[level.name]?.id" />{{level.name}} - {{level.description}}
        <br>
    </ng-container>
    <hr>
    <h2>with permArr[level.name].id using resolver</h2>
    <ng-container *ngFor="let level of levels">
        <input type="checkbox" [checked]="level.name == permArrResolved[level.name]?.id" />{{level.name}} - {{level.description}}
        <br>
    </ng-container>
</form>

TS:

 export class RolesComponent implements OnInit {

  flagpermArr: boolean = false;
  flagpermpermArrResolved: boolean = false;

  simpleArr: object[] = [
    { userId: 1, id: 1 },
    { userId: 1, id: 2 },
    { userId: 1, id: 5 },
    { userId: 1, id: 7 }
  ]
  permArr: object[] = [];
  permArrResolved: object[] = [];

  levels = [
    { name: 0, description: 'Creazione nuovo utente' },
    { name: 1, description: 'Reset password di qualsiasi utente' },
    { name: 2, description: 'Eliminazione di qualsiasi utente' },
    { name: 3, description: 'Modifica livello di qualsiasi utente' },
    { name: 4, description: 'Rinnovo delle licenze' },
    { name: 5, description: 'Gestione completa delle licenze' },
    { name: 6, description: 'Gestione completa dei clienti' },
    { name: 7, description: 'Gestione completa dei PC' }
  ];

  constructor(private api: RolesApiService, private route: ActivatedRoute, ) {
  }

  ngOnInit() {
    this.api.getKeys().subscribe(keys => {
      this.permArr = keys;
      this.flagpermArr = true
    });

    this.route.data.pipe(
      map(data => data.cres)).subscribe((key) => {
        this.permArrResolved = key;
        this.flagpermpermArrResolved = true
      });
  }
Akj
  • 7,038
  • 3
  • 28
  • 40
  • I tried, but doesn't work... I update the stackblitz, please check if you can – ufollettu Aug 02 '18 at 09:29
  • ok so in your code 'simpleArr' length is 4 while 'levels' is 8 thats why there is no index to get 'simpleArr[level.name]'. just see commented part – Akj Aug 02 '18 at 10:17
  • i hope u understood. there is nothing regarding response u just need to maintain proper array only – Akj Aug 02 '18 at 10:21
0

You could use:

<ng-container *ngIf="permArr && permArrResolved"> your code here </ng-container>

The containers content will only be rendered when permArr and perArrResoved have data in them.

Wimza
  • 61
  • 1
  • 1
  • 5