16
votes

I'm migrating an application from angular 8 to 9. If I try to build for deploy I obtain this error message

ERROR : Cannot assign value "$event" to template variable "value". Template variables are read-only.
    at _AstToIrVisitor.visitPropertyWrite (...\node_modules\@angular\compiler\bundles\compiler.umd.js:8617:31)
    at PropertyWrite.visit (...\node_modules\@angular\compiler\bundles\compiler.umd.js:7459:28)
    at convertActionBinding (...\node_modules\@angular\compiler\bundles\compiler.umd.js:8224:49)
    at prepareEventListenerParameters (...\node_modules\@angular\compiler\bundles\compiler.umd.js:16861:27)
    at Object.params (...\node_modules\@angular\compiler\bundles\compiler.umd.js:17952:24)
    at ...\node_modules\@angular\compiler\bundles\compiler.umd.js:17725:94
    at Array.map (<anonymous>)
    at ...\node_modules\@angular\compiler\bundles\compiler.umd.js:17725:60
    at ...\node_modules\@angular\compiler\bundles\compiler.umd.js:17014:87
    at Array.map (<anonymous>)
    at TemplateDefinitionBuilder.buildTemplateFunction (...\node_modules\@angular\compiler\bundles\compiler.umd.js:17014:60)
    at ...\node_modules\@angular\compiler\bundles\compiler.umd.js:17558:60
    at ...\node_modules\@angular\compiler\bundles\compiler.umd.js:16992:81
    at Array.forEach (<anonymous>)
    at TemplateDefinitionBuilder.buildTemplateFunction (...\node_modules\@angular\compiler\bundles\compiler.umd.js:16992:37)
    at Object.compileComponentFromMetadata (...\node_modules\@angular\compiler\bundles\compiler.umd.js:18643:58)

How can I find the problem happens?

15
Change the name of variable, don't use the same name of variable and model. i.e.: Use #fieldHtml and [(ngModel)]="field" - diegodsp

15 Answers

24
votes

Replace the code kinda

*ngFor="let movement of allowedMovements" [(value)]="movement" 

to

*ngFor="let movement of allowedMovements; let i = index" [(value)]="allowedMovements[i]"
18
votes

finally solved the problem. I found the problem adding this to the tsconfig. json

  "angularCompilerOptions": {
    "fullTemplateTypeCheck": true,
    "strictInjectionParameters": true
  }

I'm not sure this is added automatically to the tsconfig.json of new projects, but was missing in my project.
With these options enabled I was able to see the error in the compiler log and solve it.

7
votes

I got this error with:

    <input id="inputPassword" name="inputPassword"
        [(ngModel)]="password" required #password="ngModel">

Then i replaced password variable with an object(or with a different name), it fixed the issue.

   <input id="inputPassword" name="inputPassword"
        [(ngModel)]="user.password" required #password="ngModel">
5
votes

As stated by others the problem, "Template variables are read-only" is with the two way binding

Works with angular 8

  [(ngModel)] 
  [(value)]

Throws errors in angular 9

  [(ngModel)] 
  [(value)]

Works with angular 9

  [ngModel]
  (ngModel)
  [value]

in my case I was incorrectly placing the ngModel inside an *ngFor that just happens to have an $event

Throws errors in angular 9

 <ng-container *ngFor="let color of palette; let i = index">
 <mat-form-field class="colorbtn">
   <input matInput type="color" [(value)]="color" 
    [(ngModel)]="color + i" (change)="Color($event, i)">
 </mat-form-field>
</ng-container>

Works with angular 9

<ng-container *ngFor="let color of palette; let i = index">
 <mat-form-field class="colorbtn">
   <input matInput type="color" [value]="color" (ngModel)="color + i" (change)="Color($event, i)">
 </mat-form-field>
</ng-container>
3
votes

Don't use [(ngModel)]="password" and #password both together. I got same error and just fixed removing #password.

2
votes

Make sure you are not naming the HTML element in the DOM the same as the ngModel reference.

This will give the error:

