0

I created a stackBliz that is a smaller example of what I am trying to acheive, but if I can get it to work there. Then, I should be able to figure out the rest.

https://stackblitz.com/edit/angular-nkajg5

I want to clear or the set the value of the component to '' when the input entered by a user is not an item from the suggested items in the mat-autocomplete. Then, I also want to display a mat-error to them that "Value entered is NOT VALID please selected only from suggested values."

I got it to work by following two examples I found. Clearing out the input

https://stackblitz.com/edit/autocomplete-force-selection-tests-w2fqww?file=app%2Fapp.component.html

Telling them an error message

https://stackblitz.com/edit/angular-kdym4u

However, I have three autocomplete components and it is causing issues when I try to use the method on all three components and call them from ngAfterViewInit. No error messages it just doesn't clear out the input or show the error message. So my function doesn't seem to be getting called. (to be honest not really sure what the subscription variable is used for in the code) I think it has to do something with the subscription and where I am calling it from the ngAfterViewInt(). I tested it out and only the first component works by itself. I tried the other two components by themeselves too and they don't work at all even when they are the only control being used. I have the three matAutocomplete in a tabGroup and the other two are in the second tab of the tabGroup.

My constructor with my form groups

  constructor(private dialogRecipient:MatDialog,private fb: FormBuilder, private dialogRef: MatDialogRef<UpdateTaskComponent>,private snackBar: MatSnackBar, 
    @Inject(MAT_DIALOG_DATA){Enabled ,TaskName, TaskDescription,EmailSubject, EmailBody, tTaskTeam, PK_Task}, private service: TaskService ) { 
      this.pk_Task = PK_Task;
      console.log(this.pk_Task)
      this.form = fb.group({
        enabled: [Enabled],
        tTaskTeam: [tTaskTeam, Validators.required],
        taskName: [TaskName],
        taskDescription: [TaskDescription],
        emailSubject: [EmailSubject],
        emailBody: [EmailBody]
      })

      this.AdditionalRecipientForm = fb.group({
        recipientType: ['',Validators.required],
        tofficeUser: ['',Validators.required],
        tOfficeUserEmail: '',
        FK_Task: [this.pk_Task]
      })

    }

How I am using the @ViewChild to get the MatAutoCompletes

  @ViewChild(MatAutocomplete) autoCompleteForTaskTeam: MatAutocomplete;
  @ViewChild('auto') auto: MatAutocomplete;
  @ViewChild('autotest') autotest: MatAutocomplete;
  @ViewChild(MatTable) table: MatTable<RecipientInterface>;
  @ViewChild(MatAutocompleteTrigger) trigger: MatAutocompleteTrigger;
  subscription: Subscription;

Where I am calling the method on the controls

ngAfterViewInit() {

If I only call the tTaskTeam control it works, but when I put all three it stops working

this._subscribeToClosingActions(this.form.controls.tTaskTeam); 
this._subscribeToClosingActions(this.AdditionalRecipientForm.controls.recipientType);
    this._subscribeToClosingActions(this.AdditionalRecipientForm.controls.tofficeUser);
        }

ngOnDestroy() {
  if (this.subscription && !this.subscription.closed) {
    this.subscription.unsubscribe();
  }
}

the method that shows the error, sets my control to '', and clears out if it is not a value from the suggested.

private _subscribeToClosingActions(control): void {
  if (this.subscription && !this.subscription.closed) {
    this.subscription.unsubscribe();
  }
  this.subscription = this.trigger.panelClosingActions
    .subscribe(e => {
      if (!e || !e.source) {
       control.setValue('');
      }
    },
    err => this._subscribeToClosingActions(control),
    () => this._subscribeToClosingActions(control));
}

template code:

<h2 mat-dialog-title>Update Entry</h2>
<mat-tab-group dynamicHeight>
  <mat-tab label="Task Information">
    <div class="example-small-box mat-elevation-z4">
      <mat-dialog-content  [formGroup]="form" class="example-form" >
        <mat-slide-toggle color="primary" (click)="textForToggle()" formControlName="enabled" >{{toggle ? 'Enabled': 'Disabled'}}</mat-slide-toggle>

            <mat-form-field class="task-info-form-field" >
                <input matInput placeholder="Report Name" formControlName="taskName">
            </mat-form-field>
            <br>
            <mat-form-field  appearance="outline" class="task-info-form-field" >

The first autoComplete component

