/**
* Created by giscafer on 2017/4/15.
* 复制源码修改覆盖
* 添加事件延迟,支持中文输入
* //modified by giscafer
*/
/*tslint:disable */
import {
NgModule,
Component,
ViewChild,
ElementRef,
AfterViewInit,
AfterContentInit,
AfterViewChecked,
DoCheck,
Input,
Output,
OnDestroy,
EventEmitter,
ContentChildren,
QueryList,
TemplateRef,
IterableDiffers,
Renderer,
forwardRef
} from '@angular/core';
import {CommonModule} from '@angular/common';
import {NG_VALUE_ACCESSOR, ControlValueAccessor} from '@angular/forms';
import {InputTextModule} from "../inputtext/inputtext";
import {ButtonModule} from "../button/button";
import {PrimeNGSharedModule, PrimeTemplate} from "../common/shared";
import { ObjectUtils } from '../utils/objectutils';
import { DomHandler } from '../dom/domhandler';
export const AUTOCOMPLETE_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => AutoComplete),
multi: true
};
//modified by giscafer
let optionsTemplate = `
-
{{field ? option[field] : option}}
`;
@Component({
selector: 'p-autoComplete',
template: `
-
{{field ? option[field] : option}}
`,
//modified by giscafer
styles: ['.ui-select-group-label{ font-weight: bold; } .ui-select-choices-row{margin:3px 0 3px 1em}'],
host: {
'[class.ui-inputwrapper-filled]': 'filled',
'[class.ui-inputwrapper-focus]': 'focus'
},
providers: [DomHandler, ObjectUtils, AUTOCOMPLETE_VALUE_ACCESSOR]
})
export class AutoComplete implements AfterViewInit,AfterContentInit,DoCheck,OnDestroy,AfterViewChecked,ControlValueAccessor {
@Input() hasChildren: string;
@Input() minLength: number = 1;
@Input() delay: number = 300;
@Input() style: any;
@Input() styleClass: string;
@Input() inputStyle: any;
@Input() inputStyleClass: string;
@Input() placeholder: string;
@Input() readonly: boolean;
@Input() disabled: boolean;
@Input() maxlength: number;
@Input() size: number;
@Input() suggestions: any[];
@Input() appendTo: any;
@Output() completeMethod: EventEmitter = new EventEmitter();
@Output() onSelect: EventEmitter = new EventEmitter();
@Output() onUnselect: EventEmitter = new EventEmitter();
@Output() onFocus: EventEmitter = new EventEmitter();
@Output() onDropdownClick: EventEmitter = new EventEmitter();
@Input() field: string;
@Input() scrollHeight: string = '200px';
@Input() dropdown: boolean;
@Input() multiple: boolean;
@Input() tabindex: number;
@ContentChildren(PrimeTemplate) templates: QueryList;
public itemTemplate: TemplateRef;
public selectedItemTemplate: TemplateRef;
value: any;
onModelChange: Function = () => {
};
onModelTouched: Function = () => {
};
timeout: any;
timeout2: any;
differ: any;
panel: any;
input: any;
multipleContainer: any;
panelVisible: boolean = false;
documentClickListener: any;
suggestionsUpdated: boolean;
highlightOption: any;
highlightOptionChanged: boolean;
focus: boolean = false;
dropdownFocus: boolean = false;
filled: boolean;
@ViewChild('in') inputEL: ElementRef;
constructor(public el: ElementRef, public domHandler: DomHandler, differs: IterableDiffers, public renderer: Renderer, public objectUtils: ObjectUtils) {
this.differ = differs.find([]).create(null);
}
ngDoCheck() {
// ////console.log(this.suggestions)
let changes = this.differ.diff(this.suggestions);
if (changes && this.panel) {
if (this.suggestions && this.suggestions.length) {
this.show();
this.suggestionsUpdated = true;
}
else {
this.hide();
}
}
}
ngAfterContentInit() {
this.templates.forEach((item) => {
switch (item.getType()) {
case 'item':
this.itemTemplate = item.template;
break;
case 'selectedItem':
this.selectedItemTemplate = item.template;
break;
default:
this.itemTemplate = item.template;
break;
}
});
}
ngAfterViewInit() {
this.input = this.domHandler.findSingle(this.el.nativeElement, 'input');
this.panel = this.domHandler.findSingle(this.el.nativeElement, 'div.ui-autocomplete-panel');
if (this.multiple) {
this.multipleContainer = this.domHandler.findSingle(this.el.nativeElement, 'ul.ui-autocomplete-multiple-container');
}
this.documentClickListener = this.renderer.listenGlobal('body', 'click', () => {
this.hide();
});
if (this.appendTo) {
if (this.appendTo === 'body')
document.body.appendChild(this.panel);
else
this.domHandler.appendChild(this.panel, this.appendTo);
}
}
ngAfterViewChecked() {
if (this.suggestionsUpdated) {
this.align();
this.suggestionsUpdated = false;
}
if (this.highlightOptionChanged) {
let listItem = this.domHandler.findSingle(this.panel, 'li.ui-state-highlight');
if (listItem) {
this.domHandler.scrollInView(this.panel, listItem);
}
this.highlightOptionChanged = false;
}
}
writeValue(value: any): void {
this.value = value;
this.filled = this.value && this.value != '';
}
registerOnChange(fn: Function): void {
this.onModelChange = fn;
}
registerOnTouched(fn: Function): void {
this.onModelTouched = fn;
}
setDisabledState(val: boolean): void {
this.disabled = val;
}
/**
* 改动方法
* @param event
*/
onInput(event) {
let value = event.target.value;
if (!this.multiple) {
//modified by giscafer on 2017-4-15 16:31:57
//Cancel the search request if user types within the timeout
if (this.timeout2) {
clearTimeout(this.timeout2);
}
this.timeout2 = setTimeout(() => {
this.value = value;
this.onModelChange(value);
}, this.delay)
//modified by giscafer end
}
if (value.length === 0) {
this.hide();
}
if (value.length >= this.minLength) {
//Cancel the search request if user types within the timeout
if (this.timeout) {
clearTimeout(this.timeout);
}
this.timeout = setTimeout(() => {
this.search(event, value);
}, this.delay);
}
else {
this.suggestions = null;
}
this.updateFilledState();
}
search(event: any, query: string) {
//allow empty string but not undefined or null
if (query === undefined || query === null) {
return;
}
this.completeMethod.emit({
originalEvent: event,
query: query
});
}
selectItem(option: any, event?: any) {
if (this.multiple) {
this.input.value = '';
this.value = this.value || [];
if (!this.isSelected(option)) {
this.value.push(option);
this.onModelChange(this.value);
}
}
else {
this.input.value = this.field ? this.objectUtils.resolveFieldData(option, this.field) : option;
this.value = option;
this.onModelChange(this.value);
}
this.onSelect.emit(option);
this.input.focus();
}
groupLabelClick(event: any) {
event.stopPropagation();
}
show() {
if (!this.panelVisible && (this.focus || this.dropdownFocus)) {
this.panelVisible = true;
this.panel.style.zIndex = ++DomHandler.zindex;
this.domHandler.fadeIn(this.panel, 200);
}
}
align() {
if (this.appendTo)
this.domHandler.absolutePosition(this.panel, (this.multiple ? this.multipleContainer : this.input));
else
this.domHandler.relativePosition(this.panel, (this.multiple ? this.multipleContainer : this.input));
}
hide() {
this.panelVisible = false;
}
handleDropdownClick(event) {
this.onDropdownClick.emit({
originalEvent: event,
query: this.input.value
});
}
removeItem(item: any) {
let itemIndex = this.domHandler.index(item);
let removedValue = this.value.splice(itemIndex, 1)[0];
this.onUnselect.emit(removedValue);
this.onModelChange(this.value);
}
onKeydown(event) {
if (this.panelVisible) {
let highlightItemIndex = this.findOptionIndex(this.highlightOption);
switch (event.which) {
//down
case 40:
if (highlightItemIndex != -1) {
var nextItemIndex = highlightItemIndex + 1;
if (nextItemIndex != (this.suggestions.length)) {
this.highlightOption = this.suggestions[nextItemIndex];
this.highlightOptionChanged = true;
}
}
else {
this.highlightOption = this.suggestions[0];
}
event.preventDefault();
break;
//up
case 38:
if (highlightItemIndex > 0) {
let prevItemIndex = highlightItemIndex - 1;
this.highlightOption = this.suggestions[prevItemIndex];
this.highlightOptionChanged = true;
}
event.preventDefault();
break;
//enter
case 13:
if (this.highlightOption) {
this.selectItem(this.highlightOption);
this.hide();
}
event.preventDefault();
break;
//escape
case 27:
this.hide();
event.preventDefault();
break;
//tab
case 9:
if (this.highlightOption) {
this.selectItem(this.highlightOption);
}
this.hide();
break;
}
} else {
if (event.which === 40 && this.suggestions) {
this.search(event, event.target.value);
}
}
if (this.multiple) {
switch (event.which) {
//backspace
case 8:
if (this.value && this.value.length && !this.input.value) {
let removedValue = this.value.pop();
this.onUnselect.emit(removedValue);
this.onModelChange(this.value);
}
break;
}
}
}
onInputFocus(event) {
this.focus = true;
this.onFocus.emit(event);
}
onBlur() {
this.focus = false;
this.onModelTouched();
}
onDropdownFocus() {
this.dropdownFocus = true;
this.inputEL.nativeElement.focus();
}
onDropdownBlur() {
this.dropdownFocus = false;
}
isSelected(val: any): boolean {
let selected: boolean = false;
if (this.value && this.value.length) {
for (let i = 0; i < this.value.length; i++) {
if (this.objectUtils.equals(this.value[i], val)) {
selected = true;
break;
}
}
}
return selected;
}
findOptionIndex(option): number {
let index: number = -1;
if (this.suggestions) {
for (let i = 0; i < this.suggestions.length; i++) {
if (this.objectUtils.equals(option, this.suggestions[i])) {
index = i;
break;
}
}
}
return index;
}
updateFilledState() {
this.filled = this.input && this.input.value != '';
}
ngOnDestroy() {
if (this.documentClickListener) {
this.documentClickListener();
}
if (this.appendTo) {
this.el.nativeElement.appendChild(this.panel);
}
}
}
@NgModule({
imports: [CommonModule, InputTextModule, ButtonModule, PrimeNGSharedModule],
exports: [AutoComplete, PrimeNGSharedModule],
declarations: [AutoComplete]
})
export class AutoCompleteModule {
}