I'm submitting a ... (check one with "x")
[x] bug report => search github for a similar issue or PR before submitting
[ ] feature request
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question
Current behavior
When dynamically creating a group of radio buttons using NgFor and then creating a template variable pointing to an instance of the NgModel directive for validation, the variable is never set.
@Component({
selector: 'rio-app',
template: `
<form #myForm="ngForm">
<label *ngFor="let gender of genders">
{{ gender }}:
<input type="radio" name="gender" [value]="gender"
ngModel required #myGender="ngModel"
/>
</label>
<div [hidden]="!myGender.hasError('required')">
The gender is required
</div>
</form>
`
})
export class AppComponent {
genders = ['Male', 'Female']
}
When executing this code, I get the following error in the browser's console:
Cannot read property 'hasError' of undefined
Expected behavior
The template variable should be defined and being able to be used for validation.
If instead of using NgFor to generate the group of radio buttons I hardcode the options, the app works.
@Component({
selector: 'rio-app',
template: `
<form #myForm="ngForm">
<label>
Male:
<input type="radio" name="gender" value="Male"
ngModel required #gender="ngModel"
/>
</label>
<label>
Female:
<input type="radio" name="gender" value="Female"
ngModel required #gender="ngModel"
/>
</label>
<div [hidden]="!gender.hasError('required')">
The gender is required
</div>
</form>
`
})
export class AppComponent {}
Minimal reproduction of the problem with instructions
See plunker above
What is the motivation / use case for changing the behavior?
To be able to have a standard way to handle validation when using NgFor or hardcoding options
Please tell us about your environment:
Plunker
Angular version: 2.0.X
Angular 2.4.1
Browser: [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ]
Chrome 55 on MacOS Sierra
Typescript
node --version = I think you can use the following as workaround:
<ng-container *ngFor="let gender of genders; let last = last">
<label >
{{ gender }}:
<input type="radio" name="gender" [value]="gender"
ngModel required #myGender="ngModel" />
</label>
<div *ngIf="last" [hidden]="!myGender.hasError('required')">
The gender is required
</div>
</ng-container>
@DzmitryShylovich then it will not change the error message after radio selection
@alexzuza thank you, that works. Although, I think the angular docs about template reference variables are misleading:
We can refer to a template reference variable _anywhere_ in the current template.
That's not true when using an NgFor that creates it's own block scope.
Seeing the same problem with anything created inside of an ngSwitch as well: https://github.com/angular/angular/issues/14038
I'm having the same problem with setting a dynamically created input for phone numbers like this:
...
<div *ngFor="input of inputs">
<md-input-container *ngIf="inputType === 'phone'">
<input
...
[(ngModel)]="input"
#phone="ngModel"
>
</md-input-container>
<div
[hidden]="phone?.hasError('pattern')"
>
<p>Test</p>
</div>
</div>
...
Was this ever addressed?
If you are using Angular Material, and you must find a way to show an error message, I used the following solution:
Place a <mat-hint> directly underneath the input with the message you wish to display. You can style it red for the "error" effect. Add an *ngIf directly on this element that will only display the "hint" if there is an error. Inside the *ngFor block, it can look something like this:
<input matInput #myVar="ngModel"/>
<mat-hint *ngIf="myVar.hasError('required')" class="my-error">This field is required<mat-hint>
You can add as many as you need, each with its own unique test.
It's not great, but it's a decent workaround until this gets fixed
If I understand this correctly, I'm also seeing it with components that are made inside the ngFor. ng-container doesn't change it.
Relabeling this as a docs issue, as this is working as intended but the documentation isn't clear enough here. Specifically this section needs to clarify the scope of a template variable:
https://github.com/angular/angular/blame/master/aio/content/guide/template-syntax.md#L1767
Most helpful comment
I think you can use the following as workaround: