简体   繁体   中英

Add ONLY the selected chip

My goal is to add a chip that has been selected. If there is no option, a chip should be added by the value which has been typed.

All <mat-option> values are from my database. My problem is that if I type something and there is an option selected, it will add 2 chips.

Example

The value JavaScript exists in my database and it will be displayed as <mat-option> . If I type Java (it could also be J ), JavaScript will be selected. So what I want is to add the chip JavaScript only (since it is selected) but not both of them. This is how it looks like:

在此处输入图像描述

HTML

<mat-grid-tile [formGroup]="this.primaryFormGroup">
  <mat-chip-list #chipList>
    <mat-chip *ngFor="let currentTechnology of currentTechnologies" [selectable]="selectable"
              [removable]="removable" (removed)="remove(currentTechnology)">
      {{currentTechnology.name}}
      <mat-icon matChipRemove *ngIf="removable">cancel</mat-icon>
    </mat-chip>
    <label>
      <input #techInput
             (keyup)="onKeyUp($event)"
             [matAutocomplete]="auto"
             [matChipInputFor]="chipList"
             [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
             [matChipInputAddOnBlur]="addOnBlur"
             (matChipInputTokenEnd)="add($event)"
             placeholder="Technologies"
             name="technologies"
             formControlName="technologies"
      >
    </label>
  </mat-chip-list>

    <mat-autocomplete autoActiveFirstOption #auto="matAutocomplete" (optionSelected)="selected($event)">
      <mat-option *ngFor="let data of technologies" [value]="data.technology">
        <span matBadge="{{data.counter}}" matBadgeOverlap="false">{{data.technology}} </span>
      </mat-option>
    </mat-autocomplete>
</mat-grid-tile>

TypeScript

import {Component, ElementRef, Input, OnInit, ViewChild} from '@angular/core';
import {COMMA, ENTER, SPACE, TAB} from '@angular/cdk/keycodes';
import {MatAutocomplete, MatAutocompleteSelectedEvent, MatChipInputEvent} from '@angular/material';
import {TechnologiesService} from './technologies.service';
import {SelectionService} from '../sections/selection.service';
import {FormBuilder, FormControl, NgForm, Validators} from '@angular/forms';
import countries from '../../../assets/json/countries.json';

@Component({
  selector: 'app-form',
  templateUrl: './form.component.html',
  styleUrls: ['./form.component.sass']
})
export class FormComponent implements OnInit {
  @ViewChild('techInput', {static: false}) techInput: ElementRef<HTMLInputElement>;
  @ViewChild('auto', {static: false}) matAutocomplete: MatAutocomplete;
  @ViewChild('customForm', {static: true}) customForm: NgForm;
  @Input() section: string;

  separatorKeysCodes = [COMMA, SPACE, TAB, ENTER];
  selectable = false;
  removable = true;
  addOnBlur = true;
  technologies = [];
  setTechnologies;
  currentTechnologies = [];
  minDate = new Date();
  countries = countries;

  // Primary form group
  primaryFormGroup = this.fb.group({
    title: '',
    description: '',
    gender: '',
    city: '',
    country: '',
    language: '',
    highestEducation: '',
    dateOfBirth: '',
    workload: '',
    typeOfTask: '',
    hourlyRate: '',
    paymentTime: '',
    minPrice: '',
    maxPrice: '',
    deadlineFrom: '',
    deadlineUntil: '',
    technologies: '',
    milestones: [false, Validators.requiredTrue]
  });
  // Important form group
  importantFormGroup = this.fb.group({
    gender: [false, Validators.requiredTrue],
    city: [false, Validators.requiredTrue],
    country: [false, Validators.requiredTrue],
    language: [false, Validators.requiredTrue],
    highestEducation: [false, Validators.requiredTrue],
    dateOfBirth: [false, Validators.requiredTrue],
    workload: [false, Validators.requiredTrue],
    hourlyRate: [false, Validators.requiredTrue],
    paymentTime: [false, Validators.requiredTrue]
  });

  constructor(private technologiesService: TechnologiesService, private selection: SelectionService, private fb: FormBuilder) {}

  // Form Control
  required = new FormControl('', Validators.required);
  hourlyRate = new FormControl('', Validators.max(200));

  ngOnInit() {
    // Set the min date
    this.minDate = new Date(this.minDate.getFullYear(), this.minDate.getMonth(), this.minDate.getDate());

    // Modify the form object
    this.primaryFormGroup.valueChanges.subscribe(inputFields => {
      if (inputFields) {
        // Change technologies
        if (inputFields.technologies) {
          // inputFields.technologies = Array.from(this.setTechnologies);
          // delete inputFields.technologies;
        }
        // Change type-of-task
        const typeOfTask = inputFields.typeOfTask;
        if (typeOfTask) {
          if (typeOfTask === 'project') {
            inputFields.project = 1;
          } else if (typeOfTask === 'feature') {
            inputFields.feature = 1;
          } else if (typeOfTask === 'bugfix') {
            inputFields.bugfix = 1;
          } else if (typeOfTask === 'other') {
            inputFields.other = 1;
          }
          delete inputFields.typeOfTask;
        }
        // Change tech
        const inputEntries = Object.entries(inputFields).filter(([key, value]) => value);
        // console.log('result:', inputEntries);
      }
    });
  }

  // Get the current section
  getSelectedSection() {
    return this.selection.getSection();
  }

  // On Key up, show technologies
   onKeyUp(event: any): void {
    if (event.target.value.trim().length > 0) {
      this.technologiesService.getTechnologies(event.target.value)
        .subscribe(data => {
          if (JSON.stringify(this.technologies) !== JSON.stringify(data)) {
            this.technologies = data;
          }
        });
    }
  }

  // On enter, add technology
  onEnter(evt: any) {
    if (evt.source.selected) {
      this.add(evt.source);
      evt.source.value = '';
    }
  }
  // Add technologies
  add(event: MatChipInputEvent): void {
    if (!this.matAutocomplete.isOpen) {
      console.log('add');
      const input = event.input;
      const value = event.value;

      if ((value || '').trim()) {
        // E.g., { "name": "Java" }
        this.currentTechnologies.push({name: value.trim()});
        // Set technologies without keys
        if (this.setTechnologies) {
          this.setTechnologies.add(value);
        } else {
          this.setTechnologies = new Set();
          this.setTechnologies.add(value);
        }
      }

      // Reset the input value
      if (input) {
        event.input.value = '';
      }
    }
  }

  // Select the autocomplete
  selected(event: MatAutocompleteSelectedEvent): void {
    console.log('select');
    if (!JSON.stringify(this.currentTechnologies).includes(`{"name":"${this.techInput.nativeElement.value.trim()}"`)) {
      this.currentTechnologies.push({name: this.techInput.nativeElement.value.trim()});
      this.techInput.nativeElement.value = '';
    }
  }

  // Remove technology
  remove(tech: any): void {
    const index = this.currentTechnologies.indexOf(tech);

    if (index >= 0) {
      this.currentTechnologies.splice(index, 1);
    }
  }

}

I think the main problem is that if I select an option, both functions ( selected() and add() ) will be executed. As you see, I have already tried it with this.matAutocomplete.isOpen but the return is ALWAYS false.

I have found an example on Stackblitz , which works exactly the way I want it. But I have spent more than 10 hours to fix my code to make it work the same but I have NO IDEA what exactly is wrong with my code.

If you have an idea what it could be, I would REALLY appreciate it!

Updates

  • If I remove this.currentTechnologies.push({name: value.trim()}); (in function add() ), ONLY the selected option will be added, which is good but if I type anything else which is not in my array technologies , that will not be added.
  • If I remove this.currentTechnologies.push({name: this.techInput.nativeElement.value.trim()}); (in function selected() ), only the value that I have typed will be added.

Try to remove the addOnBlur option or set it to false in yout ts file. That worked for me!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM