Angular 2 ControlValueAccessor用于自定义复选框,如何更新从内到外的值?
我正在尝试使用bootstrap4checkbox布局实现checkbox的自定义组件 我遵循此链接来实现自定义组件 我成功地完成了无线电,但我将输入作为ID数组,输出将是基于选择的ID数组 复选框是根据JS模型实例列表创建的Angular 2 ControlValueAccessor用于自定义复选框,如何更新从内到外的值?,angular,custom-component,Angular,Custom Component,我正在尝试使用bootstrap4checkbox布局实现checkbox的自定义组件 我遵循此链接来实现自定义组件 我成功地完成了无线电,但我将输入作为ID数组,输出将是基于选择的ID数组 复选框是根据JS模型实例列表创建的 export class InputProp<T>{ displayName?:String; order?:Number; constructor( public checked:boolean=false, public va
export class InputProp<T>{
displayName?:String;
order?:Number;
constructor( public checked:boolean=false,
public value:String,
public id:T)
{}
导出类InputProp{
displayName?:字符串;
订单?:编号;
构造函数(公共选中:布尔=false,
公共值:字符串,
公共id:T)
{}
}
但有一件事我不明白,angular 2如何读取内部值并更新外部模式
有一个writevalue方法读取外部组件模型值
有像readValue这样的方法吗
源代码
checkbox-button.component.ts
import { InputProp } from './../../models/common.classes';
import { TitleCasePipe } from './../../../pipes/title-case.pipe';
import {
FormGroup,
FormControl,
FormBuilder,
ControlValueAccessor,
NG_VALUE_ACCESSOR,
NG_VALIDATORS,
ValidatorFn
} from '@angular/forms';
// Framework
import { Input,
Output,
Component,
OnInit,
OnDestroy,
forwardRef,
EventEmitter } from "@angular/core";
const noop = () => {
};
export const CUSTOM_INPUT_CONTROL_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => CheckBoxButton),
multi: true
};
@Component({
selector: 'checkbox-button',
styleUrls:['./../buttons.component.scss'],
templateUrl:'./checkbox-button.component.html',
providers:[TitleCasePipe]
})
export class CheckBoxButton implements OnInit, OnDestroy, ControlValueAccessor {
private titleCase:TitleCasePipe=new TitleCasePipe();
//(ngModelChange)="CbaCheckBoxButtonChange($event)"
@Input('label')
private labelName:String;
@Input('list')
private compList:InputProp<Number|String>[];
@Input('isenum')
private isEnumBaseCheckbox:boolean=false;
@Input('enumlist')
private enumComp:any;
@Input('isupcase')
private isUpperCase:boolean=false;
@Input('sortdir')
private sortDir:String='asc';
@Input('labeldir')
private isLabelDisplayDir:String='top';
@Output() onCheckBoxChangeNotify: EventEmitter<(Number|String)[]>;
private prevSelectedVals:(Number|String)[];
private innerVal: any[];
private orderBy:any;
constructor() {
this.onCheckBoxChangeNotify = new EventEmitter<(Number|String)[]>();
}
ngOnInit() {
this.orderBy={fields:['order'],sortDir:this.sortDir};
//console.debug('CbaCheckBoxButton::ngOnInit: ',this.compList,this.labelName);
}
ngOnDestroy(){
//console.debug('CbaCheckBoxButton::ngOnDestory: ');
}
//Placeholders for the callbacks which are later providesd
//by the Control Value Accessor
private onTouchedCallback: () => void = noop;
private onChangeCallback: (_: any) => void = noop;
//get accessor
get selectedVal(): any {
return this.innerVal;
};
// //set accessor including call the onchange callback
// set selectedVal(v: any) {
// if (v !== this.innerVal) {
// this.innerVal = v;
// this.onChangeCallback(v);
// }
// }
//From ControlValueAccessor interface
writeValue(value: any) {
if (value !== this.innerVal) {
this.innerVal = value;
this.compList.map(f=>f.checked=false);
for(let val in this.innerVal)
this.compList.filter(f=>f.id==val).map(f=>f.checked=true);
// This will initialize checkbox with select or deseclt based on input
}
}
//Set touched on blur
onBlur() {
this.onTouchedCallback();
}
//From ControlValueAccessor interface
registerOnChange(fn: any) {
this.onChangeCallback = fn;
}
//From ControlValueAccessor interface
registerOnTouched(fn: any) {
this.onTouchedCallback = fn;
}
changeCheckbox(checkbox:InputProp<Number>,event:boolean):Boolean {
console.debug('CbaCheckBoxButton::changeCheckbox: ',checkbox,event);
// Below code deselect other if All or Global selected
if(checkbox.id!=-1){
this.compList.filter(f => f.checked && f.id==-1 ).map(f=>f.checked=false);
} else {
this.compList.filter(f => f.checked && f.id!=-1 ).map(f=>f.checked=false);
}
this.innerVal=this.compList.filter(f => f.checked).map(f=>(f.id));;
this.onTouchedCallback();
// let selectedInputs=this.compList.filter(f => f.checked).map(f=>(f.id));
// let me=this;
// if(selectedInputs.length==0){
// this.compList.map(f=>{
// if(f.id==me.prevSelectedVals[0])
// f.checked=true;
// });
// return true;
// }
// // There is small bug here, when try to deselect last record, which is prevented by these code and works perfet
// // But when you jump others and try to deselect again previously tried, it need twice click
// this.prevSelectedVals=selectedInputs;
return false; // this.updateParentListener();
}
// updateParentListener():Boolean {
// this.onCheckBoxChangeNotify.emit(selectedInputs);
// }
}
checkbox-button.component.ts
从“/../../models/common.classes”导入{InputProp};
从“./../../../pipes/title case.pipe”导入{TitleCasePipe};
进口{
FormGroup,
FormControl,
造模工,
ControlValueAccessor,
NG_值存取器,
NG_验证器,
验证器fn
}从“@angular/forms”开始;
//框架
导入{输入,
产出,
组成部分,
奥尼特,
OnDestroy,
forwardRef,
EventEmitter}来自“@angular/core”;
常量noop=()=>{
};
导出常量自定义\输入\控制\值\访问器:任意={
提供:NG_值访问器,
useExisting:forwardRef(()=>CheckBoxButton),
多:真的
};
@组成部分({
选择器:“复选框按钮”,
样式URL:['./../buttons.component.scss'],
templateUrl:“./checkbox button.component.html”,
提供者:[TitleCasePipe]
})
导出类CheckBoxButton实现OnInit、OnDestroy、ControlValueAccessor{
私有标题库:标题库管道=新标题库管道();
//(ngModelChange)=“CbaCheckBoxButtonChange($event)”
@输入('标签')
私有标签名:字符串;
@输入('列表')
私有compList:InputProp[];
@输入('isenum')
private-isEnumBaseCheckbox:boolean=false;
@输入('枚举列表')
私有enumComp:任何;
@输入('isupcase')
private isUpperCase:boolean=false;
@输入('sortdir')
私有sortDir:String='asc';
@输入('labeldir')
private isLabelDisplayDir:String='top';
@Output()onCheckBoxChangeNotify:EventEmitter;
private prevSelectedVals:(数字|字符串)[];
私人内部:任何[];
私人订购人:任何;
构造函数(){
this.oncheckboxchangennotify=neweventemitter();
}
恩戈尼尼特(){
this.orderBy={fields:['order'],sortDir:this.sortDir};
//console.debug('CbaCheckBoxButton::ngOnInit:'、this.compList、this.labelName);
}
恩贡德斯特罗(){
//调试('CbaCheckBoxButton::ngondetory:');
}
//稍后提供的回调的占位符d
//通过控制值访问器
私有onTouchedCallback:()=>void=noop;
private onChangeCallback:(quo:any)=>void=noop;
//获取访问器
get selectedVal():任意{
返回这个.innerVal;
};
////设置访问器,包括调用onchange回调
//设置selectedVal(v:any){
//if(v!==this.innerVal){
//this.innerVal=v;
//这是一个错误(v);
// }
// }
//从ControlValueAccessor接口
writeValue(值:任意){
if(值!==this.innerVal){
this.innerVal=值;
这个.compList.map(f=>f.checked=false);
for(让val在此.innerVal中)
this.compList.filter(f=>f.id==val).map(f=>f.checked=true);
//这将根据输入使用select或deseclt初始化复选框
}
}
//设置模糊
onBlur(){
这个.ontouchdcallback();
}
//从ControlValueAccessor接口
注册变更(fn:任何){
this.onChangeCallback=fn;
}
//从ControlValueAccessor接口
注册人(fn:任何){
this.onTouchedCallback=fn;
}
changeCheckbox(复选框:InputProp,事件:布尔):布尔{
调试('CbaCheckBoxButton::changeCheckbox:',复选框,事件);
//下面的代码如果选择了“全部”或“全局”,则取消选择“其他”
如果(checkbox.id!=-1){
this.compList.filter(f=>f.checked&&f.id==-1).map(f=>f.checked=false);
}否则{
this.compList.filter(f=>f.checked&&f.id!=-1).map(f=>f.checked=false);
}
this.innerVal=this.compList.filter(f=>f.checked.map(f=>f.id));;
这个.ontouchdcallback();
//让selectedInputs=this.compList.filter(f=>f.checked.map(f=>f.id));
//让我=这个;
//如果(selectedInputs.length==0){
//这个.compList.map(f=>{
//如果(f.id==me.prevSelectedVals[0])
//f.checked=true;
// });
//返回true;
// }
////当尝试取消选择最后一条记录时,这里有一个小错误,这是由这些代码阻止的,并且工作正常
////但是当您跳转其他人并尝试再次取消选择以前尝试过的选项时,需要单击两次
//this.prevSelectedVals=selectedInputs;
返回false;//this.updateParentListener();
}
//updateParentListener():布尔值{
//this.onCheckBoxChangeNotify.emit(selectedInputs);
// }
}
checkbox-button.component.html
{{labelName}}
{{isUpperCase?(checkbox.displayName | | checkbox.value | uppercase):(checkbox.displayName | | checkbox.value | titleCase)}
{{labelName}}
{{labelName}}
{{isUpperCase?(checkbox.displayName | | che)
checkbox-button.component.html
<div class="btn-group d-flex flex-default" *ngIf="isLabelDisplayDir=='left' || isLabelDisplayDir=='right'">
<label class="btn btn-primary radio-label labelName btn-right-border" *ngIf="isLabelDisplayDir=='left'">{{labelName}}</label>
<template [ngIf]="!isEnumBaseCheckbox">
<label class="btn btn-primary radio-label btn-right-border" [ngClass]="{'radio-label-checked':checkbox.checked}"
*ngFor="let checkbox of compList | orderBy:orderBy" >
<input type="checkbox" class="radio-input" (blur)="onBlur()" [value]="checkbox.checked"
[(ngModel)]="checkbox.checked" (ngModelChange)="changeCheckbox(checkbox,$event)">
{{ isUpperCase ? (checkbox.displayName||checkbox.value | uppercase):(checkbox.displayName||checkbox.value | titleCase)}}
</label>
</template>
<label class="btn btn-primary radio-label labelName btn-right-border" *ngIf="isLabelDisplayDir=='right'">{{labelName}}</label>
</div>
<div *ngIf="isLabelDisplayDir=='top' || isLabelDisplayDir=='bottom'">
<div class="btn-group d-flex flex-default" *ngIf="isLabelDisplayDir=='top'">
<label class="btn btn-primary radio-label labelNameTB">{{labelName}}</label><br/>
</div>
<div class="btn-group d-flex flex-default">
<template [ngIf]="!isEnumBaseCheckbox">
<label class="btn btn-primary radio-label btn-right-border" [ngClass]="{'radio-label-checked':checkbox.checked}"
*ngFor="let checkbox of compList | orderBy:orderBy" >
<input type="checkbox" class="radio-input" (blur)="onBlur()" [value]="checkbox.checked"
[(ngModel)]="checkbox.checked" (ngModelChange)="changeCheckbox(checkbox,$event)">
{{ isUpperCase ? (checkbox.displayName||checkbox.value | uppercase):(checkbox.displayName||checkbox.value | titleCase)}}
</label>
</template>
</div>
<div class="btn-group d-flex flex-default" *ngIf="isLabelDisplayDir=='bottom'">
<label class="btn btn-primary radio-label labelNameTB">{{labelName}}</label><br/>
</div>
</div>