charsi
charsi

Reputation: 3847

Switching bootstrap radio buttons with Angular 2+ from the template

I am trying to switch a set of radio buttons with angular class and attribute statements. When I click the buttons I can see the active class being added and removed as required and the checked attribute being set too. However the radio button doesn't actually get checked.

<div class="btn-group " data-toggle="buttons">
  <label class="btn btn-primary" [ngClass]="{'active': s}">
    <input type="radio" name="options" id="option1" autocomplete="off" (click)="s=true" [attr.checked]="s"> Yes
  </label>
  <label class="btn btn-primary" [ngClass]="{'active': !s}">
    <input type="radio" name="options" id="option2" autocomplete="off"(click)="s=false" [attr.checked]="!s"> No
  </label>
</div>

Live demo of the problem

Using Angular 5, bootstrap 4.0.0

EDIT: Not a duplicate because I know there are other ways of doing it. But I am trying to figure out why the above method isn't working.

EDIT2: If I bind to a function with (click)="doSomething()" it works! but also causes an error because the function isn't defined. If I create the function it stops working again.

Upvotes: 3

Views: 4500

Answers (3)

chris_r
chris_r

Reputation: 2147

I have a better solution. You might appreciate the simplicity. Because the user is actually going to click the radio button, they had it with ngmodel which passes data into field, which you don't want.

html

<div class="btn-group" role="group" aria-label="View Type">
   <button type="button" [class.active]="sortClass" (click)="switch(1)" class="btn btn-secondary">
      <span>Grid</span>
   </button>
   <button type="button" [class.active]="listClass" (click)="switch(2)" class="btn btn-secondary">
      <span>List</span>
   </button>
 </div>

component.ts

// Create variables
private sortClass: boolean;
private listClass: boolean;

// INITIALIZE TRUE / FALSE for both variables
constructor() {
    this.sortClass = false;
    this.listClass = true;
}

switch(i) {
        if (i === 1) {
            this.sortClass = !this.sortClass
            this.listClass = !this.listClass
        } else {
            this.listClass = !this.listClass
            this.sortClass = !this.sortClass
        }
    }

Then you will see one active, and the other false. When you click they will alternative true/false.

Upvotes: 2

Martin Parenteau
Martin Parenteau

Reputation: 73731

Strange things happen when processing the click event. If the flag value is set directly in the template, the radio buttons are not checked correctly:

(click)="s = false"

but if the flag is set with a method, then everything works well:

(click)="setValue(false)"

You may prefer to handle the change event, which seems to work correctly all the time:

(change)="s = false"

A better alternative is to use data binding with ngModel, as shown in this stackblitz:

<div class="btn-group" data-toggle="buttons">
  <label class="btn btn-primary" [class.active]="s">
    <input type="radio" name="options" id="option1" [(ngModel)]="s" [value]="true"> Yes
  </label>
  <label class="btn btn-primary" [class.active]="!s">
    <input type="radio" name="options" id="option2" [(ngModel)]="s" [value]="false"> No
  </label>
</div>

Upvotes: 1

BSSchwarzkopf
BSSchwarzkopf

Reputation: 458

changing the attribute must be happening outside of Angular's context so you need to NgZone.run()

https://angular.io/api/core/NgZone

blah() {
    this.zone.run(() => { console.log("boom!"); });
}
<div class="btn-group " data-toggle="buttons">
  <label class="btn btn-primary" [ngClass]="{'active': s}">
    <input type="radio" name="options" id="option1" autocomplete="off" (click)="s=true; blah();" [attr.checked]="s === 'true' ? true : false"> Yes
  </label>
  <label class="btn btn-primary" [ngClass]="{'active': !s}">
    <input type="radio" name="options" id="option2" autocomplete="off"(click)="s=false; blah();" [attr.checked]="s === 'false' ? true : false"> No
  </label>
</div>

Upvotes: 0

Related Questions