Создал собственную библиотеку переключателей, но не проверял переключатель при привязке с реактивной формой - PullRequest
1 голос
/ 17 июня 2020
Библиотека

Angular Я пытаюсь создать библиотеку для настраиваемого переключателя с такой ролью, как предупреждение (цветная желтая точка), ошибка (цветная красная точка), информация (цветная синяя точка) и успех (цветная зеленая точка) без angular библиотека, она будет работать нормально, после этого я перемещаю свой css и шаблон в библиотеку, но теперь это не работает, ожидается

Я предоставил значение из реактивного из, но он не проверяет, как ожидалось, у меня также в конце добавлен снимок экрана, чего я ожидаю и что получаю сейчас

радио-кнопка. html

    <label class="container" >
        <input type="radio" #radioButton [name]="lbName" (click)="onClick($event)" [value]="lbValue"  
                                              [disabled]="lbDisabled" />
        <span [class]="className"></span>
        <span>
            <ng-content></ng-content>
        </span>
    </label>

радио-кнопка. css

/* The container */
.container {
    display: block;
    position: relative;
    padding-left: 35px;
    margin-bottom: 12px;
    cursor: pointer;
    font-size: 14px;
    -webkit-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}

/* Hide the browser's default radio button */
.container input {
    position: absolute;
    opacity: 0;
    cursor: pointer;
}

/* Create a custom radio button */
.lb-radio {
    position: absolute;
    top: 0;
    left: 0;
    height: 20px;
    width: 20px;
    background-color: #eee;
    border-radius: 50%;
}

.container input ~ .lb-radio--warning {
    border: 1px solid darkorange;
}

.container input ~ .lb-radio--success {
    border: 1px solid green;
}

.container input ~ .lb-radio--error {
    border: 1px solid red;
}

.container input ~ .lb-radio--info {
    border: 1px solid blue;
}

/* When the radio button is checked, add a blue background */
.container input[type='radio']:checked ~ .lb-radio--warning {
    background-color: darkorange;
}

.container input[type='radio']:checked ~ .lb-radio--success {
    background-color: green;
}

.container input[type='radio']:checked ~ .lb-radio--error {
    background-color: red;
}

.container input[type='radio']:checked ~ .lb-radio--info {
    background-color: blue;
}

/* Create the indicator (the dot/circle - hidden when not checked) */
.lb-radio::after {
    content: '';
    position: absolute;
    display: none;
}

/* Style the indicator (dot/circle) */
.container .lb-radio::after {
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background: white;
}

/* Show the indicator (dot/circle) when checked */
.container input[type='radio']:checked ~ .lb-radio::after {
    display: block;
}

.container input[type='radio']:disabled + span {
    border: 1px solid grey;
}

.container input[type='radio']:disabled ~ span {
    background-image: none;
    background-color: none;
    color: grey;
    cursor: not-allowed;
}

.container input[type='radio']:checked:disabled + span {
    border: 1px solid grey;
    background-color: grey;
}

.container input[type='radio']:checked:disabled ~ span {
    background-image: none;
    color: grey;
    cursor: not-allowed;
}

radio-button.ts

import { Component, OnInit, Output, Input, EventEmitter, forwardRef} from '@angular/core';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';

@Component({
    selector: 'lb-radio-button, [lb-radio-button]',
    templateUrl: './radio-button.component.html',
    styleUrls: ['./radio-button.component.scss'],
    providers: [
        {
            provide: NG_VALUE_ACCESSOR,
            useExisting: forwardRef(() => RadioButtonComponent),
            multi: true
        }
    ]
})
export class RadioButtonComponent implements OnInit,  ControlValueAccessor {
    @ViewChild('radioButton') radioButton: ElementRef;
    className = 'lb-radio';
    value = '';
    isDisabled: boolean;
    @Output() checkBoxClick: EventEmitter<boolean> = new EventEmitter<boolean>();

    @Input() lbRole = 'info';
    @Input() lbName = 'radioButton';

    @Input()
    set lbDisabled(value: boolean) {
        this.isDisabled = coerceBooleanProperty(value);
    }
    get lbDisabled() {
        return this.isDisabled;
    }

    @Input()
    set lbValue(value: string) {
        this.writeValue(value);
        this.onChange(value);
        this.onTouched();
    }

    get lbValue(): string {
        return this.value;
    }

    onChange: any = () => {};
    onTouched: any = () => {};

    ngOnInit() {
        this.className += ` lb-radio--${this.lbRole}`;
    }

    onClick($event): boolean {
        if (this.lbDisabled) {
            return false;
        }
        this.writeValue($event.target.value);
        this.checkBoxClick.emit($event.target.value);
    }

    registerOnChange(fn) {
        this.onChange = fn;
      }

      writeValue(value) {
        if (value)
          this.value = value;
      }

      registerOnTouched(fn) {
        this.onTouched = fn;
      }
}

И использовал библиотеку, но мужская радиокнопка не проверяется должным образом

**implementation.html**

<form [formGroup]="radioButtonForm">
    <p lb-radio-button [lbValue]="'Male'" [formControlName]="nameKey.gender" [lbRole]="'success'" [lbName]="'gender'">
       Male success radio button
    </p>
    <p lb-radio-button [lbValue]="'Female'" [formControlName]="nameKey.gender" [lbRole]="'info'" [lbName]="'gender'">
       Female info radio button
    </p>
    <div lb-button (buttonClick)="onSubmit()" type="submit" lbType="primary">Submit Form</div>
