我正在使用具有自动完成功能的角形材料输入芯片,我的设置有些复杂,如果用户尝试选择尚不存在的选项,则会弹出对话框,他们可以创建一个新选项,为此创建了一个芯片。
问题是当用户输入选项名称的一部分时,例如“ we”在搜索“ western”时,将鼠标向下移动并单击“ western”,即使我认为应该触发optionSelected,该对话框仍然会从matChipInputTokenEnd内部弹出。
当我使用箭头键选择“西部”并按Enter键而不是使用鼠标时,它会按预期工作。创建了一个“西方”芯片。
我如何用鼠标单击下拉菜单中的选项,其作用与使用箭头突出显示选项并按Enter一样?
编辑: 从add()内部删除对话框后,问题就消失了
edit 2:我决定暂时放弃此功能,我花了整整一周的时间尝试所有功能,这实在令人遗憾,因为我是如此接近,是否有某种方法可以检测是否通过触发了matChipInputTokenEnd单击还是输入,我可以解决此问题,如果有人想出了从add()中打开对话框的方法,或者如果有更合适的方法可以执行此操作,请告诉我。我能想到的唯一替代方法是使用基本的add()方法,例如将示例数据添加到数组中,然后将dialogData添加到数组中,然后为父窗体的onSubmit弹出对话框以填充其余项的详细信息,但是我不喜欢的用户体验,因此我不确定是否值得。
模板
<mat-form-field class="full-width">
<mat-chip-list #modifierChipList>
<mat-chip
*ngFor="let modifier of selectedModifiers"
[selectable]="selectable"
[removable]="removable"
(removed)="remove(modifier,'selectedModifiers','modifierList')">
{{modifier}}
<mat-icon matChipRemove *ngIf="removable">cancel</mat-icon>
</mat-chip>
<input
(click)="focusOut()"
placeholder="Modifiers"
#modifierInput
[formControl]="modifierCtrl"
[matAutocomplete]="autoModifier"
[matChipInputFor]="modifierChipList"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes"
[matChipInputAddOnBlur]="addOnBlur"
(matChipInputTokenEnd)="add($event, 'selectedModifiers', 'modifierList', 'modifierCtrl', 'modifier', filteredModifiers)">
</mat-chip-list>
<mat-autocomplete #autoModifier="matAutocomplete" (optionSelected)="selected($event, 'selectedModifiers', 'modifierInput', 'modifierCtrl', 'modifier')">
<mat-option *ngFor="let modifier of filteredModifiers | async" [value]="modifier">
{{modifier}}
</mat-option>
</mat-autocomplete>
</mat-form-field>
ts:
add(event: MatChipInputEvent, selectedItems, allItemsList, ctrl, type, filteredList): void {
const options = {};
const input = event.input;
const value = event.value;
if(!this.autoModifier.isOpen || this[allItemsList].indexOf(value) < 0 ){
// Add our new item to both the DB and local selected items
if ((value || '').trim()) {
if(!this[allItemsList].includes(value.trim())){
switch(type){
case 'category':
break;
case 'option':
options['Type'] = this.typeList;
options['Categories'] = this.categoryList;
break;
case 'component':
options['Options'] = this.optionList;
options['Categories'] = this.categoryList;
break;
case 'modifier':
options['Additions'] = this.optionList;
options['Removals'] = this.optionList;
options['Options'] = this.optionList;
options['Categories'] = this.categoryList;
options['Type'] = this.typeList;
break;
case 'tax':
break;
default:
break;
}
let dialogQuestions = this.service.getQuestions(type, options);
//open a modal to create that item
const dialogRef = this.dialog.open(DialogComponent, {
width: '70%',
data: {
dialog: this,
type: type,
details:{
name:value,
questions: dialogQuestions
}
},
});
dialogRef.afterClosed().subscribe(result => {
this[allItemsList].push(result.name);
this[selectedItems].push(result.name);
this.autocomplete.closePanel();//this doesnt work for some reason
//based on the result add it to the allItemsList and the selectedItems
//also create a chip for it
});
}
}
// Reset the input value
if (input) {
input.value = '';
}
this[ctrl].setValue(null);
}
}
selected(event: MatAutocompleteSelectedEvent, selectedItems, inputType, ctrl): void {
this[selectedItems].push(event.option.viewValue);
this[inputType].nativeElement.value = '';
this[ctrl].setValue(null);
}
整个ts文件
import { Component, OnInit, ElementRef, ViewChild, Inject } from '@angular/core';
import { FormBuilder, FormGroup, Validators, NgForm, FormControl } from '@angular/forms';
import { Router, ActivatedRoute, ParamMap } from '@angular/router';
import { ApiService } from '../../api.service';
import { forkJoin, Observable } from 'rxjs';
import { map, startWith, filter } from 'rxjs/operators';
import { MatChipsModule, MatAutocompleteSelectedEvent, MatChipInputEvent, MatAutocomplete, MatAutocompleteTrigger } from '@angular/material'
import {COMMA, ENTER} from '@angular/cdk/keycodes';
import {MatDialog, MatDialogRef, MAT_DIALOG_DATA} from '@angular/material';
import { DialogComponent } from '../../shared/dialog/dialog.component';
import { DialogQuestionService } from '../../shared/dialog/dialog-question.service';
import { a } from '@angular/core/src/render3';
@Component({
selector: 'app-item-create',
templateUrl: './item-create.component.html',
styleUrls: ['./item-create.component.css'],
providers:[DialogQuestionService]
})
export class ItemCreateComponent implements OnInit {
visible = true;
selectable = true;
removable = true;
addOnBlur = true;
readonly separatorKeysCodes: number[] = [ENTER, COMMA];
itemCreateForm: FormGroup;
modifierCtrl = new FormControl();
optionCtrl = new FormControl();
filteredModifiers: Observable<string[]>;
filteredOptions: Observable<string[]>;
categoryList: any[] = [];
optionList: any[] = []; //string[] = ['White Bread', 'Wheat Bread', 'Extra Mayo', 'Mustard', 'Grilled Onions', 'Toasted'];
modifierList: string[] = []; //string[] = ['Add Bacon', 'Add Avocado', 'Double Meat', 'Double Cheese'];
taxList: any; //string[] = ['Defaut Tax', 'Dine-in Tax', 'Hot Food Tax'];
componentList: any; //string[] = ['White Bread', 'Wheat Bread', 'Mayo', 'Turkey', 'Ham', 'Lettuce', 'Tomato', 'Onions'];
typeList: any;
//item = {};
selectedModifiers: string[] = [];
selectedOptions: any[] = [];
pageType = 'Create';
id = this.route.snapshot.params['id'];
formData:any;
//dialogQuestions:any;
@ViewChild(MatAutocompleteTrigger) autocomplete: MatAutocompleteTrigger;
@ViewChild('modifierInput') modifierInput: ElementRef<HTMLInputElement>;
@ViewChild('optionInput') optionInput: ElementRef<HTMLInputElement>;
// @ViewChild('auto') matAutocomplete: MatAutocomplete; //remove this
@ViewChild('autoModifier') autoModifier:MatAutocomplete;
// @ViewChild('autoOption') optionMatAutoComplete: MatAutocomplete;
constructor(
public service: DialogQuestionService,
private router: Router,
private route: ActivatedRoute,
private api: ApiService,
private fb: FormBuilder,
public dialog: MatDialog
){
//if the appropriate filters list is empty THEN open the modal in add()
this.filteredModifiers = this.modifierCtrl.valueChanges.pipe(
startWith(null),
map((modifier: string | null) => modifier ? this._filter(modifier, 'modifierList') : this.modifierList.slice() )
);
this.filteredOptions = this.optionCtrl.valueChanges.pipe(
startWith(null),
map((option: string | null) => option ? this._filter(option, 'optionList') : this.optionList.slice() )
);
this.createForm();
}
ngOnInit(){
this.setupForm(this.id);
}
setupForm(id) {
forkJoin(
this.api.getAll('Category'),
this.api.getAll('Modifier'),
this.api.getAll('Option'),
this.api.getAll('Tax'),
this.api.getAll('Component'),
this.api.getAll('Type')
).subscribe(([Categories, Modifiers, Options, Taxes, Components, Types]) => {
this.categoryList = Categories.map(c => c.name);
this.modifierList = Modifiers.map(c => c.name);
this.optionList = Options.map(c => c.name);
this.taxList = Taxes.map(c => c.name );
this.componentList = Components.map(c => c.name);
this.typeList = Types.map(c => c.name );
});
if(this.route.snapshot.data.update){
this.api.get('Item',id).subscribe( item => this.itemCreateForm.patchValue(item) );
this.pageType = 'Update';
}
}
createForm(){
this.itemCreateForm = this.fb.group({
name: '',
categories: [],
price: 0,
options: [],
components: [],
modifiers: [],
removals: [],
taxes: [],
description: '',
author: '',
});
}
add(event: MatChipInputEvent, selectedItems, allItemsList, ctrl, type, filteredList): void {
const options = {};
const input = event.input;
const value = event.value;
if(!this.autoModifier.isOpen || this[allItemsList].indexOf(value) < 0 ){ // just trying this out for modifiers so far, if it works make a switch or something
console.log('listy',filteredList, this.autoModifier);
// Add our new item to both the DB and local selected items
if ((value || '').trim()) {
if(!this[allItemsList].includes(value.trim())){
//in this case it is new
switch(type){
case 'category':
break;
case 'option':
options['Type'] = this.typeList;
options['Categories'] = this.categoryList;
break;
case 'component':
options['Options'] = this.optionList;
options['Categories'] = this.categoryList;
break;
case 'modifier':
options['Additions'] = this.optionList;
options['Removals'] = this.optionList;
options['Options'] = this.optionList;
options['Categories'] = this.categoryList;
options['Type'] = this.typeList;
break;
case 'tax':
break;
default:
break;
}
let dialogQuestions = this.service.getQuestions(type, options);
//open a modal to create that item
const dialogRef = this.dialog.open(DialogComponent, {
width: '70%',
data: {
dialog: this,
type: type,
details:{
name:value,
questions: dialogQuestions
}
},
});
dialogRef.afterClosed().subscribe(result => {
this[allItemsList].push(result.name);
this[selectedItems].push(result.name);
this.autocomplete.closePanel();//this doesnt work for some reason
//based on the result add it to the allItemsList and the selectedItems
//also create a chip for it
});
}
}
// Reset the input value
if (input) {
input.value = '';
}
this[ctrl].setValue(null);
}
remove(item: string, type): void {
console.log('removing from selected', this, item , type, this[type]);
const index = this[type].indexOf(item);
if (index >= 0) {
this[type].splice(index, 1);
}
}
selected(event: MatAutocompleteSelectedEvent, selectedItems, inputType, ctrl): void {
console.log('SELECTED HAS FIRED')
this[selectedItems].push(event.option.viewValue);
this[inputType].nativeElement.value = '';
this[ctrl].setValue(null);
}
focusOut() {
//change this to pass in the control as argument
this.modifierCtrl.disable();
this.modifierCtrl.enable();
// this seems to help but now im having issues with focus moving
// to the first chip when trying to select the input box.
// its good enough for now
}
private _filter(value: string, type): string[] {
const filterValue = value.toLowerCase();
return this[type].filter(item => item.toLowerCase().indexOf(filterValue) === 0);
}
onFormSubmit(form: NgForm) {
if (this.pageType === 'Create') {
console.log('Creating', form)
this.api.post('Item', form)
.subscribe(res => {
console.log('resres',res)
let id = res['_id'];
this.router.navigate(['menu/details', id]);
}, (err) => {
console.log(err);
});
} else if (this.pageType === 'Update') {
console.log('Updating', form)
this.api.update('Item', this.id, form)
.subscribe(res => {
console.log('resres',res)
let id = res['_id'];
this.router.navigate(['menu/details', id]);
}, (err) => {
console.log(err);
});
}
}
}
答案 0 :(得分:3)
我遇到了同样的问题,我通过将addOnBlur选项设置为false解决了它。 希望这对您有用!
答案 1 :(得分:0)
这似乎是组件的一个非常普遍的问题,主要是因为当您按ENTER键时,matChipInputTokenEnd和optionSelected会触发。然后,芯片将已经添加,并且输入将没有任何值可添加。也许这就是为什么您没有获得该方法的回调的原因。无论如何,请检查此答案,可能会有帮助:
[https://stackoverflow.com/a/52814543/5625648][1]
希望会有所帮助,加油。