Search code examples
htmlangulartypescriptangular-ngmodel

append String to ngModel to get an expression


I have a Business Object Car{id:2,name:ford,modelNo:123} I also have a MetaData class for CarMetaData{columnName:string,type:text,required:true} each attribute has it's own CarMetaData object how can I display Car data in a form in such a way using an Array of CarMetaData objects

In the template.html file

<form #carForm='ngForm'>
 <div class="form-group" *ngFor="let metaData of metadata" >
   <input [(ngModel)]="car[metaData.columnName]" name="metaData.columnName" 
    type="text">
 </div>
</form>

In the component file

car:Car;
metadata:CarMetaData[];

The above method isn't working because of the [] in car[meta.columnName] in the ngModel is there a way an expression can be worked out in *ngFor or [(ngModle)] to calculate columName


Solution

  • Very stupid mistake. Please try this:

    <div *ngFor="let meta of metaData;">
      <input [(ngModel)]="car[meta.columnName]" name="{{meta.columnName}}"  />
      {{meta.columnName}}
    </div>
    


    Old answer:

    But first - your loop (meta in metaData) is wrong as you loop array, not object. So you better do let meta of metaData, let i = index.

    And the interesting part is how ngModel sets the value of inputs in ngFor. If you try this:

    <div *ngFor="let meta of metaData; let i = index">
      <input [(ngModel)]="car[meta.columnName]" name="meta.columnName"  />
      {{meta.columnName}}
    </div>
    

    You will see that input value is the same of all inputs (it was picked from the first ngFor iteration and repeated to all inputs). However, {{meta.columnName}} prints correct values. So there is some scoping issue. Other strange thing - its definitely related with ngForm and input's name property. If you move that outside of the form - all happens as expected. And if you:

    <div *ngFor="let meta of metaData; let i = index">
      <input [(ngModel)]="car[meta.columnName]" name="{{metaData[i].columnName}}"  />
      {{meta.columnName}}
    </div>
    

    Inside the form - again, all works well. So that might be your workaround.

    Here is a DEMO. Hopefully someone will explain it further.