MG7
MG7

Reputation: 153

Is there a way to enable the autocomplete for angular reactive form?

I want to set on the autocomplete attribute for an angular form but it doesn't work as expected. It remembers only the values that I submitted only the first time and I would like to remember and suggest all the values no matter how many times I click on submit button.

Here is the stackblitz with the code that I tried.

<form
  autocomplete="on"
  (ngSubmit)="onSubmit()"
  name="filtersForm"
  [formGroup]="formGroup1"
>
  <div>
    <label>First Name</label>
    <input
      id="firstName"
      name="firstName"
      autocomplete="on"
      formControlName="firstName"
    />
  </div>
  <div>
    <label>Last Name</label>
    <input
      id="firstName"
      name="lastName"
      autocomplete="on"
      formControlName="lastName"
    />
  </div>
  <button type="submit">Submit</button>
</form>

Here are the details about the autocomplete attribute that I used.

In Firefox, the autocomplete is working after several clicks on Submit button, the problem is in Chrome and Edge.

Is there a way to make the autocomplete to work for inputs inside the angular form?

Upvotes: 1

Views: 2006

Answers (4)

Pankaj Parkar
Pankaj Parkar

Reputation: 136144

I think, I have found a workaround, that only works with Template Driven Form.


TLDR;

What I have discovered while looking after this issue.

  • On first form submit autofill remember only first time submission values
  • form submit POST method can remember all values.

Yes, by looking at above, it clearly seems like 2nd way is suitable for us. But why would anybody do form POST for submitting form to BE. There should be better way to tackle this. Otherwise we would have to think of handling PostBack 😃😃 (FW like .Net does it by keeping hidden input's).

Don't worry we can do some workaround here to avoid form POST. I found an answer for handling POST call without page refresh.

Working JSBin with plain HTML and JS

AutoCompleteSaveForm = function(form){
    var iframe = document.createElement('iframe');
    iframe.name = 'uniqu_asdfaf';
    iframe.style.cssText = 'position:absolute; height:1px; top:-100px; left:-100px';
    document.body.appendChild(iframe);
    var oldTarget = form.target;
    var oldAction = form.action;
    form.target = 'uniqu_asdfaf';
    form.action = '/favicon.ico';
    form.submit();
    setTimeout(function(){
        form.target = oldTarget;
        form.action = oldAction;
        document.body.removeChild(iframe);
    });
}

Basically we change set few things on form attribute.

  1. target="iframe_name" - Connects to iFrame to avoid page refresh.
  2. method="POST" - POST call
  3. url="/favicon" - API url to favicon (lightweight call)

In angular you can create an directive for the same.

import { 
  Directive, ElementRef, EventEmitter,
  HostBinding, HostListener, Input, Output,
} from '@angular/core';

@Directive({
  selector: '[postForm]',
})
export class PostFormDirective {
  @HostBinding('method') method = 'POST';

  @HostListener('submit', ['$event'])
  submit($event) {
    $event.preventDefault();
    this.autoCompleteSaveForm(this.el.nativeElement);
  }

  constructor(private el: ElementRef) {}

  autoCompleteSaveForm(form) {
    let iframe = document.querySelector('iframe');
    if (!iframe) {
      iframe = document.createElement('iframe');
      iframe.style.display = 'none';
    }
    iframe.name = 'uniqu_asdfaf';

    document.body.appendChild(iframe);
    var oldTarget = form.target;
    var oldAction = form.action;
    form.target = 'uniqu_asdfaf';
    form.action = '/favicon.ico'; // dummy action
    form.submit();
    setTimeout(() => {
      // set back the oldTarget and oldAction
      form.target = oldTarget;
      form.action = oldAction;
      // after form submit 
      this.onSubmit.emit();
    });
  }

  @Output() onSubmit = new EventEmitter();

  ngOnDestroy() {
    let iframe = document.querySelector('iframe');
    if (iframe) {
      document.body.removeChild(iframe);
    }
  }
}

Okay, so far everything went well. Then I started integrating this in formGroup(Model Driven Form), somehow it didn't worked. It does not store value next time these fields.

<form (ngSubmit)="onSubmit()" [formGroup]="formGroup1" autocomplete="on">
  <div>
    <label>First Name</label>
    <input id="firstName" name="firstName" formControlName="firstName" />
  </div>
  <div>
    <label>Last Name</label>
    <input id="lastName" name="lastName" formControlName="lastName" />
  </div>
  <button>Submit</button>
</form>

Later I tried the same with Template Driven Form. It just worked like a charm! I did not went into the depth why it didn't work for Model Driven Form (perhaps that investigation could eat more time).

<form #form1="ngForm" ngForm postForm (onSubmit)="onSubmit(form1)">
  <ng-container [ngModelGroup]="userForm">
    <div>
      <label>First Name</label>
      <input name="firstName" [(ngModel)]="userForm.firstName" />
    </div>
    <div>
      <label>Last Name</label>
      <input name="lastName" [(ngModel)]="userForm.lastName" />
    </div>
  </ng-container>

  <button>Submit</button>
</form>

Yeah, I just said in the begining it works only with Template Driven Form. So you would have to switch to Template. And one more important thing to note, you may think of creating dummy POST api call, that can be lightweight rather than hitting favicon.

Stackblitz

Upvotes: 1

Yozmo
Yozmo

Reputation: 581

You have to create an array with your desired options which should be displayed as autocomplete. You can have a look here https://material.angular.io/components/autocomplete/examples, there are multiple examples which should help you. Even if you're not using Angular Material, the logic would be the same

Upvotes: 0

You just need to remove autocomplete="on" in input tag. With chrome, we only add attribute autocomplete="on" in form element and it will be cached all value that user input into input text. Result will be like this: enter image description here

<form
  autocomplete="on"
  (ngSubmit)="onSubmit()"
  name="filtersForm"
  [formGroup]="formGroup1"
>
  <div>
    <label>First Name</label>
    <input
      id="firstName"
      name="firstName"
      formControlName="firstName"
    />
  </div>
  <div>
    <label>Last Name</label>
    <input
      id="firstName"
      name="lastName"
      formControlName="lastName"
    />
  </div>
  <button type="submit">Submit</button>
</form>

Upvotes: 0

Sergey
Sergey

Reputation: 7692

autocomplete attribute works only with submitted values. It has nothing to do with Angular.

https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete

If you need some custom behavior then you are better off creating your own component to autocomplete user's input, this way you can use some default values, add more of them on blur etc.

Upvotes: 0

Related Questions