Почему мой элемент радиовхода внутри пользовательского компонента не работает должным образом с реактивными формами? - PullRequest
1 голос
/ 04 апреля 2020

Я пытаюсь написать компонент, который предоставляет повторно используемое поле ввода радио. Я создал очень простой пример в StackBlitz , который, я надеюсь, передает то, что я пытаюсь сделать. Это простое приложение не работает, как ожидалось, и я еще не понял, почему. Буду любить чужие глаза на это.

Настраиваемый радиокомпонент html

<div [formGroup]="frm">
  <label [for]="id">
  <input [id]="id" type="radio" [value]="value" [formControlName]="cn">{{label}}
  </label>
</div>

Настраиваемая машинопись радиокомпонента

import { Component, Input } from "@angular/core";
import { FormGroup } from "@angular/forms";

@Component({
  selector: 'app-radio',
  templateUrl: './radio.component.html',
  styleUrls: ['./radio.component.css']
})
export class RadioComponent {
  @Input() frm: FormGroup;
  @Input() label: string;
  @Input() cn: string;
  @Input() value: string;
  @Input() id: string;
}

HTML, где используется пользовательский радиокомпонент

<form [formGroup]="frm">
  <app-radio id="rc1" [frm]="frm" label="Yes" value="Yes" cn="question" ></app-radio>
  <app-radio id="rc2" [frm]="frm" label="No" value="No" cn="question"></app-radio>
</form>
<hr>
<h3>Form Data</h3>
<pre>{{frm.value | json}}

Машинопись для html с использованием пользовательского радиокомпонента

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit  {
  frm: FormGroup;

  constructor(private fb: FormBuilder){}

  ngOnInit() {
     this.frm = this.fb.group({
    question: ['Yes', Validators.required]
  });
  }
}

Ожидаемое поведение

  1. Ожидается следующий начальный экран:

    Expected Result

  2. Когда я нажимаю на радио Нет после начального экрана радио Да отключается, а радио Нет проверяется.
  3. Когда я нажимаю на радио Нет после начального экрана, данные формы показывают, что свойство вопроса имеет значение " Нет ".
  4. При последующих щелчках параметра снимается флажок другого параметра и проверяется выбранный параметр.
  5. При последующих щелчках параметра изменяется значение формы для поля вопроса.

Фактическое поведение

  1. Успех
  2. Ошибка. Обе опции отмечены.
  3. Успех
  4. Ошибка. Ничего не происходит, когда я нажимаю на любую из опций.
  5. Ошибка. Кажется, ничего не происходит, когда я нажимаю на любую из этих опций.

Другие наблюдения Я был удивлен, что не нашел атрибуты value и name в результирующих элементах ввода, как видно на снимке экрана, который я сделал из Chrome Dev Tool:

enter image description here

Я был бы очень признателен, если бы кто-нибудь мог мне помочь. Спасибо.

Ответы [ 3 ]

2 голосов
/ 04 апреля 2020

Вот обновленный StackBlitz , который, как я считаю, соответствует вашим ожиданиям в отношении поведения. Я добавил [checked]="frm.get(cn).value === value".

radio.component. html

<div [formGroup]="frm">
  <label [for]="id">
    <input [id]="id" type="radio" [value]="value" [formControlName]="cn" [checked]="frm.get(cn).value === value">{{label}}
  </label>
</div>

Я все еще копаюсь сам, но подозреваю проблема в том, что внешняя директива formGroup в app.component.html не может найти вложенные радиовходы, созданные RadioComponent.

2 голосов
/ 06 апреля 2020

Почему мой образец Stackblitz не работал?

Ответ о том, почему мой образец Stackblitz не работает должным образом, находится в исходном коде RadioControlRegistry (packages / forms / src /). directives / radio_control_value_accessor.ts).

  select(accessor: RadioControlValueAccessor) {
    this._accessors.forEach((c) => {
      if (this._isSameGroup(c, accessor) && c[1] !== accessor) {
        c[1].fireUncheck(accessor.value);
      }
    });
  }

  private _isSameGroup(
      controlPair: [NgControl, RadioControlValueAccessor],
      accessor: RadioControlValueAccessor): boolean {
    if (!controlPair[0].control) return false;
    return controlPair[0]._parent === accessor._control._parent &&
        controlPair[1].name === accessor.name;
  }

