角形材料optionSelected在输入字段中的值时不会触发

时间:2019-05-01 00:59:47

标签: angular angular-material2

我正在使用具有自动完成功能的角形材料输入芯片,我的设置有些复杂,如果用户尝试选择尚不存在的选项,则会弹出对话框,他们可以创建一个新选项,为此创建了一个芯片。

问题是当用户输入选项名称的一部分时,例如“ 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);
        });
    }
  }

}


2 个答案:

答案 0 :(得分:3)

我遇到了同样的问题,我通过将addOnBlur选项设置为false解决了它。 希望这对您有用!

答案 1 :(得分:0)

这似乎是组件的一个非常普遍的问题,主要是因为当您按ENTER键时,matChipInputTokenEnd和optionSelected会触发。然后,芯片将已经添加,并且输入将没有任何值可添加。也许这就是为什么您没有获得该方法的回调的原因。无论如何,请检查此答案,可能会有帮助:

[https://stackoverflow.com/a/52814543/5625648][1]

希望会有所帮助,加油。