Angular 2 ControlValueAccessor用于自定义复选框,如何更新从内到外的值?

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

我正在尝试使用bootstrap4checkbox布局实现checkbox的自定义组件

我遵循此链接来实现自定义组件

我成功地完成了无线电,但我将输入作为ID数组,输出将是基于选择的ID数组

复选框是根据JS模型实例列表创建的

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>