0

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?

MG7
  • 119
  • 13
  • This doesn't seem to be an angular-specific issue, will update if I find any solution. – Pankaj Parkar Jul 24 '22 at 06:20
  • It's up on with web browser, not Angular issue. Alternative you can find other way on css framework that will work for you (eg. http://primefaces.org/primeng/autocomplete) – paranaaan Jul 26 '22 at 08:19

4 Answers4

1

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

Pankaj Parkar
  • 134,766
  • 23
  • 234
  • 299
  • thank you for your effort, it's a good idea to use an iframe. Looks like your solution works in Edge, but in my Chrome version still not working. I will use your solution with iframe and continue to search for the Chrome problem. – MG7 Jul 29 '22 at 08:02
  • I had it working on chrome as well :) And I tested on Chrome only. I don't know why it does not work for you, which chrome version you have? – Pankaj Parkar Jul 29 '22 at 08:06
  • Version 103.0.5060.134 (Official Build) (64-bit) – MG7 Jul 29 '22 at 08:12
  • @Magda I had `103.0.5060.53` on Mac (it works on it), let me update and try again. – Pankaj Parkar Jul 29 '22 at 10:56
  • @Magda OMG! it does not work after an update. How come that is possible? Let me dig a bit deeper into it again. – Pankaj Parkar Jul 29 '22 at 10:59
  • thank you for your effort. I used the code from your autoCompleteSaveForm and ngOnDestroy methods inside my submit method and it works. This works in a Model Driven Form angular form and with the Chrome version 103.0.5060.134. – MG7 Aug 04 '22 at 11:39
  • @Magda fantastic to know that. I don't know what Chrome is changing with each version, when I did autocomplete workaround, it was only working with template-driven. Now model-driven froms. LOL :) – Pankaj Parkar Aug 04 '22 at 16:04
0

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.

Sergey
  • 7,184
  • 13
  • 42
  • 85
  • In angular form that I added, the autocomplete works only with the first submitted values. If I submit a second time new values, these new values are not remembered. This is why I think the issue is because of the Angular form. In my case, the input has to suggest all the previous submitted values, exactly what autocomplete should do. – MG7 Jul 25 '22 at 08:12
  • Do you refresh the page between the attempts? For forms it's customary to load another page after submit. – Sergey Jul 25 '22 at 08:38
  • I don't refresh the page, I have to stay on the same page in order to display the backend validation errors in case there are any. – MG7 Jul 25 '22 at 09:20
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>
0

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

Yozmo
  • 521
  • 2
  • 10