I created a dialog modal form that I want to use required validation on a select dropdown list. This dropdown list is called Entities in the add-contact-dialog.component.html file below. I am using app-chips component in that form.
mat-chip-list is actually inside of another component file called chips.component.html. How do I get the required validation to work for the app-chips component. Basically I'm trying to get the map-chip-list in one file to connect the app-chips component Entities dropdown in another for required validation.
I need help making the Entities dropdown a required field with the code used below. I am not sure how to do this with the way this is setup.
I would guess that it would be a similar setup to the hint/mat-hint but I'm not sure.
add-contact-dialog.html
<app-modal [title]="title" [onOkClick]="onSave" [okDisabled]="addRemoveForm.invalid"> <form [formGroup]="addRemoveForm" novalidate> <div class="form-group row"> <div class="col-md-5"> <mat-form-field> <mat-label>First Name</mat-label> <input matInput type="text" aria-labelledby="firstName" formControlName="firstName" maxlength="100" required /> <mat-error *ngIf="addRemoveForm.controls['firstName'].invalid"> First Name is <strong>required</strong> </mat-error> </mat-form-field> </div> <div class="col-md-2"> <mat-form-field> <mat-label>M.I</mat-label> <input matInput type="text" aria-labelledby="middleInitial" formControlName="middleInitial" maxlength="1" /> </mat-form-field> </div> <div class="col-md-5"> <mat-form-field> <mat-label>Last Name</mat-label> <input matInput type="text" aria-labelledby="lastName" formControlName="lastName" maxlength="100" required /> <mat-error *ngIf="addRemoveForm.controls['lastName'].invalid"> Last Name is <strong>required</strong> </mat-error> </mat-form-field> </div> </div> <div class="col-md-12"> **<app-chips label="Entities" placeholder="Add Entity..." [options]="entityOptions" [selectedOptions]="selectedEntities" [hint]=" entityListHasDisabledOptions === true ? 'Please remove this contact from any roles for an entity prior to removing their association to that entity.' : undefined " (onRemoved)="removeEntity($event)" </app-chips>** </div> </form>
add-contact-dialog.ts - onSave method for dialog modal form
onSave = (): void => { this.addAddressToModel(); this.addPhoneNumbersToModel(); const dob = this.addRemoveForm.controls['dateOfBirth'].value; this.contact.isActive = true; this.contact.entityIds = this.selectedEntities.map((e) => e.value); this.contact.firstName = this.addRemoveForm.controls['firstName'].value; this.contact.lastName = this.addRemoveForm.controls['lastName'].value; this.contact.dateOfBirth = dob ? DateTime.fromISO(dob) : undefined; this.contact.contactTypeCode = this.contact.contactTypeCode ? this.contact.contactTypeCode : 'SiteContact'; this.contact.startMonth = this.addRemoveForm.controls['startMonth'].value; this.contact.startYear = this.addRemoveForm.controls['startYear'].value; this.contact.emailAddress = this.addRemoveForm.controls['email'].value; this.contact.titlePosition = this.addRemoveForm.controls['title'].value; this.contact.middleInitial = this.addRemoveForm.controls['middleInitial'].value; const mi = this.addRemoveForm.controls['middleInitial'].value ? this.addRemoveForm.controls['middleInitial'].value + ' ' : ''; this.contact.fullName = `${this.contact.firstName} ${mi} ${this.contact.lastName}`; this.contactsDirectoryClientService.postContact(this.contact).subscribe( (data) => { if (data && data.contactId && data.contactId > 0) { this.data.contactId = data.contactId; this.snackBar.success('Contact Information Saved.', '', 5000); this.dialogRef.close({ refreshTable: true, data: data, }); } }, (error) => { this.snackBar.error('ERROR! Contact Information NOT Saved', 'Dismiss', 0); this.dialogRef.close({ refreshTable: false, data: null, }); } ); };
onSave method for the dialog modal form
chips.component.html
In this file, I am using the mat-chip-list to contain the item. This is the mat-chip component for the properties used in the file above.
<mat-form-field [floatLabel]="floatLabel"> <mat-label>{{ label }}</mat-label> <mat-chip-list #optionList aria-label="label" required> <mat-chip *ngFor="let item of selectedOptions" (removed)="onRemoved.emit(item)" [removable]="!item.disabled" [disabled]="item.disabled"> {{ item.text }} <mat-icon *ngIf="!item.disabled" matChipRemove>cancel</mat-icon> </mat-chip> <input #optionInput type="text" [placeholder]="placeholder" [formControl]="formControl" [matAutocomplete]="optionAutoComplete" [matChipInputFor]="optionList" /> <mat-error> {{ required }}</mat-error> </mat-chip-list> <mat-autocomplete #optionAutoComplete="matAutocomplete" (optionSelected)="selected($event.option.value)"> <mat-option *ngFor="let option of filteredOptions | async" [value]="option"> {{ option.text }} </mat-option> </mat-autocomplete> <mat-hint *ngIf="hint">{{ hint }}</mat-hint> </mat-form-field>
chips.component.ts
@Component({ selector: 'app-linq-chips', templateUrl: './linq-chips.component.html', }) export class LinqChipsComponent implements OnInit, DoCheck { @Input() label = ''; @Input() placeholder = ''; @Input() options: Options[] = []; @Input() selectedOptions: Options[] = []; @Input() floatLabel: FloatLabelType = 'auto'; @Input() hint!: string | undefined; @Input() required!: string | undefined; @ViewChild('optionInput') optionInput: ElementRef | undefined; @Output() onRemoved = new EventEmitter<Options>(); @Output() selectedOptionsChanged = new EventEmitter<Options[]>(); formControl = new FormControl(''); filteredOptions: Observable<Options[]> | undefined; iterableDiffer: IterableDiffer<Options>; constructor(private readonly iterableDiffers: IterableDiffers) { this.iterableDiffer = this.iterableDiffers.find([]).create(); } ngDoCheck(): void { const optionChanges = this.iterableDiffer.diff(this.options); if (optionChanges) { this.filteredOptions = of(this.options); } } ngOnInit(): void { this.subscribeFilterOptions(); } selected(value: Options): void { if (this.optionInput) { this.optionInput.nativeElement.value = ''; } if (!this.selectedOptions.find((x) => x.text === value.text)) { this.selectedOptions.push(value); this.selectedOptionsChanged.emit(this.selectedOptions); } } private subscribeFilterOptions() { this.filteredOptions = this.formControl.valueChanges.pipe( startWith(''), map((value: string | Options) => value && typeof value === 'string' ? this.options.filter((o) => o.text.toLowerCase().includes(value.toLowerCase())) : this.options.slice() ) ); } }