[Fixed] How can i make my ngModel to work automatically in my ng-template?


I have a lot of form-groups with labels and inputs.I wanted to make ng-template which will be reusable

So i had

  <div class="form-group">
      <label class="form-control-label" for="address">Address</label>
       <input [disabled]="isTheLoggedInUserAdmin" id="address" class="form-control form- 
       [(ngModel)]="sharedService.tempUser.address" name="address" type="text">

and with ng-template it is converted to

 <ng-template [ngTemplateOutlet]="formGroup"
      [ngTemplateOutletContext]="{data: {inputId:'address', label:'Address', ngModel: 
      sharedService.tempUser.address }}"

<ng-template #formGroup let-data="data">
 <div class="form-group">
    <label class="form-control-label" [for]="data.inputId">{{data.label}}</label>
    <input [disabled]="isTheLoggedInUserAdmin" [id]="data.inputId" class="form-control form-control- 
    [(ngModel)]="sharedService.tempUser.address" [name]="data.inputId" type="text">

so i am passing here inputId, label name automatically and for now the ngModel is hardcoded it is pointing to sharedService.tempUser.address

But my ng-template needs to be dynamic so with ng-template call i should pass argument like label for example – tthe argument shpuld point to different ngModel variables in my typescript files

But when i do that

 <div class="form-group">
    <label class="form-control-label" [for]="data.inputId">{{data.label}}</label>
    <input [disabled]="isTheLoggedInUserAdmin" [id]="data.inputId" class="form-control form-control-alternative"
      [(ngModel)]="data.ngModel" [name]="data.inputId" type="text">

now data.ngModel is sended from the ng-template call – which is sharedService.tempUser.address, i get the actual value from sharedService.tempUser.address but *THE PROBLEM IS THAT ngModel does not work here`

when i type something it is not updated

How can i solve this ?


You might want to write an implementation of ControlValueAccessor as an alternative. It’s an Angular official way to create reusable component compatible with Form API. A minimum example will be something like this:


<div class="form-group">
   <label class="form-control-label" for="{{data.field}}">{{data.label}}</label>
   <input [disabled]="disabled" id="{{data.field}}" class="form-control form-control-alternative"
          [value]="value" name="{{data.field}}" type="text"
          (change)="onChange($event)" (blur)="onBlur($event)">


    import things....

  selector: 'MyControl',
  templateUrl: './my-custom-control.component.html',
  styleUrls: ['./my-custom-control.component.scss'],
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => MyControlComponent),
    multi: true
export class MyControlComponent implements OnInit, ControlValueAccessor {

  @Input() value: any;
  @Input() data: { 'field': string, 'label': string } =  { field: "default", label: "default" };
  @Input() disabled: boolean = false;
  setDisabledState(isDisabled: boolean) {
    this.disabled = isDisabled;

  propagateChange = (_: any) => { };
  propagateTouch = () => { };
  registerOnChange(fn) {
    this.propagateChange = fn;
  registerOnTouched(fn) {
    this.propagateTouch = fn;
  constructor() {

  ngOnInit() {
  // value changed from ui
  onChange(event: any){
    this.value = event.target.value;
    // tell angular that the control value is changed
  // received value from form api
  writeValue(newFormValue: any) {
    this.value = newFormValue;
  // tell angular that this form control is touched
  onBlur(event: any) {

After that, you could reuse this component like:

<!-- With FormGroup -->
<form [formGroup]="myForm">
  <MyControl formControlName="address" [disabled]="isTheLoggedInUserAdmin" [data]="{ field: 'address', label: 'Address' }">
  <MyControl formControlName="title" [disabled]="isTheLoggedInUserAdmin" [data]="{ field: 'title', label: 'Title' }">
  <MyControl formControlName="surname" [disabled]="isTheLoggedInUserAdmin" [data]="{ field: 'surname', label: 'Surname' }">
  <!-- other controls... -->

<!-- Without FormGroup -->
  <MyControl [(ngModel)]="myModel.address" [ngModelOptions]="{standalone: true}" [disabled]="isTheLoggedInUserAdmin" [data]="{ field: 'address', label: 'Address' }">
  <MyControl [(ngModel)]="myModel.title"   [ngModelOptions]="{standalone: true}" [disabled]="isTheLoggedInUserAdmin" [data]="{ field: 'title', label: 'Title' }">
  <MyControl [(ngModel)]="myModel.surname" [ngModelOptions]="{standalone: true}" [disabled]="isTheLoggedInUserAdmin" [data]="{ field: 'surname', label: 'Surname' }">
  <!-- other controls... -->

which can also be easily written into an ngFor if you have a model description array.

Edit: Add a sample scss styling.


@import "variable.scss";

:host(.ng-dirty.ng-invalid, .ng-touched.ng-invalid) input {
  border-color: $c-alert !important;
  border-width: 2px;
  border-style: solid;
  color: $c-alert;

Leave a Reply

(*) Required, Your email will not be published