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
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. 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.