Villhellm
Villhellm

Reputation: 69

Angular 7 Material | mat-select autofill on keypress not resetting the source after it is cleared

I want to be able to control this component with the keyboard alone as much as possible. I do this by making sure you are pointed to the proper control on a tab press. On the last form field when you press tab it adds the temporary item to the list, resets the temporary item, and focuses you back on the first line item field.

The problem is if I want to have 2 line items with the same account, the second time I try to select it by pressing the first letter of the account name (in this case 'c') it skips an option because it was previously selected. I would like the options to reset when the temporary item is cleared.

To recreate the issue I'm trying to describe:

Enter any number for amount

Hit tab

Hit c (it will go to "Car Insurance")

Hit tab to submit the line and start at amount again

Enter an amount

Hit tab

and now if you try pressing 'c' again "Car Insurance" is no longer the first option. It defaults to the next item in the list for some reason.

I do not get the same issue if I use standard and tags, only when I use the material equivalents.

Here is the relevant code https://stackblitz.com/edit/angular-tab8tm

Upvotes: 2

Views: 1637

Answers (1)

Marshal
Marshal

Reputation: 11081

Because you are technically still using the same mat-select... and not creating a new one... the old value is still in the mat-select...

There is a keyManager behind the MatSelect component managing all of this logic, and because you are pushing the current values into a new array and resetting the ngModel... you will also need to reach into the keyManager and default the values.


Create a templateRef for the root mat-select of #select

<mat-form-field class="temp-item-field" *ngIf="accounts">
        <mat-select #select [(ngModel)]="tempItem.accountId" placeholder="Account">
          <mat-option *ngFor="let account of accounts" [value]="account.id">{{account.name}}</mat-option>
        </mat-select>
      </mat-form-field>

Create @ViewChild in component to get the reference

 @ViewChild('select') _select

In your addItem() set the activeItem and activeItemIndex to their default values.

addItem() {
    if (this.tempItem.amount && this.tempItem.accountId) {
      this.tempItem.transactionId = this.transaction.id;
      this.transaction.transactionItems.push(this.tempItem);
      this.tempItem = new TransactionItem();
      this.focusTransactionAmount();
      this._select['_keyManager']['_activeItem'] = undefined;
      this._select['_keyManager']['_activeItemIndex'] = -1;     
    }
  }

Stackblitz

https://stackblitz.com/edit/angular-ywrz7v?embed=1&file=src/app/app.component.ts


Personally I would recommend you explore using ReactiveForms for this and generate your new fields via a FormControlArray... instead of "shifting" your current values out of the current fields into new ones, and resetting the original root fields...

You could basically do the inverse of what you are doing now... create new fields with new formControls and new state on tab leaving the current entered values where they are... and essentially be able to avoid this and using ngModel all together.

Please see this SO answer where I provided a walk through of how the reactive forms work in an *ngFor loop... along with stackblitz example.

The buildForm() in the stackblitz would essentially be what you need to get your addItem() pushing new formControls which create new fields in the view automatically.

Dynamic form using *ngFor and submitting values from it

Upvotes: 1

Related Questions