import { Input, Component, ElementRef, NgZone, Renderer2, ChangeDetectorRef, TemplateRef, OnInit, AfterContentInit, forwardRef, ViewEncapsulation, Output, EventEmitter, OnChanges, SimpleChanges, HostListener, ChangeDetectionStrategy, ViewChild, Inject } from '@angular/core'; import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms'; import { Observable } from 'rxjs'; import { LookupGridResult, ServerSideToken } from '@farris/ui-lookup'; import { KeyCode, SelectItem, DEFAULT_COMBOSTATE, defaultComboState, COMBO_FACADE_SERVICE } from '../core/combo-types'; import { ComboComponent } from '../combo.component'; import { ComboFacadeService } from '../core/combo-facade.service'; import { ComboService } from '../combo.service'; import { CommonUtils, FarrisDataItem } from '@farris/ui-common'; import { map, debounceTime } from 'rxjs/operators'; import { DatalistComponent } from '@farris/ui-datalist'; export const INPUT_COMBOBOX_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => CombolistComponent), multi: true }; @Component({ selector: 'farris-combolist', template: ` `, providers: [ INPUT_COMBOBOX_VALUE_ACCESSOR, {provide: DEFAULT_COMBOSTATE, useValue: defaultComboState}, {provide: COMBO_FACADE_SERVICE, useClass: ComboFacadeService, deps: [ DEFAULT_COMBOSTATE ] } ], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush }) export class CombolistComponent extends ComboComponent implements OnInit, OnChanges, AfterContentInit, ControlValueAccessor { @Input() multiSelect = false; @Input() uri: string; @Input() data: any; @Input() disableExpress: (item) => boolean; /** * 映射字段 { helpField: targetField } * - helpField: 控件数据源中的字段 * - targetField: 目标字段 */ @Input() mapFields: { [helpField: string]: string }; @Output() selected = new EventEmitter(); @Output() unSelected = new EventEmitter(); @Output() selectedValuesChange = new EventEmitter(); @Input() itemTemplate: TemplateRef; private _dl: DatalistComponent; @ViewChild(DatalistComponent) set dlComp(cmp: DatalistComponent) { this._dl = cmp; } data$ = this.cmbFacade.data$.pipe( map((d: FarrisDataItem[]) => { return d.map( item => item.data ); }) ); constructor( public el: ElementRef, public zone: NgZone, public render: Renderer2, public cd: ChangeDetectorRef, @Inject(COMBO_FACADE_SERVICE) public cmbFacade: ComboFacadeService, public cmbState: ComboService, private utils: CommonUtils ) { super(el, zone, render, cd, cmbFacade, cmbState); } ngOnInit() { super.ngOnInit(); const { idField, textField, valueField, multiSelect } = {...this}; this.cmbFacade.initControlState({ idField, valueField, textField, multiSelect}); if (this.disableExpress) { this.cmbFacade.disableExpress = this.disableExpress; } if (this.data) { this.cmbFacade.loadData(this.data, this.selectedValues); } this.cmbFacade.selections$.pipe( debounceTime(0) ).subscribe( items => { if (items && items.length) { this.displayText = items.map(item => this.utils.getValue(this.textField, item)).join(','); } else { this.displayText = ''; } this.onModelChange(this.displayText); this.updateValues(items); this.updateChanges(); }); } ngOnChanges(changes: SimpleChanges) { } ngAfterContentInit() { } @HostListener('keydown', ['$event']) handleKeyDown($event: KeyboardEvent) { if (KeyCode[$event.which]) { switch ($event.which) { case KeyCode.ArrowDown: this._handleArrowDown($event); break; case KeyCode.ArrowUp: this._handleArrowUp($event); break; // case KeyCode.Space: // this._handleSpace($event); // break; // case KeyCode.Enter: // this._handleEnter($event); // break; case KeyCode.Tab: this._handleTab($event); break; case KeyCode.Esc: this.close(); $event.preventDefault(); $event.stopPropagation(); break; // case KeyCode.Backspace: // this._handleBackspace(); // break; } } // else if ($event.key && $event.key.length === 1) { // this._keyPress$.next($event.key.toLocaleLowerCase()); // } } getData(uri: string, params?: any): Observable { return null; } clearSelections() { this.setValue(''); this.cmbFacade.clearSelections(); } onValueChanged(val: any) { this.onModelChange(val); if (!val) { this.cmbFacade.clearSelections(); } this.valueChange.emit(val); } @HostListener('mousedown', ['$event']) handleMousedown($event: MouseEvent) { const target = $event.target as HTMLElement; if (target.tagName === 'INPUT' || target.tagName === 'I') { return; } $event.preventDefault(); $event.stopPropagation(); } private _handleArrowDown(event: any) { } private _handleArrowUp(event: any) {} private toggleSelect(selectItem: SelectItem, isSelected: boolean) { if (isSelected) { this.selected.emit(selectItem); } else { this.unSelected.emit(selectItem); } if (!this.multiSelect) { this.cmbFacade.hidePanel(); } } private updateChanges() { this.cd.markForCheck(); this.cd.detectChanges(); } private updateValues(selections: any[]) { if (selections && selections.length) { const val = selections.map(item => item[this.valueField]).join(','); this.setValue(val); } else { this.setValue(''); } } getSelections() { return this.cmbFacade.getSelections(); } setValue(val: string) { this.selectedValuesChange.emit(val); } onSelect($event: SelectItem) { const { data, index } = {...$event}; this.cmbFacade.selectItem(data, index); this.toggleSelect($event, true); } onUnSelected($event: SelectItem) { const { data, index } = {...$event}; this.cmbFacade.unSelectItem(data); this.toggleSelect($event, false); } onClear() { this.cmbFacade.clearSelections(); if (this._dl) { this._dl.clearSelections(); } this.clear.emit(); } writeValue(obj: any): void { this.displayText = obj; this.updateChanges(); } registerOnChange(fn: any): void { this.onModelChange = fn; } registerOnTouched(fn: any): void { this.onModelTouched = fn; } setDisabledState?(isDisabled: boolean): void { this.setDisable(isDisabled); } }