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

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

问题描述

角库我正在尝试为自定义单选按钮创建一个库,其作用是警告(颜色黄点)错误(颜色红点)信息(颜色蓝点)和成功(颜色绿点)而没有角度库,它将正常工作,之后我将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

 /*容器*/.容器 {显示:块;职位:相对padding-left:35px;底边距:12px;光标:指针;font-size:14px;-webkit-user-select:无;-moz-user-select:无;-ms-user-select:无;用户选择:无;}/*隐藏浏览器的默认单选按钮*/.container输入{位置:绝对;不透明度:0;光标:指针;}/*创建一个自定义单选按钮*/.lb-radio {位置:绝对;最高:0;左:0;高度:20px;宽度:20px;背景颜色:#eee;边界半径:50%;}.container输入〜.lb-radio--警告{边框:1px的深橙色;}.container输入〜.lb-radio--成功{边框:1px纯绿色;}.container输入〜.lb-radio--错误{边框:1px纯红色;}.container输入〜.lb-radio--info {边框:1px纯蓝色;}/*选中单选按钮时,添加蓝色背景*/.container input [type ='radio']:已选中〜.lb-radio--warning {背景色:深橙色;}.container input [type ='radio']:选中了〜.lb-radio--成功{背景颜色:绿色;}.container input [type ='radio']:checked〜.lb-radio--error {背景颜色:红色;}.container input [type ='radio']:checked〜.lb-radio--info {背景颜色:蓝色;}/*创建指示器(点/圆-未选中时隐藏)*/.lb-radio ::之后{内容: '';位置:绝对;显示:无;}/*设置指示器的样式(点/圆)*/.container .lb-radio :: after {最高:50%;左:50%;转换:translate(-50%,-50%);宽度:5像素;高度:5像素;边界半径:50%;背景:白色;}/*选中时显示指示符(点/圆圈)*/.container input [type ='radio']:checked〜.lb-radio :: after {显示:块;}.container input [type ='radio']:已禁用+ span {边框:1px纯灰色;}.container input [type ='radio']:disabled〜span {背景图片:无;背景颜色:无;颜色:灰色;游标:不允许;}.container输入[type ='radio']:已选中:已禁用+ span {边框:1px纯灰色;背景颜色:灰色;}.container input [type ='radio']:已选中:已禁用〜span {背景图片:无;颜色:灰色;游标:不允许;} 

radio-button.ts

  import'@ angular/core'中的{组件,OnInit,输出,输入,EventEmitter,forwardRef};从'@ 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< boolean>= new EventEmitter< boolean>();@Input()lbRole ='信息';@Input()lbName ='radioButton';@输入()设置lbDisabled(value:boolean){this.isDisabled = coerceBooleanProperty(value);}得到lbDisabled(){返回this.isDisabled;}@输入()设置lbValue(value:字符串){this.writeValue(value);this.onChange(value);this.onTouched();}获取lbValue():字符串{返回this.value;}onChange:任意=()=>{};onTouched:任意=()=>{};ngOnInit(){this.className + =`lb-radio-$ {this.lbRole}`;}onClick($ event):布尔值{如果(this.lbDisabled){返回false;}this.writeValue($ event.target.value);this.checkBoxClick.emit($ event.target.value);}registerOnChange(fn){this.onChange = fn;}writeValue(value){如果(值)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"> Submit Form</div></form>** implement.ts **导出类RadioButtonComponent实现OnInit {radioButtonForm:FormGroup;nameKey = {性别:性别",};ngOnInit(){this.radioButtonForm = this.formBuilder.group({性别:['男',需要验证者],});}onSubmit(){警报(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天全站免登陆