0

I want to change a value of a radio button based on another radio button, but the event is never triggered and i have no console error. So let's say i click on the Honda car at position 5, so the brand Civic should be selected automaticly at position 5 in the form.

I'm using Angular 5 and Material design 2.

Here's my template:

<mat-list> 
    <mat-list-item *ngFor="let id of ids">
      <mat-radio-group>
        <mat-radio-button name="car" value="honda" id="honda{{id}}" (change)="onChangeCar(id,honda)">Honda</mat-radio-button>
        <mat-radio-button name="car" value="toyota" id="toyota{{id}}" (change)="onChangeCar(id,toyota)">Toyota</mat-radio-button>
      </mat-radio-group>
      <mat-radio-group>
        <mat-radio-button name="brand" value="civic" id="civic{{id}}" (change)="onChangeBrand(id)">Civic</mat-radio-button>
        <mat-radio-button name="brand" value="camry" id="camry{{id}}" (change)="onChangeBrand(id)">Camry</mat-radio-button>
      </mat-radio-group>
   </mat-list-item>
</mat-list>

In the controller i tried this, but the brand is never triggered :

@ViewChildren(MatRadioButton) rbuttons;
rbuttonsList: any[];

// This works
this.rbuttons.filter(x => x.id == 'brand13')[0].checked = true;
this.rbuttons.filter(x => x.id == 'brand131')[0].checked = true;

This give me en error : Cannot read property 'rbuttons' of undefined
// Get the ids that start with brand 13
this.rbuttonsList = this.rbuttons.filter(x => x.id.lastIndexOf('brand13', 0) === 0);
this.rbuttonsList.forEach(function (rbuttonIndex) {
    this.rbuttons.filter(x => x.id == rbuttonIndex.id)[0].checked = true;
  });
dwayne
  • 13
  • 2
  • 8
  • Where do you define `honda` and `toyota` as variables in your code? Did you open your console to check for errors? – baao Jun 13 '18 at 20:31
  • Honda and Toyota are not defined as variable in the controller. Only in the view. Like i said, i have no console error – dwayne Jun 13 '18 at 20:42
  • Did you intend to pass the string "honda" or "toyota" to the `onChangeCar` function? If so, you should enclose them in quotes, e.g., `onChangeCar(id, 'honda')`. – Andy King Jun 13 '18 at 20:54
  • 1
    Selecting the element with `document` is not the way to go in Angular. See [How can I select an element in a component template](https://stackoverflow.com/questions/32693061/how-can-i-select-an-element-in-a-component-template) –  Jun 13 '18 at 20:54
  • I think what you're looking for is the change event that fires for the mat radio group. See documentation here: https://material.angular.io/components/radio/api – ye-olde-dev Jun 13 '18 at 21:09
  • @eric99, @ViewChild('myname') input; is good to catch a unique id, but what about if i have dynamiq id's in my form ? like myname1, myname2, myname3, etc... – dwayne Jun 14 '18 at 13:25
  • From Günter Zöchbauer's answer, I would try `this.elRef.nativeElement.querySelector('#civic' + id)`. You may have to fiddle with the selector. –  Jun 14 '18 at 21:47
  • There is also `@ViewChildren` which can select all your `mat-radio-button` and provide a QueryList which you can iterate through in your method. –  Jun 14 '18 at 22:00

1 Answers1

2

There is a couple of ways to reference the element with dynamic ids in the template.

ElementRef

You can select using Angular's ElementRef instead of document. This gets you the native element, but there is no checked property on the native version of mat-radio-button, so this does not work for your purpose.

constructor(private elRef:ElementRef) {}

onChangeCar (id, car) {
  if (car == 'honda') {
    let el = this.elRef.nativeElement.querySelector('#civic' + id)
    el.checked = true; // there is no 'checked' property to set 
  }
}

ViewChildren

Since id's are dynamic, instead of ViewChild which needs an explicit id, you can use ViewChildren and QueryList methods. This works better since it uses the Angular element wrapper, and checked is available to be set (see rbutton on the console, there is a checked property).

@ViewChildren(MatRadioButton) rbuttons;

onChangeCar (id, car) {
  if (car == 'honda') {
    let rbutton = this.rbuttons.filter(x => x.id == 'civic' + id);
    rbutton[0].checked = true;
  }
}

By component property and template

You can set via checked attribute on the template, referencing a component's property.

brand = ''

onChangeCar (id, car) {
  this.brand = car; 
}
<mat-radio-group>
  <mat-radio-button name="brand" value="civic" id="civic{{id}}" 
    [checked]="brand === 'honda'"
    (change)="onChangeBrand(id)">Civic</mat-radio-button>
  <mat-radio-button name="brand" value="camry" id="camry{{id}}" 
    [checked]="brand === 'toyota'"
    (change)="onChangeBrand(id)">Camry</mat-radio-button>
</mat-radio-group>

Here is a StackBlitz (note both ViewChildren and property methods are active, so comment out one).

  • When i try to check multiple radiobutton inside a loop, i have an error : Cannot read property 'rbuttons' of undefined, But when i do it outside the loop it work. I edited my post to show you what i did so far and where the problem is – dwayne Jun 17 '18 at 20:59
  • Ok, i replaced the foreach by a simple for and it works :) – dwayne Jun 17 '18 at 21:18
  • Re your edit to the question code, I presume you are using `@ViewChildren(MatRadioButton) rbuttons;` –  Jun 17 '18 at 21:25
  • Something like `this.rbuttons.filter(x => x.id.lastIndexOf('brand13', 0) === 0).forEach(x => x[0].checked = true);` –  Jun 17 '18 at 21:26