<input matInput (keyup.enter)="chooseFirstOption(autoCompleteForTaskTeam)" [matAutocomplete]="autoCompleteForTaskTeam" formControlName="tTaskTeam" matTooltip="You can search and it will try to autocomplete the name for you!" placeholder="Select Group">
                  <mat-autocomplete #autoCompleteForTaskTeam='matAutocomplete'  [displayWith]="displayTeamName">
                    <mat-option class="matAutoCompleteSelect"*ngFor="let user of filteredOptions | async" 
                      [value]="user">
                      <span>{{ user.TeamName }}</span>
                    </mat-option>
                  </mat-autocomplete>
                  <mat-error >
                    Value entered is NOT VALID please selected only from suggested values.
                  </mat-error>
                </mat-form-field>
                <br>
                <mat-form-field class="task-info-form-field" >
                  <textarea #message matInput placeholder="Report Description"  formControlName="taskDescription"></textarea>
                  <mat-hint align="end">{{message.value.length}} / 256</mat-hint>
                  <mat-hint align="start"><strong>Put a detailed Description</strong> </mat-hint>
                  <mat-error >
                    Please enter less than 256 characters.
                  </mat-error>
                </mat-form-field>
                <br>
                <mat-form-field class="task-info-form-field" >
                  <input matInput placeholder="Email Subject: " formControlName="emailSubject">
                </mat-form-field>
                <br>
                <mat-form-field class="task-info-form-field">
                  <textarea matInput #message2 placeholder="Email Body"  formControlName="emailBody"></textarea>
                  <mat-hint align="end">{{message2.value.length}} / 256</mat-hint>
                  <mat-error >
                    Please enter less than 256 characters.
                  </mat-error>
                </mat-form-field>
          </mat-dialog-content>

        </div>
      </mat-tab>
      <mat-tab class="example-containerGrid" label="Recipient Information">

        <div class="example-containerGrid mat-elevation-z8">
          <div class="example-header">
            <mat-form-field class="searchGrid-form-field">
              <input matInput #filter (keyup)="applyFilter($event.target.value)" placeholder="Filter Doesn't Work At The Moment">
              <button mat-icon-button matSuffix aria-label="clear" *ngIf="filter.value" (click)="filter.value=''; applyFilter('');">
                <mat-icon>close</mat-icon>
              </button>
            </mat-form-field>
          </div>

          <mat-table #table [dataSource]="dataSource">

            <ng-container matColumnDef="Login">
              <mat-header-cell *matHeaderCellDef> Login </mat-header-cell>
              <mat-cell *matCellDef="let element"> {{element.tOfficeUser.Login}} </mat-cell>
            </ng-container>

            <ng-container matColumnDef="Email">
              <mat-header-cell class="actionsCell" *matHeaderCellDef> Email </mat-header-cell>
              <mat-cell *matCellDef="let element"> {{element.tOfficeUser.Email}} </mat-cell>
            </ng-container>

            <ng-container matColumnDef="typerecipient">
              <mat-header-cell *matHeaderCellDef> Type Of Recipient </mat-header-cell>
              <mat-cell *matCellDef="let element"> {{element.RecipientType.typerecipient}} </mat-cell>
            </ng-container>

            <ng-container matColumnDef="Actions">
                <mat-header-cell  *matHeaderCellDef> Actions  </mat-header-cell>
                <mat-cell  *matCellDef="let element"> {{element.Actions}}
                <div class="example-button-row">  
                  <button mat-raised-button color="primary"  (click)="updateRecipientInfo(element)"> <mat-icon class="example-icon" matTooltip="You can edit this report">edit</mat-icon> <b>Edit</b></button>
                  <!-- <button mat-raised-button (click)="test">  <mat-icon class="example-icon" matTooltip="This button is to copy reports"> filter_none </mat-icon><b>Copy</b></button> -->
                  <button mat-raised-button color="warn" (click)="deleteRecipient(element)">  <mat-icon class="example-icon" matTooltip="This is to delete this report"> delete_outline</mat-icon><b>Delete</b></button>
                </div>
              </mat-cell>
            </ng-container>

            <!-- Fav Column -->
            <!-- <ng-container matColumnDef="fav">
              <mat-header-cell *matHeaderCellDef> Favorite </mat-header-cell>
              <mat-cell *matCellDef="let element">
                <mat-form-field floatLabel="never">
                  <mat-select [(value)]="element.fav" placeholder="Favorite">
                    <mat-option>None</mat-option>
                    <mat-option value="Yes">Yes</mat-option>
                  </mat-select>
                </mat-form-field>
              </mat-cell>
            </ng-container> -->

            <mat-header-row *matHeaderRowDef="columnsToDisplay; sticky: true"></mat-header-row>
            <mat-row *matRowDef="let row; columns: columnsToDisplay;"></mat-row>
          </mat-table>
          <mat-card class="spinnerMatCard" *ngIf="isLoading">
              <mat-progress-spinner 
                color="primary" 
                mode="indeterminate">
              </mat-progress-spinner>
            </mat-card>
        </div>



        <div class="example-container mat-elevation-z8">
            <mat-accordion>
                <mat-expansion-panel [formGroup]="AdditionalRecipientForm"> 
                  <mat-expansion-panel-header [collapsedHeight]="customCollapsedHeight" [expandedHeight]="customExpandedHeight">
                    <mat-panel-title>
                      Add Additional Recipients
                    </mat-panel-title>
                    <mat-panel-description>
                      Type your name and age
                    </mat-panel-description>
                  </mat-expansion-panel-header>
                  <button mat-raised-button (click)="createNewRecipientUser()" [disabled]="!AdditionalRecipientForm.valid" [disabled]="!AdditionalRecipientForm.dirty" color="primary"><b>Update</b></button>
                  <button mat-raised-button  (click)="resetFields()" color="primary"><b>Reset</b></button>
                  <br>

                    <mat-form-field appearance="outline">
                        <mat-label>Login Name Field</mat-label>

