Show hidden mat-option when all the others are filtered out

0

Issue

So I have this task where I have to create multiple <mat-select>s dynamically, depending how many "tag types" are returned from the backend. Moreover the <mat-select>s are populated with data (tags). A user can create a new "tag type", which means a new <mat-select> needs to be created. The <mat-select>s also have a filter. Now here is where I’m stuck, I need to show a default <mat-option> for creating a new tag (otherwise hidden), when the filter filters out all the options. Nothing seems to work, I tried putting (change)="onFilterChange()" on the <mat-select> tag and tried to check the length of the <mat-select> options (accessed it from @ViewChild('select')), if it is one switch the boolean to true, to show the default option for creating a tag. This didn’t work though. Also, (selectionChange) doesn’t help me at all because that is not triggered when filtering the options. Any help or insight is much appreciated. Here is the code:

<div class="col-md-6">
    <form *ngIf="preferencesFormGroup" [formGroup]="preferencesFormGroup">
      <mat-form-field *ngFor="let tagType of tagTypes$ | async">
        <mat-select placeholder="{{tagType.name}}" multiple #select [formControlName]="tagType.name">
          <mat-select-trigger>
            <mat-chip-list>
              <mat-chip *ngFor="let tag of preferencesFormGroup.controls[tagType.name].value" [removable]="true"
                (removed)="onTagsRemoved(tagType.name, tag)">
                {{ tag }}
                <mat-icon matChipRemove>cancel</mat-icon>
              </mat-chip>
            </mat-chip-list>
          </mat-select-trigger>
          <div class="search-wrapper">
            <input matInput placeholder="Search tags" [formControlName]="tagType.name + 'Search'" type="text"
              id="searchInput">
            <a mat-icon-button id="resetButton" (click)="clearFilter(tagType.name)">
              <mat-icon aria-label="Icon-button for clearing the input" id="clearIcon" [inline]="true">clear</mat-icon>
            </a>
          </div>
          <hr id="hrSearch">
          <mat-option
          *ngFor="let tag of tagType.tags | contains : preferencesFormGroup.controls[tagType.name+'Search'].value"
          [value]="tag.name">{{tag.name}}</mat-option>
          <mat-option *ngIf="noMoreOptions" (click)="createNewTag(tagType.name)"><i>Didn't find the tag you want? Click here to create your own.</i></mat-option>
        </mat-select>
      </mat-form-field>
    </form>
    <div class="row" style="align-items: center;">
      <div class="col-md-6">
        <div class="row" *ngIf="addDropDownActive" style="align-items: center;">
          <div class="col-md-8">
            <mat-form-field>
              <input matInput placeholder="Add new preference" type="text" [(ngModel)]="newPreference">
            </mat-form-field>
          </div>
          <div class="col-md-4">
            <button mat-button mat-stroked-button color="accent" (click)="createDropdown()"
              [disabled]="(newPreference === null) || (newPreference === '')">Save</button>
          </div>
        </div>
      </div>
      <div class="col-md-6">
        <button mat-button (click)="switchVisible()" color="accent" class="float-right">{{preferenceBtn}}</button>
      </div>
    </div>

    <div class="row mt-3">
      <div class="col-md-6">
        <button mat-button mat-stroked-button class="float-left" color="accent"
          (click)="goToPreviousStep()">Back</button>
      </div>
      <div class="col-md-6">
        <button mat-button mat-raised-button class="float-right" color="accent" (click)="goToNextStep()">Next</button>
      </div>
    </div>
  </div>

and the .ts file:

@Component({
  selector: 'tours-tour-tag',
  templateUrl: './tour-tag.component.html',
  styleUrls: ['./tour-tag.component.css']
})
export class TourTagComponent implements OnInit {

  tagTypes$: Observable<TagType[]>;
  preferencesFormGroup: FormGroup;
  addDropDownActive = false;
  newPreference: string = null;
  preferenceBtn = 'Create new preference';
  filterPropertySearch = '';
  noMoreOptions = false;

  @ViewChild('select') matSelect: MatSelect;

  constructor(private store: Store) { }

  ngOnInit(): void {
    this.tagTypes$ = this.store.select(TagTypeSelectors.selectAllTagTypes);
    this.tagTypes$.subscribe((tagTypes) => { this.initForm(tagTypes) });
  }

  onTagsRemoved(tagTypeName: string, tag: string) {
    const tags = this.preferencesFormGroup.controls[tagTypeName].value as string[];
    this.removeFirst(tags, tag);
    this.preferencesFormGroup.controls[tagTypeName].setValue(tags);
  }

  private removeFirst<T>(array: T[], toRemove: T): void {
    const index = array.indexOf(toRemove);
    if (index !== -1) {
      array.splice(index, 1);
    }
  }

  switchVisible() {
    this.addDropDownActive = !this.addDropDownActive;
    if (this.addDropDownActive) {
      this.preferenceBtn = 'Close';
    } else {
      this.preferenceBtn = 'Create new preference';
    }
    this.newPreference = null;
  }

  initForm(tagTypes: TagType[]) {
    this.preferencesFormGroup = new FormGroup({});
    tagTypes.forEach((tagType: TagType) => {
      this.preferencesFormGroup.addControl(tagType.name, new FormControl([]));
      const searchFormControl = new FormControl([]);
      searchFormControl.setValue('');
      this.preferencesFormGroup.addControl(tagType.name + 'Search', searchFormControl);
    });
    console.log(this.preferencesFormGroup);
  }

  createDropdown() {
    const newTagType: TagType = { id: null, code: null, name: this.newPreference, tags: null };
    this.store.dispatch(TagTypeActions.createTagType({ tagType: newTagType }));
    this.switchVisible();
  }

  clearFilter(tagTypeName: string): void {
    const searchControlName = tagTypeName.concat('Search');
    this.preferencesFormGroup.controls[searchControlName].setValue('');
  }

  createNewTag(tagTypeName: string) {
    console.log('open modal component');
  }
}

Solution

The problem is the variable "noMoreOption" -really you need an array of booleans, not only a simple variable-. But better than to create an array, make that Angular make the work for you.

You can make it using *ngIf to store a variable in the way <ng-container *ngIf="what-ever as items">

So, you can use some like (*)

 <ng-container *ngIf="tagType.tags | contains : 
                       preferencesFormGroup.controls[tagType.name+'Search'].value 
                as items">
    <mat-option *ngFor="let tag of items"
              [value]="tag.name">{{tag.name}}
    </mat-option>
    <mat-option *ngIf="items.length==0"
                (click)="createNewTag(tagType.name)">
           <i>Didn't find the tag you want? Click here to create your own.</i>
    </mat-option>
<ng-container>

(*)I imagine your pipe contains return an empty array if no match, not a null

Answered By – Eliseo

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave A Reply

Your email address will not be published.

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept Read More