先看效果:
从后台查到有数据就会展示,一点击清空内容,根据大类去查询相应品牌,并且可以选择
输入内容后会先在当前的选项中过滤,如果没有的话会向后台发请求查询
这是组件代码
export interface SearchOption {
placeholder?: string;
searchId: string; // 控件的标识id,用于区分多个select控件
optionId: string; // 绑定值的id
optionField: string; // input框展示内容
asyncLoading?: boolean;
needInput?: boolean; // 组件为typeahead+select,如果设置为true,代表需要input框中的value,且该value不属于任何一个option
}
@Component({
selector: 'search-select',
template: `
<input [(ngModel)]="selected"
[typeahead]="dataSource"
(typeaheadOnSelect)="searchOnSelect($event)"
[typeaheadMinLength]="0"
[typeaheadOptionsLimit]="500"
(blur)="blurOnSelect($event)"
(focus)="focusOnSelect($event)"
(input)="keyupOnSelect($event)"
[typeaheadScrollable]="true"
typeaheadOptionField="{{searchOption.optionField}}"
placeholder="{{searchOption.placeholder}}"
class="form-control"
id="select"
>
`
})
export class SearchSelectComponent {
@Input() showItems: any[];
@Input() selected: string;//初始化默认值
@Input() searchOption: SearchOption;
@Input() keepValue:boolean;
@Input() itemInfo:any;//当前选中的节点
@Output('onSearchOnSelect') onSearchOnSelect = new EventEmitter<any>();
@Output('asyncLoadFunction') asyncLoadFunction = new EventEmitter<any>();
@Output('onKeyupOnSelect') onKeyupOnSelect = new EventEmitter<any>();
lastTimeSelected: string;
tmpSelected:string;
dataSource: Observable<any>;
constructor() {
this.dataSource = Observable.create((observer: any) => {
observer.next(this.selected);
}).mergeMap((token: string) => this.getSearchObservable(token));
}
ngOnInit(){
if(this.keepValue){
this.tmpSelected = this.selected;
}
}
getSearchObservable(token: string): Observable<any> {
if (this.searchOption.asyncLoading) {
this.asyncLoadFunction.emit(token);
}
return this.checkObservableOption(token);
}
searchOnSelect(e: TypeaheadMatch): void {
this.selected = e.value;
this.lastTimeSelected = e.value;
const item = {
key: this.searchOption.searchId,
selected: e
};
this.onSearchOnSelect.emit(item);
}
checkObservableOption(token: string): Observable<any> {
return Observable.of(
this.showItems.filter((state: any) => {
return state[this.searchOption.optionField].toLowerCase().indexOf(token) > -1;
})
);
}
/**
* 未选择数据之前为清空状态
* @param e
*/
blurOnSelect(e): void {
if (!this.searchOption.needInput) {
// 大部分的控件使用这种模式,保持向后兼容性,不会修改控件行为
this.emitSelection();
}
else {
// 新添加的行为,除了保持之前的模式,还支持获取控件中输入的值
this.emitInputOrSelection();
}
}
private emitSelection() {
if (this.selected) {
this.selected = this.lastTimeSelected;
}
else {
this.handleEmptySelection();
}
}
private emitInputOrSelection() {
if (this.selected && this.showItems.find(item => item === this.selected)) {
this.selected = this.lastTimeSelected;
} else if (this.selected && !this.showItems.find(item => item === this.selected)) {
const item = {
key: this.searchOption.searchId,
input: this.selected
};
this.onSearchOnSelect.emit(item);
}else if(this.keepValue&&!this.selected){
this.selected = this.tmpSelected
}
else {
this.handleEmptySelection();
}
}
private handleEmptySelection() {
this.selected = '';
this.lastTimeSelected = '';
const item = {
key: this.searchOption.searchId,
selected: ''
};
this.onSearchOnSelect.emit(item);
}
/**
* 选择数据之前清空
*/
focusOnSelect(e): void {
this.selected = '';
if(this.itemInfo){
this.asyncLoadFunction.emit({itemInfo: this.itemInfo});
}
}
keyupOnSelect(e): void {
const item = {
key: $("#select").val(),
keycode: e.keyCode
}
this.onKeyupOnSelect.emit(item)
}
}
根据需要修改过组件,比如需要留下input的值,加了个keepValue,总之根据需要添加input和output。这是使用时的html
<td>
<search-select
id="brand{{idx}}"
[searchOption]="asyncBrandOption"
[showItems]="showBrand"
[keepValue]=true
[selected]="purchaseTable.materialModelBrand"
[itemInfo]="purchaseTable"
(asyncLoadFunction)="asyncBrandFunction(purchaseTable,idx)"
(onSearchOnSelect)="onSearchOnSelect($event)"
(onKeyupOnSelect)="onKeySelectBrand($event)">
</search-select>
</td>
使用时在ts中定义相应的函数,比如asyncBrandFunction,接收到组件传的值,再发请求得到动态的数据,赋值给showitems就可以了。
上面说完了大致思想,下面说遇到的问题。
由于点击后发请求,有个延迟问题,在第二次点击的时候数据才能请求回来,并且点的太快,会显示上一次的选项,对于页面中输入框多的情况,点击后选项会乱。
选项会乱这个问题很好解决,只要在发请求之前清空上次的选项值就可以了。
第二次点击才能出来这个问题,最后有一种思路,就是我们让程序自动点击一次当前的输入框。
代码如下:
如果你仔细看会看到上面的循环把index赋值给了idx。
$('#brand'+idx+' input').click();