38

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?

ESCoder
  • 15,431
  • 2
  • 19
  • 42
Luca Morelli
  • 2,530
  • 3
  • 28
  • 45
  • 1
    Change the name of variable, don't use the same name of variable and model. i.e.: Use #fieldHtml and [(ngModel)]="field" – diegodsp Jun 15 '21 at 20:09

17 Answers17

45

Replace the code kinda

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

to

*ngFor="let movement of allowedMovements; let i = index" [(value)]="allowedMovements[i]"
  • Please explain why this works? I am reluctant to just change the code that is expected to work to some boiler plate solution without any solid explanation on why this is expected to resolve the issue... – Wilt Jan 05 '23 at 13:37
  • The change is a non-breaking change; instead of 2-way binding on the variable `movement`, you're binding on the element at the appropriate index of the array `allowedMovements`. I'm assuming it's because Angular is making `movement` a `const`, hence "Template variables are read-only". – Nate Mar 29 '23 at 18:18
27

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.

Luca Morelli
  • 2,530
  • 3
  • 28
  • 45
23

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

Dananjaya
  • 466
  • 5
  • 9
  • 1
    This solution solved the problem for me in an Angular 12 form. I ended up removing [(ngModel)]="password" because the input wouldn't accept the value from there. It did accept the value from #password when it was passed to the function as: (ngSubmit)=onSubmit(password.value). – PDill5446 Aug 20 '21 at 03:47
  • 1
    In my case, this solved the problem. – Lê Duy Thứ Jul 09 '23 at 11:02
13

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">
Sameera K
  • 1,298
  • 1
  • 17
  • 26
8

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>
Gavin Chebor
  • 265
  • 3
  • 5
  • Yes. This solved my problem (Angular version 11) – Niko Dec 16 '20 at 04:59
  • This solved the issue "Can't bind to 'ngModel' since it isn't a known property of 'input'." for me. I am so grateful. There are so many threads about this issue but I couldn't find this solution, and the main thread is locked. I hope my comment triggers google searches for the issue and let devs find this answer. – steel00 May 27 '22 at 09:36
5

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

Chirag
  • 1,683
  • 2
  • 17
  • 26
5

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>
Matt
  • 51
  • 1
  • 3
1

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.

Kurt Hamilton
  • 12,490
  • 1
  • 24
  • 40
  • I have the same problem running ng serve. cleaned up everything and reinstalled all the pacakges, but I have the same problem. in all the templates $event is just used a a parameter in calls to methods of the component. I suppose the problem is in a third part component, but I don't understand which one – Luca Morelli Feb 08 '20 at 16:30
  • Does the solution to this question help: https://stackoverflow.com/questions/48021581/error-cannot-assign-to-a-reference-or-variable-angular-4 ? – Kurt Hamilton Feb 08 '20 at 16:38
  • I don't have this kind of expressions in the code. I just use $event as parameter in component method calls and and the app is running fine if I don't upgrade to angular 9 – Luca Morelli Feb 08 '20 at 16:54
  • Do you have a lot of instances of using $event? Too many to post here or try removing one by one to see if the problem disappears? – Kurt Hamilton Feb 08 '20 at 16:56
1

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.

Ashley
  • 422
  • 6
  • 18
1

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"

1

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.

Gander
  • 1,854
  • 1
  • 23
  • 30
1

I solved it by changing the name of directive ouput and input variable:

Before
<input type="password" [(ngModel)]="password" #password>

After

<input type="password" [(ngModel)]="password" #passwordValue>
azizkale
  • 37
  • 2
  • 6
0

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

Sam
  • 1
0

compile with aot to view all runtime errors

ng serve --aot
hamSh
  • 1,163
  • 1
  • 13
  • 27
0
#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"
mostafa kazemi
  • 514
  • 6
  • 7
0

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 }"/>
Samif
  • 57
  • 8
0

From Angular v8 onward the two-way binding to a template variable is ignored and deprecated.

Cannot assign to template variables

explained here https://angular.io/guide/deprecations#cannot-assign-to-template-variables and in previous releases. https://v8.angular.io/guide/deprecations#cannot-assign-to-template-variables

The solution is also provided in the docs i.e removing two-way template variable binding with one-way binding.

Changing From banana in the box [()] to box [] only.

<option *ngFor="let optionName of options" [(value)]="optionName"></option>

To

<option *ngFor="let optionName of options" [value]="optionName"></option>

Aamer Shahzad
  • 2,617
  • 1
  • 27
  • 25