我正在尝试使用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>