简体   繁体   中英

How to create independent instances of one angular component

I'm trying to create a simple component in Angular that basically maps out an enumeration to a Radio button input. I got that set up and working, but I'm running into some weird issues when I put multiple instances of that component on a single page. What I'm seeing is that both copies of the component react to each other.

This is for a project using Angular 8. I've broken the code out into a small project hosted here so you can see what's happening. The hosting component is app.component.html/.ts and the radio button component is radio.component.html/.ts.

https://stackblitz.com/edit/angular-rcurne

Editing to add some actual code here

app.component.html

<app-radio
  [radioSelection]="selectionRadio1"
  (radioSelectionChange)="selectionRadio1 = $event"
></app-radio>
<label>Radio Option Selected: {{ selectionRadio1.toString() }}</label>
<br />
<br />
<app-radio
  [radioSelection]="selectionRadio2"
  (radioSelectionChange)="selectionRadio2 = $event"
></app-radio>
<label>Radio Option Selected: {{ selectionRadio1.toString() }}</label>

radio.component.html

<div class="form-check">
  <input
    class="form-check-input"
    type="radio"
    name="radioInput"
    id="radioInput1"
    [value]="radioSelectionType.Option1"
    [(ngModel)]="radioSelection"
    (ngModelChange)="radioSelectionChangedByUser($event)"
  />
  <label class="form-check-label" for="radioInput1">Option 1</label>
</div>
<div class="form-check">
  <input
    class="form-check-input"
    type="radio"
    name="radioInput"
    id="radioInput2"
    [value]="radioSelectionType.Option2"
    [(ngModel)]="radioSelection"
    (ngModelChange)="radioSelectionChangedByUser($event)"
  />
  <label class="form-check-label" for="radioInput2">Option 2</label>
</div>
<div class="form-check">
  <input
    class="form-check-input"
    type="radio"
    name="radioInput"
    id="radioInput3"
    [value]="radioSelectionType.Both"
    [(ngModel)]="radioSelection"
    (ngModelChange)="radioSelectionChangedByUser($event)"
  />
  <label class="form-check-label" for="radioInput3">Both</label>
</div>

radio.component.ts

import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { RadioSelectionType } from './RadioSelectionType'

@Component({
  selector: 'app-radio',
  templateUrl: './radio.component.html'
})
export class RadioComponent implements OnInit {
  @Input() radioSelection: RadioSelectionType;
  @Output() radioSelectionChange = new EventEmitter<RadioSelectionType>();
  public radioSelectionType = RadioSelectionType;

  constructor() { }

  ngOnInit() {
    // If no default was specified via the input, default to Option 3
    if (!this.radioSelection) {
      this.radioSelection = RadioSelectionType.Option3;
    }
  }

  radioSelectionChangedByUser(value: RadioSelectionType) {
    this.radioSelectionChange.emit(this.radioSelection);
  }

}

As mentioned above, I'm seeing the radio inputs react to each other even though they are separate components. Feel free to criticize my coding choices as well - I'm a C# developer that has recently started using Angular so I'm likely missing something about how this works on the web.

Radio group should have unique name. In your code, the all radio button has same name.

Solution: Passing the name as an input property will solve the problem

  ...
  export class RadioComponent implements OnInit {
     @Input() name: string;
     ...

template:

...
<div class="form-check">
  <input
    class="form-check-input"
    type="radio"
    [name]="name"
...

// app.component.html

<h1>Hello, world!</h1>
<app-radio
name="r1"
  [radioSelection]="selectionRadio1"
  (radioSelectionChange)="selectionRadio1 = $event"
></app-radio>
<label>Radio Option Selected: {{ selectionRadio1.toString() }}</label>
<br />
<br />
<app-radio
name="r2"
  [radioSelection]="selectionRadio2"
  (radioSelectionChange)="selectionRadio2 = $event"
></app-radio>
<label>Radio Option Selected: {{ selectionRadio2.toString() }}</label>

https://stackblitz.com/edit/angular-bxp4my

Here is a working example based on your code: https://angular-4dsvdv.stackblitz.io

The component instances are independent: one component instance can't see the variables of the other component instance of the same type. In this scenario you violated the W3C Rules for RadioButton groups. You create a radio button group by giving the belonging radio buttons the same name attribute value. If you associate a label to a radio button, the label 'for' attribute must contain the id of the associated radio button. Ids must be unique within the DOM tree.

If you have two instances of your component on your page, the HTML snippet of your radio component will be duplicated in the DOM tree of your page. Both radio button groups have the same group name and the ids of your radio buttons are not unique. You can solve this by providing a unique button group name as input value (or create it dynamically within the component) and creating unique radio button ids dynamically. Also the 'for' attribute of the labels need to point to these dynamic ids.

The DOM should look like this: enter image description here

I was also able to solve this by adding a <form> tag that wrapped around my component like so:

<form>
  <div class="form-check">
    <input
      class="form-check-input"
      type="radio"
      name="radioInput"
      id="radioInput1"
      [value]="radioSelectionType.Option1"
      [(ngModel)]="radioSelection"
      (ngModelChange)="radioSelectionChangedByUser($event)"
    />
    <label class="form-check-label" for="radioInput1">Option 1</label>
  </div>
  <div class="form-check">
    <input
      class="form-check-input"
      type="radio"
      name="radioInput"
      id="radioInput2"
      [value]="radioSelectionType.Option2"
      [(ngModel)]="radioSelection"
      (ngModelChange)="radioSelectionChangedByUser($event)"
    />
    <label class="form-check-label" for="radioInput2">Option 2</label>
  </div>
  <div class="form-check">
    <input
      class="form-check-input"
      type="radio"
      name="radioInput"
      id="radioInput3"
      [value]="radioSelectionType.Both"
      [(ngModel)]="radioSelection"
      (ngModelChange)="radioSelectionChangedByUser($event)"
    />
    <label class="form-check-label" for="radioInput3">Both</label>
  </div>
</form>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM