Mark

Mark

  • NA
  • 28
  • 14.3k

Required validation on mat-chip-list

Nov 5 2021 12:37 AM

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

 


Answers (1)