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()
)
);
}
}