9

Seasons greetings everyone!

I have the following code that constructs a single radio-button based on the materialize-css framework http://materializecss.com/forms.html#radio

<input name = 'group1' type = 'radio' id = 'test2'/> <label for = 'test2'>Yellow</label> 

My attempt at using *ngFor is shown below:

 statuses: string[] = [ 'Single', 'Married', 'Divorced', 'Common-law', 'Visiting' ]; <p>{{maritalStatus?.status}}</p> <div *ngFor = 'let status of statuses; let indx = index'> <input #widget class = 'with-gap' name = 'statusGroup' type = 'radio' id = 'status' [value] = 'status' [(ngModel)] = 'maritalStatus.status' (change) = 'radioBtnChange$.next(status)' /> <label for = 'status'>{{status}}</label> <p>{{status}}{{ indx}}</p> </div> 

All buttons are created but only the first button (Single) can be selected.

How can I get the series of button to function as radio-buttons are expected to do?

Thanks

1 Answer 1

11

Plunker

Why it's not working

The status variable in your *ngFor loop is not being used in the for attribute of your label or the id attribute of your input.

There are two options to fix this:

Template expressions

You can use a template expression by putting the attributes in square brackets like this:

<input [id]="status">

which is what you did (correctly) with the value attribute.

A template expression produces a value. Angular executes the expression and assigns it to a property of a binding target; the target might be an HTML element, a component, or a directive.

Interpolation

You can use interpolation by using double-curly braces like this:

<input id="{{status}}">

More generally, the material between the braces is a template expression that Angular first evaluates and then converts to a string.

What's the difference?

Checkout this answer for an explanation on the differences between these methods.

Full Template Html

<h2>Current Status</h2> <p>{{maritalStatus?.status}}</p> <h2>Options</h2> <div *ngFor="let status of statuses; let indx = index"> <input #widget class='with-gap' name='statusGroup' type='radio' [id]='status' [value]='status' [(ngModel)]='maritalStatus.status' /> <label [for]='status'>{{status}}</label> </div> 

Full Component

import {Component} from '@angular/core'; import {Http} from '@angular/http' import {bootstrap} from '@angular/platform-browser-dynamic'; @Component({ selector: 'material-app', templateUrl: 'app.component.html' }) export class AppComponent { maritalStatus = { status: 'Nothing selected' }; statuses: string[] = [ 'Single', 'Married', 'Divorced', 'Common-law', 'Visiting' ]; constructor() { } } 

Update - Angular 2 versions < 2.2.0

If you're using an Angular 2 version that is less than 2.2.0 you need to explicitly set the label's for attribute like this:

<label [attr.for]='status'>{{status}}</label> 

because for is not a property of label elements.

Why?

Since Angular 2.2.0 (634b3bb), Angular maps the for attribute to the related htmlFor property.

It sounds like a lot of developers intuitively expected this, so they added it.

This was all pretty confusing for me at first, and this article by Pascal Precht really cleared up a lot of questions.

Sign up to request clarification or add additional context in comments.

3 Comments

Great, and thanks. Two small tweaks I had to make to get it running - both id on the input and for on the label are attributes, NOT properties. Hence I had to prefix both with attr for it to work. See EDIT1 above for what works for me. Thanks
Actually, only a single tweak is necessary - the one for label since id is both a property and an attribute.
I just found out that it's only needed if you're using an Angular 2 version that is less than 2.2.0.. I was pretty confused at first, and it turns out they added this as a feature recently. Thanks for pointing it out as it led to some fun learning about the framework!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.