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

时间:2017-04-07 06:58:15

标签: angular custom-component

我正在尝试使用bootstrap 4复选框布局为复选框实现自定义组件。

我正在关注此链接以实现自定义组件 http://almerosteyn.com/2016/04/linkup-custom-control-to-ngcontrol-ngmodel

我成功地为收音机做了,但是复选框我正在输入作为ID的数组,输出将是基于选择的ID数组。

Checkbox是根据JS模型实例列表创建的。

export class InputProp<T>{
displayName?:String;
order?:Number;

constructor( public checked:boolean=false,
             public value:String,
             public id:T) 
{}

}

但有一件事我不理解,角度2如何读取内部值并更新外部模态

有writevalue方法读取组件外的组件ngModel值。

是否有像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.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> 

0 个答案:

没有答案