Three state toggle switch in angular

15,307

Solution 1

really I like the radio-buttons of ng-bootstrap https://ng-bootstrap.github.io/#/components/buttons/examples#radio (well, you can add as style

.btn-primary.focus, .btn-primary:focus {  
   box-shadow: 0 0 0 0; 
}

To avoid the "ugly" shadow in focus)

Make some similar without third-party libraries is play with css. if we has an options like

<div *ngFor="let option of options" style="display:inline"  
    [className]="value==option.value?'active':'noactive'" >
    <input class="radio" type="radio" name="state-d" id="{{option.id}}"
            [(ngModel)]="value" [value]="option.value" />
        <label for="{{option.id}}">
           {{option.id}}
        </label>
</div>

Simply add

[type="radio"].radio {
  border: 0; 
  clip: rect(0 0 0 0); 
  height: 1px; margin: -1px; 
  overflow: hidden; 
  padding: 0; 
  position: absolute; 
  width: 1px;
}
.active{
   color:red;
}
.noactive{
   color:blue;
}

remove the "caret" and give some style to radio buttons. well, make an animation it's more complex. I'll try using Angular Animations defining three states, but I'm not pretty sure how make it

Updated using Angular animations Angulars animation in this case is easy. Just defined three states (I make a very simple example (my options will be a simple array)

An animation is only defined states and how pass to one state to other

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
  animations: [
    trigger('posX',[
      state('uno',style({transform:'translateX(0)'})),
      state('dos',style({transform:'translateX(100px)'})),
      state('tres',style({transform:'translateX(200px)'})),
      transition('* => uno',animate(200)),
      transition('* => dos',animate(200)),
      transition('* => tres',animate(200)),
    ])
  ]
})
export class AppComponent {
  pos:string='uno';
  pos2:string='uno';
  options=['uno','dos','tres'];
}

The animation only say that, when [@posX] was 'uno', no translate, when [@posX] was 'dos' translate 100px to the left and when [@posX] was 'tres' translate 200px

An html will be two divs that are in the same position (I use margin-top:-2rem)

<div style="background-color:transparent;">
  <div *ngFor="let option of options" style="display:inline;" >
    <input class="radio" type="radio" name="state-d" id="{{'l'+option}}"
            [(ngModel)]="pos" [value]="option" />
        <label class="radio" for="{{'l'+option}}">
           {{option}}
        </label>
  </div>
</div>
<div style="overflow:hidden;margin-top:-2rem">
   <div  [@posX]="pos" (@posX.done)="pos2=pos" style="background:blue;width:100px" >
      {{pos2}}
  </div>
</div>

And a css to hide the "caret" and give width to the labels.

[type="radio"].radio {
  border: 0; 
  clip: rect(0 0 0 0); 
  height: 1px; margin: -1px; 
  overflow: hidden; 
  padding: 0; 
  position: absolute; 
  width: 1px;
}
label.radio
{
  width:100px;
}

you can see in the stackblitz

NOTE: Obviously is an ugly example with style in-line and not in .css

Solution 2

You need to bind the values accodingto the selected tab, like this.

The [checked]="value === 'on'" the input will be checked whenever the input value is changed an when the value is changed you can bind back to the value model like this (change)="onSelectionChange('on')". Your form model looks good with no change, but radio button can be treated like this to have them sync.

<form name="form" (ngSubmit)="f.form.valid && onSubmit()" #f="ngForm" novalidate>
                    <div class="form-group">
                        <div class="switch-toggle switch-3 switch-candy">
                            <input id="on" name="state-d" [value]="on"  [checked]="value === 'on'"  type="radio" (change)="onSelectionChange('on')" >
                            <label for="on">ON</label>

                            <input id="na" name="state-d"  [value]="na" [checked]="value === 'na'"  type="radio" checked="checked" (change)="onSelectionChange('na')">
                            <label for="na" >N/A</label>

                            <input id="off" name="state-d"  [value]="off"  [checked]="value === 'off'"  type="radio" (change)="onSelectionChange('off')">
                            <label for="off" >OFF</label>

                            <a></a>
                        </div>
                    </div>
                </form>

Your component

export class AppComponent {
  model: any = {};

  public options = [
    { value: "on", id: "On" },
    { value: "na", id: "NA" },
    { value: "off", id: "Off" },
  ]

  onSubmit() {
    alert('SUCCESS!! :-)\n\n' + JSON.stringify(this.model))
  }

  onSelectionChange(entry) {
    this.value = entry;
  }
}

Demo

EDIT:

added a demo with dynamic inputs,

You need to use the ng-container like this

<form name="form" (ngSubmit)="f.form.valid && onSubmit()" #f="ngForm" novalidate>
                    <div class="form-group">

<div class="switch-toggle switch-3 switch-candy">
<ng-container *ngFor="let option of options" ><input type="radio"
               name="state-d"
               id="state-d"
               [checked]="value === option.value"
               [value]="option.value" 
               />
        <label (click)="onSelectionChange(option.value)"  for="{{option.id}}">{{option.id}}
        </label></ng-container> <a></a>
          </div> </div> </form>