</form>

**implementation.ts**

export class RadioButtonComponent implements OnInit {
    radioButtonForm: FormGroup;

    nameKey =  {
        gender: 'gender',
    };

    ngOnInit() {
        this.radioButtonForm = this.formBuilder.group({
            gender: ['Male', Validators.required],
        });
    }

    onSubmit() {
        alert(JSON.stringify(this.radioButtonForm.value))
    }

}

Ожидается

enter image description here

Что я получаю сейчас

enter image description here

Ответы [ 2 ]

2 голосов
/ 17 июня 2020

Демо Stackblitz (демо содержит немного больше информации, чем основы, описанные в предложении ниже).

Использование одного и того же экземпляра FormControl, привязанного к нескольким компонентам, не является чем-то особенным Мне комфортно. Если вы разрешите мне предложение, которое BTW решает вашу проблему с начальным значением, не установленным в FormControl: почему бы вам не использовать свой компонент, создав «RadioGroupControl» только для использования с формами , таким образом, чтобы вы могли связать FormControl с группой, вместо того, чтобы делать это с отдельными элементами управления.

Что вы могли бы сделать:

1) Создайте LbRadioButtonGroupComponent

Его шаблон был бы очень простым:

selector: 'lb-radio-group-control',
template: '<ng-content></ng-content>'

Как говорит селектор, этот компонент предполагается использовать только для группировки радиокнопок внутри форм. Он должен работать просто как оболочка.

Внутри его машинописного текста возьмите все экземпляры RadioButtons.

@ContentChildren(RadioButtonComponent, { descendants: true })
_radioButtons: QueryList<RadioButtonComponent>;

Затем подпишитесь на эмиттер событий внутри каждого RadioButtonComponent. Вам нужно будет передать больше, чем просто логическое значение, чтобы LbRadioButtonGroupComponent мог установить для правого переключателя checked состояние true, когда вы получаете входящее значение (устанавливается хостом с помощью setValue(), patchValue(), или что-то вроде того). Я предлагаю свойство RadioButtonChange, содержащее 2 атрибута: target (нажатый input) и value (удобный атрибут, вы можете избавиться от него, если хотите). Итак, у нас есть:

ngAfterViewInit() {
  const observableList: Observable<RadioButtonChange>[] = [];
  this._radioButtons.map((rb: RadioButtonComponent) => 
    observableList.push(rb.checkBoxClick));

  merge(...observableList)
    .pipe(
      filter(Boolean),
      takeUntil(this._destroy$)
    )
    .subscribe((value: any) => {
      this._onTouch();
      this._onChange(value ? value.value : null);
    });
}

Тогда нам понадобится просто способ установить, какая кнопка в данный момент отмечена. Проблема здесь просто в сроках, потому что мы действительно не знаем, когда метод будет вызван для установки значения, и мы должны убедиться, что наш QueryList уже был выполнен, а this._radioButtons заполнен. Как только мы убедимся, что это произошло, следующий метод должен установить правый переключатель на checked:

private _setGroupValue(value: any) {
  this._radioButtons.forEach(
    rb => (rb.radioButton.nativeElement.checked =
        rb.radioButton.nativeElement.value === value)
  );
}

2) Оберните RadioButtonComponent s с RadioButtonGroupComponent при использовании их в форме в качестве группы

После того, как у нас есть эти части, мы можем использовать наш компонент следующим образом:

<form [formGroup]="radioButtonForm">
  <lb-radio-group-control formControlName="gender">
    <p lb-radio-button [lbValue]="'Male'" 
                       [lbRole]="'success'">
      Male success radio button
    </p>
    <p lb-radio-button [lbValue]="'Female'" 
                       [lbRole]="'info'">
      Female info radio button
    </p>
  </lb-radio-group>
  <div lb-button (buttonClick)="onSubmit()" 
                 type="submit" 
                 lbType="primary">Submit Form</div>
</form>

Поскольку у нас уже есть экземпляры lb-radio-button s, нет необходимости устанавливать имя на них: мы можем сделать это в LbRadioButtonGroupComponent. Конечно, вы можете сделать это таким образом, что не имеет значения, устанавливаете ли вы имя или устанавливаете разные имена для каждой радиокнопки (по ошибке).

enter image description here

Боковые примечания

В коде на Stackblitz вы найдете более полный код, содержащий некоторые проверки, такие как проверка, чтобы увидеть, нет ли внутри группы никаких RadioButtonComponent привязан к FormControl, или разработчик забыл привязать LbRadioButtonGroupComponent к FormControl.

Ну, это всего лишь общая идея, чтобы избежать привязки одного и того же FormControl к нескольким компонентам в шаблон. Он не полный и требует таких вещей, как проверка формы, отключение элемента управления формой и возможность установить значение группы с помощью @Input() value (я начал писать его в демонстрации, но я нахожусь в ru sh, и я думаю, что вы '' я уже понял суть).

1 голос
/ 17 июня 2020

Не вызывайте writeValue внутри установщика lbValue, он переопределит значение, установленное FormControl.

@Input lbValue;

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

 onClick($event): boolean {
        if (this.lbDisabled) {
            return false;
        }
        this.onChange($event.target.value);
        this.onTouched();
        this.checkBoxClick.emit($event.target.value);
    }

Пример

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