import {NgModule,Component,ElementRef,OnInit,AfterViewInit,AfterContentInit,AfterViewChecked,OnDestroy,Input,Output,Renderer2,EventEmitter, forwardRef,ViewChild,ChangeDetectorRef,TemplateRef,ContentChildren,QueryList,ContentChild} from '@angular/core'; import {CommonModule} from '@angular/common'; import {SelectItem} from '../common/selectitem'; import {DomHandler} from '../dom/domhandler'; import {ObjectUtils} from '../utils/objectutils'; import {SharedModule,PrimeTemplate,Footer} from '../common/shared'; import {NG_VALUE_ACCESSOR, ControlValueAccessor} from '@angular/forms'; export const MULTISELECT_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => Select), multi: true }; @Component({ selector: 'p-select', template: `
  • {{option.label}}
`, host: { '[class.ui-inputwrapper-filled]': 'filled', '[class.ui-inputwrapper-focus]': 'focus' }, providers: [DomHandler,ObjectUtils,MULTISELECT_VALUE_ACCESSOR] }) export class Select implements OnInit,AfterViewInit,AfterContentInit,AfterViewChecked,OnDestroy,ControlValueAccessor { @Input() scrollHeight: string = '200px'; @Input() panelWidth: string = "200px"; @Input() multi: boolean = false; @Input() defaultLabel: string = '请选择'; @Input() style: any; @Input() styleClass: string; @Input() panelStyle: any; @Input() panelStyleClass: string; @Input() inputId: string; @Input() disabled: boolean; @Input() filter: boolean = true; @Input() filterPlaceHolder: string; @Input() overlayVisible: boolean; @Input() tabindex: number; @Input() appendTo: any; @Input() dataKey: string; @Input() name: string; @Input() displaySelectedLabel: boolean = true; @Input() maxSelectedLabels: number = 3; @Input() selectedItemsLabel: string = '已选择 {0} 个'; @Input() showToggleAll: boolean = true; @Input() resetFilterOnHide: boolean = false; @Input() dropdownIcon: string = 'icon ion-arrow-down-b'; @Input() optionLabel: string; @Input() showHeader: boolean = true; @ViewChild('container') containerViewChild: ElementRef; @ViewChild('panel') panelViewChild: ElementRef; @ViewChild('filterInput') filterInputChild: ElementRef; @ContentChild(Footer) footerFacet; @ContentChildren(PrimeTemplate) templates: QueryList; @Output() onChange: EventEmitter = new EventEmitter(); @Output() onFocus: EventEmitter = new EventEmitter(); @Output() onBlur: EventEmitter = new EventEmitter(); @Output() onPanelShow: EventEmitter = new EventEmitter(); @Output() onPanelHide: EventEmitter = new EventEmitter(); public value: any[]; public onModelChange: Function = () => {}; public onModelTouched: Function = () => {}; public valuesAsString: string; public focus: boolean; filled: boolean; public documentClickListener: any; public container: HTMLDivElement; public panel: HTMLDivElement; public selfClick: boolean; public panelClick: boolean; public filterValue: string; public visibleOptions: SelectItem[]; public filtered: boolean; public itemTemplate: TemplateRef; public focusedItemCheckbox: HTMLInputElement; _options: any[]; constructor(public el: ElementRef, public domHandler: DomHandler, public renderer: Renderer2, public objectUtils: ObjectUtils, private cd: ChangeDetectorRef) {} @Input() get options(): any[] { return this._options; } set options(val: any[]) { let opts = this.optionLabel ? this.objectUtils.generateSelectItems(val, this.optionLabel) : val; this._options = opts; this.updateLabel(); } ngOnInit() { this.updateLabel(); } ngAfterContentInit() { this.templates.forEach((item) => { switch(item.getType()) { case 'item': this.itemTemplate = item.template; break; default: this.itemTemplate = item.template; break; } }); } ngAfterViewInit() { this.container = this.containerViewChild.nativeElement; this.panel = this.panelViewChild.nativeElement; if(this.appendTo) { if(this.appendTo === 'body') document.body.appendChild(this.panel); else this.domHandler.appendChild(this.panel, this.appendTo); } if(this.overlayVisible) { this.show(); } } ngAfterViewChecked() { if(this.filtered) { if(this.appendTo) this.domHandler.absolutePosition(this.panel, this.container); else this.domHandler.relativePosition(this.panel, this.container); this.filtered = false; } } writeValue(value: any) : void { this.value = value; this.updateLabel(); this.updateFilledState(); this.cd.markForCheck(); } updateFilledState() { this.filled = (this.valuesAsString != null && this.valuesAsString.length > 0); } registerOnChange(fn: Function): void { this.onModelChange = fn; } registerOnTouched(fn: Function): void { this.onModelTouched = fn; } setDisabledState(val: boolean): void { this.disabled = val; } onItemClick(event, option) { if(this.multi){ let selectionIndex = this.findSelectionIndex(option); if(selectionIndex != -1) this.value = this.value.filter((val,i) => i!=selectionIndex); else this.value = [...this.value||[],option]; this.onModelChange(this.value); }else { this.value = [option]; this.onModelChange(this.value[0]); this.hide(); } this.onChange.emit({originalEvent: event, value: this.value, itemValue: option.label}); this.updateLabel(); this.updateFilledState(); } isSelected(value) { return this.findSelectionIndex(value) != -1; } findSelectionIndex(val: any): number { let index = -1; if(this.value) { for(let i = 0; i < this.value.length; i++) { if(this.objectUtils.equals(this.value[i].label, val.label, this.dataKey)) { index = i; break; } } } return index; } toggleAll(event, checkbox) { if(checkbox.checked) { this.value = []; } else { let opts = this.getVisibleOptions(); if(opts) { this.value = []; for(let i = 0; i < opts.length; i++) { this.value.push(opts[i].value); } } } checkbox.checked = !checkbox.checked; this.onModelChange(this.value); this.onChange.emit({originalEvent: event, value: this.value}); this.updateLabel(); } isAllChecked() { if(this.filterValue && this.filterValue.trim().length) return this.value&&this.visibleOptions&&this.visibleOptions.length&&(this.value.length == this.visibleOptions.length); else return this.value&&this.options&&(this.value.length == this.options.length); } show() { this.overlayVisible = true; this.panel.style.zIndex = String(++DomHandler.zindex); this.bindDocumentClickListener(); if(this.appendTo) this.domHandler.absolutePosition(this.panel, this.container); else this.domHandler.relativePosition(this.panel, this.container); this.domHandler.fadeIn(this.panel, 250); this.onPanelShow.emit(); } hide() { this.overlayVisible = false; this.unbindDocumentClickListener(); if(this.resetFilterOnHide){ this.filterValue = null; this.filterInputChild.nativeElement.value = null; } this.onPanelHide.emit(); } close(event) { this.hide(); event.preventDefault(); event.stopPropagation(); } onMouseclick(event,input) { if(this.disabled) { return; } if(!this.panelClick) { if(this.overlayVisible) { this.hide(); } else { input.focus(); this.show(); } } this.selfClick = true; } onInputFocus(event) { this.focus = true; this.onFocus.emit({originalEvent: event}); } onInputBlur(event) { this.focus = false; this.onBlur.emit({originalEvent: event}); this.onModelTouched(); } onInputKeydown(event) { switch(event.which) { //down case 40: if(!this.overlayVisible && event.altKey) { this.show(); } event.preventDefault(); break; //escape and tab case 27: case 9: this.hide(); break; } } updateLabel() { if(this.value && this.options && this.value.length && this.displaySelectedLabel) { let label = ''; for(let i = 0; i < this.value.length; i++) { let itemLabel = this.findLabelByValue(this.value[i].id); if (itemLabel) { if(label.length > 0) { label = label + ', '; } label = label + itemLabel; } } if(this.value.length <= this.maxSelectedLabels) { this.valuesAsString = label; } else { let pattern = /{(.*?)}/; if (pattern.test(this.selectedItemsLabel)) { this.valuesAsString = this.selectedItemsLabel.replace(this.selectedItemsLabel.match(pattern)[0], this.value.length + ''); } } } else { this.valuesAsString = this.defaultLabel; } } findLabelByValue(id: any): string { let label = null; for(let i = 0; i < this.options.length; i++) { let option = this.options[i]; if(id == null || this.objectUtils.equals(id, option.id, this.dataKey)) { label = option.label; break; } if(option.children){ for(let s = 0; s < option.children.length; s++) { let children = option.children[s]; if(id == null || this.objectUtils.equals(id, children.id, this.dataKey)) { label = children.label; break; } } } } return label; } onFilter(event) { this.filterValue = event.target.value.trim().toLowerCase(); this.visibleOptions = []; for(let i = 0; i < this.options.length; i++) { let option = this.options[i]; if(option.label.toLowerCase().indexOf(this.filterValue.toLowerCase()) > -1) { this.visibleOptions.push(option); } } this.filtered = true; } isItemVisible(option: SelectItem): boolean { if(this.filterValue && this.filterValue.trim().length) { for(let i = 0; i < this.visibleOptions.length; i++) { if(this.visibleOptions[i].value == option.value) { return true; } } } else { return true; } } getVisibleOptions(): SelectItem[] { if(this.filterValue && this.filterValue.trim().length) { let items = []; for(let i = 0; i < this.options.length; i++) { let option = this.options[i]; if(option.label.toLowerCase().includes(this.filterValue.toLowerCase())) { items.push(option); } } return items; } else { return this.options; } } bindDocumentClickListener() { if(!this.documentClickListener) { this.documentClickListener = this.renderer.listen('document', 'click', () => { if(!this.selfClick && !this.panelClick && this.overlayVisible) { this.hide(); } this.selfClick = false; this.panelClick = false; this.cd.markForCheck(); }); } } unbindDocumentClickListener() { if(this.documentClickListener) { this.documentClickListener(); this.documentClickListener = null; } } ngOnDestroy() { this.unbindDocumentClickListener(); if(this.appendTo) { this.container.appendChild(this.panel); } } } @NgModule({ imports: [CommonModule,SharedModule], exports: [Select,SharedModule], declarations: [Select] }) export class SelectModule { }