创建自定义单选按钮库,但在与反应形式绑定时未检查单选按钮 [英] Created custom radio button library, but did not check radio button while binding with reactive-form

查看:23
本文介绍了创建自定义单选按钮库,但在与反应形式绑定时未检查单选按钮的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

角度库我正在尝试为自定义单选按钮创建一个库,其作用类似于警告(颜色黄点)错误(颜色红点)信息(颜色蓝点)和成功(颜色绿点)而没有角度库它会正常工作,之后我将我的 css 和模板移到库中,但现在预计它不起作用

我提供了来自reactive from的价值,但它没有按预期检查我还在最后添加了我所期待的和我目前得到的截图

radio-button.html

 

radio-button.scss

/* 容器 */.容器 {显示:块;位置:相对;padding-left: 35px;底边距:12px;光标:指针;字体大小:14px;-webkit-user-select:无;-moz-user-select:无;-ms-user-select:无;用户选择:无;}/* 隐藏浏览器的默认单选按钮 */.容器输入{位置:绝对;不透明度:0;光标:指针;}/* 创建一个自定义单选按钮 */.lb-radio {位置:绝对;顶部:0;左:0;高度:20px;宽度:20px;背景色:#eee;边界半径:50%;}.container 输入 ~ .lb-radio--warning {边框:1px 实心暗橙色;}.container 输入 ~ .lb-radio--success {边框:1px纯绿色;}.container 输入 ~ .lb-radio--error {边框:1px纯红色;}.container 输入 ~ .lb-radio--info {边框:1px纯蓝色;}/* 选中单选按钮时,添加蓝色背景 */.container input[type='radio']:checked ~ .lb-radio--warning {背景颜色:深橙色;}.container input[type='radio']:checked ~ .lb-radio--success {背景颜色:绿色;}.container input[type='radio']:checked ~ .lb-radio--error {背景颜色:红色;}.container input[type='radio']:checked ~ .lb-radio--info {背景颜色:蓝色;}/* 创建指标(点/圆圈 - 未选中时隐藏)*/.lb-radio::after {内容: '';位置:绝对;显示:无;}/* 设置指示器样式(点/圆)*/.container .lb-radio::after {顶部:50%;左:50%;变换:翻译(-50%,-50%);宽度:5px;高度:5px;边界半径:50%;背景:白色;}/* 选中时显示指示器(点/圆圈)*/.container input[type='radio']:checked ~ .lb-radio::after {显示:块;}.container input[type='radio']:disabled + span {边框:1px 纯灰色;}.container input[type='radio']:disabled ~ span {背景图像:无;背景颜色:无;颜色:灰色;游标:不允许;}.container input[type='radio']:checked:disabled + span {边框:1px 纯灰色;背景颜色:灰色;}.container input[type='radio']:checked:disabled ~ span {背景图像:无;颜色:灰色;游标:不允许;}

radio-button.ts

import { Component, OnInit, Output, Input, EventEmitter, forwardRef} from '@angular/core';从 '@angular/cdk/coercion' 导入 { coerceBooleanProperty };从@angular/forms"导入 { ControlValueAccessor, NG_VALUE_ACCESSOR};@成分({选择器: 'lb-radio-button, [lb-radio-button]',templateUrl: './radio-button.component.html',styleUrls: ['./radio-button.component.scss'],供应商: [{提供:NG_VALUE_ACCESSOR,useExisting: forwardRef(() => RadioButtonComponent),多:真}]})导出类 RadioButtonComponent 实现 OnInit、ControlValueAccessor {@ViewChild('radioButton') radioButton: ElementRef;className = 'lb-radio';值 = '';isDisabled: 布尔值;@Output() checkBoxClick: EventEmitter= new EventEmitter();@Input() lbRole = '信息';@Input() lbName = 'radioButton';@输入()设置 lbDisabled(值:布尔值){this.isDisabled = coerceBooleanProperty(value);}得到 lbDisabled() {返回 this.isDisabled;}@输入()设置磅值(值:字符串){this.writeValue(value);this.onChange(value);this.onTouched();}获取 lbValue(): 字符串 {返回 this.value;}onChange: any = () =>{};onTouched: any = () =>{};ngOnInit() {this.className += ` lb-radio--${this.lbRole}`;}onClick($event): 布尔 {如果(this.lbDisabled){返回假;}this.writeValue($event.target.value);this.checkBoxClick.emit($event.target.value);}registerOnChange(fn) {this.onChange = fn;}写值(值){如果(值)this.value = 值;}registerOnTouched(fn) {this.onTouched = fn;}}

并使用了库但男性单选按钮未按预期检查

**implementation.html**<form [formGroup]="radioButtonForm"><p lb-radio-button [lbValue]="'Male'" [formControlName]="nameKey.gender" [lbRole]="'success'" [lbName]="'gender'">男性成功单选按钮</p><p lb-radio-button [lbValue]="'Female'" [formControlName]="nameKey.gender" [lbRole]="'info'" [lbName]="'gender'">女性信息单选按钮</p><div lb-button (buttonClick)="onSubmit()" type="submit" lbType="primary">提交表单</div></表单>**实现.ts**导出类 RadioButtonComponent 实现 OnInit {radioButtonForm: FormGroup;名称键 = {性别:'性别',};ngOnInit() {this.radioButtonForm = this.formBuilder.group({性别:['男',Validators.required],});}提交(){警报(JSON.stringify(this.radioButtonForm.value))}}

预期

目前我得到了什么

解决方案

附注

在 Stackblitz 上的代码中,您会找到更完整的代码,其中包含一些验证,例如检查组内是否没有任何 RadioButtonComponent 绑定到 FormControl,或者开发者是否忘记将 LbRadioButtonGroupComponent 绑定到 FormControl.

好吧,这只是避免将相同的 FormControl 绑定到模板中的多个组件的总体思路.它不完整,需要诸如表单验证、表单控件禁用以及通过 @Input() 值 设置组值的能力(我开始在演示中编写它,但我在匆忙,我想你已经明白了).

Angular library I am trying to created a library for custom radio button with its role like warning(color yellow dot) error(color red dot) info(color blue dot) and success(color green dot) without angular library it will work fine, After that I move my css and template to library but now it is not work is expected

I have provide value from reactive from but it does not check as expect I have also added screenshot at the end what I am expecting and what I get currently

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;
      }
}

