创建了自定义单选按钮库,但在与反应式表单绑定时没有检查单选按钮。

问题描述 投票:0回答:1

Angular库我试图为自定义单选按钮创建一个库,它的作用是警告(黄色点)错误(红色点)信息(蓝色点)和成功(绿色点),没有Angular库就能正常工作,之后我把我的css和模板移到库里,但现在它不能正常工作了。

我有提供价值从反应从,但它不检查作为预期,我也添加了截图,在最后我期待什么,我得到什么,目前

radio-button.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>

radio-button.scss

/* 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

angular angular-reactive-forms reactive-forms angular-library
1个回答
2
投票

Stackblitz演示 演示包含的信息比下面建议中描述的基础知识更多一些)。

使用相同的 FormControl 实例绑定到多个组件上,这让我很不舒服。如果你允许我提出一个建议,另外 解决了你的问题,即初始值没有在 FormControl说明:你为什么不通过创建一个 "RadioGroupControl "来利用你的组件,只是为了与表单一起使用,这样你就可以将一个 "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) 包裹你的 RadioButtonComponentRadioButtonGroupComponent 当在一个表格中把它们作为一组使用时

一旦我们有了这些部件,我们就可以像这样使用我们的组件。

<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的,不需要在它们上设置名称:我们可以在 LbRadioButtonGroupComponent. 当然,你可以这样做,如果你设置了名称,或者给每个单选按钮设置了不同的名称(误),都没有关系。

enter image description here

附注

在Stackblitz上的代码中,你会发现一个更完整的代码,包含一些验证,比如检查组内是否没有任何的 RadioButtonComponent 拘泥于 FormControl或开发者是否忘记绑定 LbRadioButtonGroupComponentFormControl.

好吧,这只是一个整体的想法,以避免绑定相同的。FormControl 到模板中的几个组件。它并不完整,还需要诸如表单验证、表单控制禁用,以及能够通过一个 @Input() value (我在演示中就开始写了,但我很急,我想你已经明白了)。


1
投票

不要在lbValue setter里面调用writeValue,它会覆盖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);
    }

例子

© www.soinside.com 2019 - 2024. All rights reserved.