The second autoComplete component

 matTooltip="You can search and it will try to autocomplete the name for you!" placeholder="Select Type Of Logins">

                <mat-hint>You can type in the name and it will autocomplete for you!</mat-hint>
              <mat-autocomplete #auto [displayWith]="displayLogin">
                  <mat-option class="matAutoCompleteSelect"*ngFor="let user of OfficeUserfilteredOptions | async" 
                    [value]="user">
                    <span>{{ user.Login }}</span>
                  </mat-option>
              </mat-autocomplete>
              <mat-error >
                Value entered is NOT VALID please selected only from suggested values.
              </mat-error>
            </mat-form-field>

            <mat-form-field class="add-addtional-recipients-form-field" appearance="outline">
                <mat-label>Email Field</mat-label>
              <input  matInput placeholder="User Email Address"   formControlName="tOfficeUserEmail">

              <mat-hint align="left"><strong>You cannot edit user email address here! Select a login to see the email.</strong> </mat-hint>
            </mat-form-field>

            <mat-form-field class="add-addtional-recipients-form-field" appearance="outline">
                <mat-label>Recipient Type</mat-label>

The third autoComplete component

   <input matInput [matAutocomplete]="autotest" (keyup.enter)="chooseFirstOptionForAutoTest()" formControlName="recipientType" matTooltip="You can search and it will try to autocomplete the name for you!" placeholder="Select Type Of Recipient">
                <mat-hint>Start typing in the type of reicpient you want. </mat-hint>
                <mat-autocomplete #autotest  [displayWith]="displayRecipientName">
                  <mat-option class="matAutoCompleteSelect"*ngFor="let user of filteredRecipientOptions | async" 
                    [value]="user">
                    <span>{{ user.typerecipient }}</span>
                  </mat-option>
                </mat-autocomplete>
                <mat-error >
                  Value entered is NOT VALID please selected only from suggested values.
                </mat-error>
              </mat-form-field>
        </mat-expansion-panel>
      </mat-accordion>
</div> 

Close Save

elquesogrand
  • 197
  • 4
  • 14
  • Show your template code. You are only subscribing to one autocomplete trigger - `this.trigger`. If you have 3 autocompletes, you probably need to subscribe to all 3 triggers - unless I have misunderstood your description (please clarify). – G. Tranter May 28 '19 at 16:55
  • @G.Tranter Hey I just updated the question with my template. Let me know if you need anything else. The trigger to me doesn't seem like it's subscribed to a specific autocomplete. Inside the private method _subscribeToClosingActions I am sending the control to it, so shouldn't the control I send already be subscribed? – elquesogrand May 28 '19 at 17:59
  • I created a stackblitz that is similar to what I am doing. https://stackblitz.com/edit/angular-nkajg5 – elquesogrand May 29 '19 at 03:47
  • @G.Tranter you were right... It was an issue with the trigger. I ended up solving it by creating a triggerCollection, I updated my stackBlitz with the code if someone ever needs it. Thank you again G. Tranter – elquesogrand May 29 '19 at 15:33

1 Answers1

0

If someone ever starts working with more than one autocomplete in a form and want to clear out the input if it's not one of the item suggested from your data and show an error message. I found a solution:

My problem is that the trigger I was trying to reference only finds the first autocomplete that it finds in the DOM.

 @ViewChild(MatAutocompleteTrigger) trigger: MatAutocompleteTrigger;

So, I need to subscribe to each MatAutoComplete trigger since each of them have their own. To be able to do that we have to create a

@ViewChildren(MatAutocompleteTrigger) triggerCollection: QueryList<MatAutocompleteTrigger>;

This will hold a collect of all the matAutComplete controls triggers.

From there we can go through the collection by making it an array and checking which of the controls is dirty.

private _subscribeToClosingActions(): void {

    if (this.subscription && !this.subscription.closed) {
      this.subscription.unsubscribe();
    }

for (var trigger of this.triggerCollection.toArray()) {
    this.subscription = trigger.panelClosingActions
      .subscribe(e => {
        if (!e || !e.source) {
        if(this.CarsFormGroup.controls.carCtrl.dirty) {
 this.CarsFormGroup.controls.carCtrl.setValue(null);
          }
        if(this.StatesFormGroup.controls.stateCtrl.dirty) {
 this.StatesFormGroup.controls.stateCtrl.setValue(null);
          }
        }
  });
}
  }
elquesogrand
  • 197
  • 4
  • 14