Когда вы нажимаете на неконтролируемый элемент управления радио, вызывается эта функция выбора. Он будет вызывать fireUncheck для других радиоуправлений с таким же свойством имени И с тем же родителем.

Эта последняя часть - проблема. Каждый пользовательский элемент управления радио имеет своего родителя. Родитель здесь относится к экземпляру FormGroupDirective. Каждый пользовательский элемент управления радио обернут в div, который получает новый экземпляр FormGroupDirective. FireUncheck никогда не вызывается.

Решение:

Если бы я как-то мог сделать родителей равными в этом сценарии, это сработало бы.

Я обнаружил, что могу использовать директиву formControl вместо директивы formControlName в радиокомпоненте.

Я создал модифицированную версию образца Stackblitz . Я заменил директивы formGroup и formControlName на директиву formControl. Работает как брелок.

Вот модифицированный код:

Пользовательский компонент радио html

<div>
  <label [for]="id">
  <input [id]="id" type="radio" [value]="value" [formControl]="ctrl">{{label}}
  </label>
</div>

Пользовательское радио машинописный текст

import { Component, Input } from "@angular/core";
import { FormControl } from "@angular/forms";

@Component({
  selector: 'app-radio',
  templateUrl: './radio.component.html',
  styleUrls: ['./radio.component.css']
})
export class RadioComponent {
  @Input() ctrl: FormControl;
  @Input() label: string;
  @Input() value: string;
  @Input() id: string;
}

HTML, где используется пользовательский радиокомпонент

<form [formGroup]="frm">
  <app-radio id="rc1" label="Yes" value="Yes" [ctrl]="frm.get('question')" ></app-radio>
  <app-radio id="rc2" label="No" value="No" [ctrl]="frm.get('question')"></app-radio>
</form>
<hr>
<h3>Form Data</h3>
<pre>{{frm.value | json}}

машинописный текст для html с использованием пользовательского радиокомпонент

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit  {
  frm: FormGroup;

  constructor(private fb: FormBuilder){}

  ngOnInit() {
     this.frm = this.fb.group({
    question: ['Yes', Validators.required]
  });
  }
}
1 голос
/ 04 апреля 2020

Я думаю, что здесь происходит то, что ваши две кнопки радио не связаны. Я только что попробовал ваш пример Stackblitz, и для меня начальное значение Да, и когда я нажимаю Нет, оба проверяются.

Насколько я знаю, переключатели должны иметь одно и то же имя собственность должна быть подключена, как это также задокументировано школами W3 . Вы можете попробовать следующее, которое использует имя question :

<form [formGroup]="frm">
  <app-radio id="rc1" name="question" [frm]="frm" label="Yes" value="Yes" cn="question" ></app-radio>
  <app-radio id="rc2" name="question" [frm]="frm" label="No" value="No" cn="question"></app-radio>
</form>

Если честно: я не совсем понимаю, чего вы пытаетесь достичь с помощью пользовательского компонента здесь. Вы также можете go с родными переключателями:

<form [formGroup]="frm">
  <input type="radio" id="rc1" name="question" label="Yes" value="Yes" cn="question" >
  <input type="radio" id="rc2" name="question" label="No" value="No" cn="question">
</form>

Примечание: я удалил [frm] = "frm" здесь.

И если вы нужен пользовательский CSS, вы также можете перезаписать это:

input {
    color: red;
}

РЕДАКТИРОВАТЬ: Использование RadioControlValueAccessor

Официальная Angular документация показывает пример использования radioButton внутри реактивной формы, аналогичной тому, что вы делаете (за исключением пользовательского компонента, обертывающего поле ввода radioButton).

<form [formGroup]="form">
    <input type="radio" formControlName="food" value="beef" > Beef
    <input type="radio" formControlName="food" value="lamb"> Lamb
    <input type="radio" formControlName="food" value="fish"> Fish
</form>

Здесь все они используют одно и то же formControlName food , чтобы связать радио кнопки. В вашем вопросе это будет вопрос , соответствующий вашему формуляру управления.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...