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>
单选按钮.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;
}
单选按钮.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))
}
}
预期的
目前我得到的
解决方案
Stackblitz 演示(该演示包含比以下建议中描述的基础知识更多的信息)。
使用FormControl
绑定到多个组件的同一个实例并不是我喜欢的。如果您允许我提出一个建议,顺便说一句,您可以通过以下方式解决您的问题FormControl
:您为什么不通过创建一个仅用于表单的“RadioGroupControl”来利用您的组件,以便您可以关联aFormControl
到组,而不是使用单独的控件进行操作。
你可以做什么:
1) 创建一个LbRadioButtonGroupComponent
它的模板将是一个非常简单的模板:
selector: 'lb-radio-group-control',
template: '<ng-content></ng-content>'
正如选择器所说,该组件应该仅用于对表单内的单选按钮进行分组。它应该只是一个包装器。
在其打字稿中,获取 RadioButtons 的所有实例。
@ContentChildren(RadioButtonComponent, { descendants: true })
_radioButtons: QueryList<RadioButtonComponent>;
然后订阅每个内部的事件发射器RadioButtonComponent
。您需要发出的不仅仅是一个布尔值,以便可以在您获得传入值时LbRadioButtonGroupComponent
设置正确的单选按钮checked
状态(由主机使用,或类似的东西设置)。我建议一个包含 2 个属性的属性:(clicked )和(一个方便的属性,如果你愿意,你可以去掉它)。所以我们有:true
setValue()
patchValue()
RadioButtonChange
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
包装起来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
. 当然,您可以通过设置名称或为每个单选按钮(错误地)设置不同名称的方式来执行此操作。
旁注
在 Stackblitz 上的代码中,您会发现更完整的代码,其中包含一些验证,例如检查组内是否没有RadioButtonComponent
绑定到 a FormControl
,或者开发人员是否忘记绑定LbRadioButtonGroupComponent
到 a FormControl
。
好吧,这只是避免将其绑定FormControl
到模板中的多个组件的总体思路。它不完整,需要表单验证、禁用表单控件以及通过 an 设置组值的能力@Input() value
(我开始在演示中编写它,但我很着急,我想你已经明白了) .
推荐阅读
- ruby-on-rails - 从不同域发送电子邮件
- bootstrap-4 - 列没有在移动视图或小屏幕中调整,它们正在引导程序 4 中移动到下一行
- visual-studio-code - 如何在 VSCode 中更改编辑器缩进的颜色
- node.js - 在 Heroku 上上传图像的替代方法
- typescript - 将 Gatsby 与 Typescript 一起使用时,Markdown 图像仍然模糊,我做错了什么?
- salt-stack - 渲染 SLS 'base:test.test1' 失败:Jinja 变量 'list object' 没有属性 'sub1'
- powershell - 使用 powershell 脚本自动登录 SAP GUI
- amazon-web-services - 如何使用 AWS WAF 阻止“user-agent”标头不是“Java/1.8”的请求?
- android-studio - Android UI 故障问题
- database - 我们可以在同一个模式中有两个 Postgres 数据库吗?