<select class="custom-select mr-sm-2" id="myVariable" name="myVariable" [(ngModel)]="myVariable" #myVariable="ngModel" required>

Changing the elements in the HTML DOM to something different fixed it for me.

<select class="custom-select mr-sm-2" id="myVariableHtml" name="myVariableHtml" [(ngModel)]="myVariable" #myVariableHtml="ngModel" required>
1
votes

It sounds like you've got some html somewhere that looks like

<input #value (click)="value = $event" />

Where input is some element, and (click) is some event handler. (I've used input and click as examples, the error message doesn't state what they actually are)

Try looking at everywhere in your code where you are using a template variable (#value in this case), and trying to somehow assign the result of an event to it in an event handler.

Edit:

Does this mean you're not seeing this error when running your dev environment? If so, does that mean you don't have aot compilation in your dev environment? I think v9 now sets aot to be true by default. At least it did to me when I recently upgraded.

1
votes

My problem came from incorrect usage of mat-option from Angular Material.

In Angular 8 this code is fine:

<mat-select formControlName="qualityStatus" required (selectionChange)="onChangeQualityStatus($event)">
                    <mat-option *ngFor="let movement of allowedMovements" [(value)]="movement">
                        {{movement.qualityStatusDescription}}
                    </mat-option>
</mat-select>

but in Angular 9 the [(value)]="movement" will throw errors. This code should simply be [value]="movement". The internal parentheses are incorrect and causing the compiler to say you're assigning to a variable. Angular 8 ignored this issue.

Hope this helps someone else.

1
votes

As the error suggests, template variables are read-only so you cannot bind your model to template variables, instead you can bind to component property.

In my case, with Angular 11, I was binding input field to template variable (i.e. [(ngModel)]="UserNameSearch") instead of component property since I had same property name as template variable. I changed my component property to camel case and used it to bind and it worked.

Before: [(ngModel)]="UserNameSearch" (binding to template variable) Binding property name same as template variable

After: [(ngModel)]="userNameSearch" (binding to component property)

Binding property name different than template variable

0
votes

enable AOT: angular.json >> architect >> build >> options >> "aot":true

add: "angularCompilerOptions": { "fullTemplateTypeCheck": true, "strictInjectionParameters": true }, to tsconfig.json

This way you should be able to see where the error is

0
votes

compile with aot to view all runtime errors

ng serve --aot
0
votes
#userName="ngModel" name="userName" [(ngModel)]="userName"

#userName value with [(ngModel)]="userName" should not be same
id change property in component.ts to
modelUserName :strgin;

#userName="ngModel" name="userName" [(ngModel)]="modelUserName"
0
votes

I was using ngFor for drop down control and was getting this error when upgraded to version Angular 9. The issue is we do not need round brackets for drop down option

Before [(ngValue)]="listItem"

After [ngValue]="listItem"

0
votes

I get this error with this

<input id="DateOfBirth"
        type="text"
        #dateOfBirth
        name="DateOfBirth"
        class="form-control"
        bsDatepicker
        datePickerMomentModifier
        [(date)]="dateOfBirth"
        [(ngModel)]="model.dateOfBirth"
        [bsConfig]="{ adaptivePosition: true }"/>

Then I solved in this way, added model in date binding.

 <input id="DateOfBirth"
        type="text"
        #dateOfBirth
        name="DateOfBirth"
        class="form-control"
        bsDatepicker
        datePickerMomentModifier
        [(date)]="model.dateOfBirth"
        [(ngModel)]="model.dateOfBirth"
        [bsConfig]="{ adaptivePosition: true }"/>
0
votes

I've seen this when a component passes one of its inputs into a child component with two-way data binding.

public class MyComponent {
  @Input()
  public foo;
}

public class MyChildComponent {
  @Input()
  public bar;
}
<!-- Invalid -->
<my-child-component [(bar)="foo"/>

<!-- Valid -->
<my-child-component [bar]="foo"/>

The output binding is invalid because Angular can't assign the value of foo from within the component. If foo needs to change, you need to emit an event outside of MyComponent to get that value changed.