Home > Net >  How to use an ngModel inside an ng-template which is repeated using *ngFor
How to use an ngModel inside an ng-template which is repeated using *ngFor

Time:09-11

I have a ng-template which is repeated using an *ngFor. I am passing additional data to the template from ngFor using ngTemplateOutletContext. With this data that I am passing to the template I am creating a dropdown list. I have this stackblitg example showing a code extract of what I have done.

Data

data = {
 row1_ID: {
  'article 1': 'Lorem Ipsum is simply dummy text ',
  'article 2': 'Lorem Ipsum has been the industry standard dummy text ',
},
 row2_ID: {
  'article 1': 'aldlsadalskjd;asjsa;kdj dalskdjaslkjd',
  'article 2': 'Lorem ipsum dolor sit ame',
 },
};

HTML

<div *ngFor="let row of data | keyvalue">
   <ng-container 
       [ngTemplateOutlet]="OpRef2"
       [ngTemplateOutletContext]="{ data: row.value, id: row.key }">
   </ng-container>
</div>

<ng-template #OpRef2 let-data="data" let-id="id">
  <div >
     <h2>Row ID: {{ id }}</h2>
     <select>  <!--Using ng model here doesnt work-->
        <option *ngFor="let article of data | keyvalue" [value]="article.key">
        {{ article.key }}
        </option>
     </select>
  <div>
  <!--I would like the contect of this do change based on what's selected in the dropdpwn but varaibles created in the context of ng-template are readonly hence nh-model doesnt work-->
  <div>{{ data['article 1'] }}</div>
</div>

Now based on the values selected in the dropdown I want the content below it to change. But since its a template, any varianle I create are readonly and can't be used in the ngModel for the select. Also I cant use an external variable list as the rows in the data property are fetched from an API and I cant be sure how many rows it would contain.

Is there any way of achiving this?

CodePudding user response:

If its only to be able to change de between articles you could do it without binding the ngModel to a property and accesing the ngModel value through a template variable.

It would be something like this.

<div >
  ...
  <select ngModel #articleSelected="ngModel">
    ...
  </select>

  <div>
    <div>{{ data[articleSelected.value] }}</div>
  </div>
</div>

Cheers

CodePudding user response:

Case is that you don't have a variable (reference object) to bind ngModel to.

I have done a simple implementation for you using a separate map as collection of reference. I used the data to create the map of references dynamically too.

Have a look at this stackblitz

HTML

  <ng-template #OpRef2 let-data="data" let-id="id">
    <div >
      <h2>Row ID: {{ id }}</h2>
      <select [(ngModel)]="valueHolders.get(id).value">
        <!--Using ng model here doesnt work-->
        <option *ngFor="let article of data | keyvalue" [value]="article.key">
          {{ article.key }}
        </option>
      </select>
      <!--I would like the contect of this do change based on what's selected in the dropdpwn but varaibles created in the context of ng-template are readonly hence nh-model doesnt work-->
      <div>{{ data[valueHolders.get(id).value] }}</div>
    </div>
  </ng-template>

TS

  valueHolders = new Map<string, ValueHolder>(); 

  createValueHoldersByData(){
    Object.keys(this.data).forEach(key=>{
      const valueHolder = new ValueHolder(key, Object.keys(this.data[key])[0]);
      this.valueHolders.set(key, valueHolder);
    }); 
  }

  export class ValueHolder{
    key: string;
    value: string;
    constructor(key: string, value: string){
      this.key = key;
      this.value = value;
    }
  }

CodePudding user response:

I would suggest creating a new property in your component class (e.g. selectedData) to hold the selected value from the dropdowns, then, binding the ngModel to it by id.

You can try something like the following:

<!-- 
  In your component class, define a selectedData with and assign an empty object to it,
  to be used in the component's template: `selectedData = {};`
-->
<ng-template #OpRef2 let-data="data" let-id="id">
  <div >
    <h2>Row ID: {{ id }}</h2>
    <!-- Use ngModel to bind to the selected value -->
    <select [(ngModel)]="selectedData[id]">
      <option *ngFor="let article of data | keyvalue" [value]="article.key">
        {{ article.key }}
      </option>
    </select>
    <div>
      <!-- Here we can use the selected[id] to bind to the selected value -->
      <div>{{ data[selectedData[id]] }}</div>
    </div>
  </div>
</ng-template>
  • Related