Demo

Solution 3

Here is updated Stackbilz link https://stackblitz.com/edit/angular-qkmkvu HTML code`

<h3>Angular 6 Template-Driven Form Validation</h3>
<form name="form" (ngSubmit)="f.form.valid && onSubmit()" #f="ngForm" novalidate>
  <div class="switch-toggle switch-3 switch-candy">
    <div class="radio" *ngFor="let option of options">
      <input type="radio" name="state-d" id="{{option.id}}" [(ngModel)]="value" [value]="option.value" />
      <label for="{{option.id}}">{{option.id}}
      </label>
    </div>
  </div>
</form>

`

TS code `

export class AppComponent {
  model: any = {};
  value = "na";
  public options = [
    { value: "on", id: "On" },
    { value: "na", id: "NA" },
    { value: "off", id: "Off" }
  ];

  onSubmit() {
    alert("SUCCESS!! :-)\n\n" + JSON.stringify(this.model));
  }
}

`

I just initialise first radio button value and change some styles

Share:
15,307
Maniraj Murugan
Author by

Maniraj Murugan

Proud | 27 | Farmer | Cricketer Born and brought up from evergreen town Karur, Tamilnadu, India. Civil Engineer by graduation and Software Engineer by Profession .. If my answers helped you, feel free to buy a coffee for me You can contact me via email in [email protected] ..

Updated on June 26, 2022

Comments

  • Maniraj Murugan
    Maniraj Murugan almost 2 years

    In my angular 6 application i am in the need to use three state toggle switch which has the order,

    ---ON---NA---OFF---

    For which i have used the following Html,

    <div class="switch-toggle switch-3 switch-candy">
       <input id="on" name="state-d" type="radio" checked="">
       <label for="on" onclick="">ON</label>
    
       <input id="na" name="state-d" type="radio" checked="checked">
       <label for="na" onclick="">N/A</label>
    
       <input id="off" name="state-d" type="radio">
       <label for="off" onclick="">OFF</label>
    
       <a></a>
    </div>
    

    Couldn't share the css file because it has lot of codes..

    Working Example: https://stackblitz.com/edit/angular-6-template-driven-form-validation-z2rsig

    In which you could able to see two times made switch boxes in which the first one is entirely hard coded radio buttons..

    But in second i am trying to make it dynamic but i am unable to get the same UI and value of the selected switch..

    The thing i am in the need is, Need to make three state toggle switch which will have values as On --- NA --- Off. IF user switch to Off then the value of state-d needs to be Off and if its On then the value of state-d needs to be On and likewise for NA. In default the NA needs to be selected (checked)..

    I am need of result as like first one in the link https://stackblitz.com/edit/angular-6-template-driven-form-validation-z2rsig

    But the radio buttons needs to be dynamic as like i am tried in second one in the same link.. (It doesn't works).

    Html:

    <div *ngFor="let option of options" class="switch-toggle switch-3 switch-candy">
            <div class="radio">
                <input type="radio"
                   name="state-d"
                   id="{{option.id}}"
                   [(ngModel)]="value"
                   [value]="option.value"
                   />
            <label for="{{option.id}}">{{option.id}}
            </label>
        </div>
      </div>
    

    Ts:

        public options = [
        {value: "on", id:"On"},
        {value: "na", id:"NA"},
        {value: "off", id:"Off"},
      ]
    

    Also i need to get the value of the current switch.

    Kindly help me to achieve the expected result via dynamic generation of radio buttons and make the NA default value upon switching the buttons make change the values accordingly..

    Result is expected without using any third-party libraries (even angular material not allowed) or jquery.. It is good if there is change in the UI but expected result is in pure angular, typescript/javascript based.

  • Maniraj Murugan
    Maniraj Murugan over 5 years
    I am not sure if i have explained it clearly i am in the need of the result dynamically.. The second one is i tried with but you can give me single result itself.. Whereas the inputs needs to be dynamic which will be from *ngFor="let option of options" and there should not be any hard coded.. In my stackblitz i have tried first with hard coded and then dynamic and in dynamic i have not got expected result.. I am in the need of output from dynamic values and not hard coded..
  • Maniraj Murugan
    Maniraj Murugan over 5 years
    Also help me whether that 500 lines of css (app.component.css) code is needed to make this three state toggle.. Is there any easy way of achieving it?? UI can also be changed which i have mentioned in question..
  • Eliseo
    Eliseo over 5 years
    @Undefined, see the answer. See how write a form with a formArray
  • Enthu
    Enthu about 5 years
    @Eliseo, could please explain (@posX.done) used in html
  • Enthu
    Enthu about 5 years
    @ManirajfromKarur for me ngfor is working and I can see elements appended in dom , however I only see the first uno not the the other, could u help me plz, thanks
  • Eliseo
    Eliseo about 5 years
    @Enthu, when the event has finished we want to make an action -in this case change the variable pos2-
  • Maniraj Murugan
    Maniraj Murugan about 5 years
    @Enthu, Please provide the stackblitz of the things you tried.. I will look into it..