How to create variable in ngFor loop?
I think this is a classic case for making a subcomponent.
<td *ngFor="#prod of products">
<subComp [prod]=prod></subComp>
</td>
Your component would then have a prod
input and run the required function once in OnInit
.
Simple example plunk here
I think local variables (defined with the #
character) don't apply for your use case.
In fact, when you define a local variable on an HTML element it corresponds to the component if any. When there is no component on the element, the variable refers to the element itself.
Specifying a value for a local variable allows you to select a specific directive associated with the current element. For example:
<input #name="ngForm" ngControl="name" [(ngModel)]="company.name"/>
will set the instance of the ngForm
directive associated with the current in the name
variable.
So local variables don't target what you want, i.e. setting a value created for the current element of a loop.
If you try to do something like that:
<div *ngFor="#elt of eltList" >
<span #localVariable="elt.title"></span>
{{localVariable}}
</div>
You will have this following error:
Error: Template parse errors:
There is no directive with "exportAs" set to "elt.title" ("
<div *ngFor="#elt of eltList" >
<span [ERROR ->]#localVariable="elt.title"></span>
{{localVariable}}
</div>
"): AppComponent@2:10
Angular2 actually looks for a directive matching the provided name elt.title
here)... See this plunkr to reproduce the error: https://plnkr.co/edit/qcMGr9FS7yQD8LbX18uY?p=preview
See this link: http://victorsavkin.com/post/119943127151/angular-2-template-syntax, section "Local variables" for more details.
In addition to the current element of the iteration, ngFor
only provides a set of exported values that can be aliased to local variables: index
, last
, even
and odd
.
See this link: https://angular.io/docs/ts/latest/api/common/NgFor-directive.html
What you could do is to create a sub component to display elements in the loop. It will accept the current element as parameter and create your "local variable" as attribute of the component. You will be able then to use this attribute in the template of the component so it will be created once per element in the loop. Here is a sample:
@Component({
selector: 'elt',
template: `
<div>{{attr}}</div>
`
})
export class ElementComponent {
@Input() element;
constructor() {
// Your old "localVariable"
this.attr = createAttribute(element.title);
}
createAttribute(_title:string) {
// Do some processing
return somethingFromTitle;
}
}
and the way to use it:
<div *ngFor="#elt of eltList" >
<elt [element]="elt></elt>
</div>
Although arguments can be made about the advantages of simply writing a new subcomponent. I think that the following solution is the most correct answer to the question asked.
<div *ngFor="let person of listOfPeople">
<ng-container
*ngTemplateOutlet="introText; context: { title: getTitle(person), color: person.color }"
></ng-container>
</div>
<ng-template #introText let-title="title" let-color="color">
<p [style.color]="color">
{{title}} loves the color {{color}}
</p>
</ng-template>
Check out a codesandbox of this.
This will allow for defining multiple template variables, and reusing the output of a calculation an unlimited number of times.
Reasons to use ng-template:
- As OP mentioned, this is lighter weight than a whole new component. Therefore this may be a good option if you have a simple template that reuses calculated logic
- In some cases the template NEEDS to be in the same file - I ran into this with a 'mat-stepper' (Material library) component for example
Reasons to avoid ng-template:
- If the code should be reusable in other components
- If the template gets too complex, it will become far more confusing than helpful
With angular 4 you can do this:
<td *ngFor="#prod of products">
<div *ngIf="getBuild(branch,prod); let build">
<a href="{{build.url}}">
{{build.status}}
</a>
</div>
</td>