And used the library but male radio button does not checked as expected

**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))
    }

}

Expected

Currently what I get

解决方案

Stackblitz demo (the demo contains a little bit more information than the basics described in the suggestion below).

Using the same FormControl instance bound to several components is not something I'm comfortable with. If you allow me a suggestion which BTW solves your problem with the initial value not being set in the FormControl: why don't you leverage your component by creating a "RadioGroupControl" just to be used with forms, in such a way that you can associate a FormControl to the group, instead of doing it with individual controls.

What you could do:

1) Create a LbRadioButtonGroupComponent

Its template would be a very simple one:

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

As the selector says, this component is supposed to be used only for grouping radio-buttons inside forms. It should just work as a wrapper.

Inside its typescript, grab all the instances of the RadioButtons.

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

Then subscribe to the event emitter inside each RadioButtonComponent. You'll need to emit more than just a boolean so that LbRadioButtonGroupComponent can set the right radio button checked state to true when you get an incoming value (set by the host with setValue(), patchValue(), or something like that). I suggest a RadioButtonChange property containing 2 attributes: target (the clicked input) and value (a convenience attribute, you can get rid of it if you want to). So we have:

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);
    });
}

Then we would need just a way to set up which button is currently checked. The problem here is just a matter of timing because we don't really know when the method is gonna be called to set a value and we must make sure that our QueryList was already executed and this._radioButtons is populated. Once we make sure that happened the following method should set the right radio button to checked:

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

2) Wrap your RadioButtonComponents with RadioButtonGroupComponent when using them in a form as a group

Once we have these pieces in place, we can use our component like this:

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

As we already own the instances of lb-radio-buttons, there's no need to set the name on them: we can do that in LbRadioButtonGroupComponent. Of course, you can do it in such a way that it doesn't matter if you set the name, or set different names to each radio button (by mistake).

Side notes

In the code on Stackblitz, you'll find a more complete code, containing some validations, like a check to see whether inside the group there aren't any RadioButtonComponent bound to a FormControl, or whether the developer forgot to bind LbRadioButtonGroupComponent to a FormControl.

Well, this is just an overall idea to avoid binding the same FormControl to several components in the template. It's not complete and needs things like form validation, form control disabling, and ability to set the group value via an @Input() value (I started writing it in the demo, but I'm in a rush and I think you've already got the point).

这篇关于创建自定义单选按钮库,但在与反应形式绑定时未检查单选按钮的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