nbonniot
nbonniot

Reputation: 1126

How to access variable defined in *ngFor?

In my first Angular 4 application, I defined a list component :

<edm-document *ngFor="let document of documents" class="column is-one-quarter"></edm-document>

Document is an interface :

export interface Document {
    id?: Number,
    name: string,
    filePath: string
}

All is working as expected, i.e I get my documents list. But now I would like to access to document variable inside my DocumentComponent (the edm-document tag component)

Inside my DocumentComponent template, if I try this it's not working :

<p>{{ document.name }}</p>

I get this error : DocumentComponent.html:1 ERROR TypeError: Cannot read property 'name' of undefined.

I need to enforce document definition like this, and specify document as an input :

<edm-document *ngFor="let document of documents" [document]="document" class="column is-one-quarter"></edm-document>

Now it works but seems a bit redundant to me as I defined a let in loop. Does that mean the variable defined with let is only available in tag where ngFor directive is set ?

Am I missing something?

Thanks,

Nicolas

Upvotes: 4

Views: 4980

Answers (4)

Arjan
Arjan

Reputation: 23529

it works but seems a bit redundant to me as I defined a let in loop

It is not as redundant as it might seem, which becomes obvious when rewriting things a bit:

  • When not explicitly defining what the component should use (with [document]="document" in your example) then how would your component know that the parent variable is named document? Consider:

    <edm-document *ngFor="let d of documents" [document]="d"></edm-document>
    

    One could argue that Angular could introduce some parent variable to access the outer loop variable, but then the component would know how it's going to be used, and could only be used in a loop. Reusable components should not be aware of that.

  • How would it know that it can use that loop variable directly, and does not need some child property instead? Like:

    <edm-document *ngFor="let d of documents" [document]="d.text"></edm-document>
    

So: your code is just fine.

Upvotes: 4

Ales
Ales

Reputation: 56

well you can also do something like this

<edm-document *ngFor="let document of documents" class="column is-one-quarter">
 <span class="something">{{document.name}}</span>
</edm-document>

and in the edm-document.component.html do something like

<ng-content select=".something"></ng-content>

Upvotes: 1

Tade
Tade

Reputation: 41

The value (document) of the loop is valid inside of that block where the *ngFor placed. In your case between: <edm-document>..</edm-document>

In your example:

<edm-document *ngFor="let document of documents"class="column is-one-quarter">
<p>{{ document.name }}</p> <!-- document.name is valid -->
</edm-document>
<p>{{ document.name }}</p> <!-- document.name is invalid -->

Upvotes: 0

Aravind
Aravind

Reputation: 41533

Initially during DOM rendering the documents object will undefined

  • Use a typesafe ? operator

    <p>{{ document?.name }}</p>
    
  • Use a *ngIf with a array length condition as below,

    <span *ngIf="documents.length > 0"> 
       <edm-document *ngFor="let document of documents" [document]="document" 
                     class="column is-one-quarter"></edm-document>
    </span>
    

Upvotes: 1

Related Questions