angular / angular

Deliver web apps with confidence πŸš€
https://angular.dev
MIT License
94.89k stars 24.77k forks source link

forms: update required validator when input is autofilled #30616

Open mmalerba opened 5 years ago

mmalerba commented 5 years ago

πŸš€ feature request

Relevant Package

This feature request is for @angular/forms ### Description The `RequiredValidator` does not update its validation status when an input is autofilled in Chrome. This is problematic for users who set up a required username and password input and then conditionally enable the submit button when the inputs are valid. The user sees: 1. Page loads 2. Inputs are autofilled with username and password 3. Submit button is still disabled. This issue has been filed in the past on the components repo: https://github.com/angular/components/issues/3414, though the change would really have to happen in @angular/forms. ### Describe the solution you'd like @angular/cdk has an `AutofillMonitor` that could be moved to @angular/forms and used to detect when an input is autofilled. ### Describe alternatives you've considered It is possible to monkey patch a solution, as @kherock [demonstrated](https://stackblitz.com/edit/autofillmon-validate?file=app/autofillvalueaccessor.directive.ts) on the components issue.
cyraid commented 4 years ago

I came across this bug. It's terrible because it's random when it happens! Sometimes it shows up correctly, sometimes it does not. The ng-invalid and such classes are added to it, until the user clicks on the page.

rbinsztock commented 4 years ago

Any news on this issue ? related to : https://bugs.chromium.org/p/chromium/issues/detail?id=352527 and https://github.com/vuejs/vue/issues/7058. We updated to last Angular 9.2 but the issue is still here.

juansanluq commented 3 years ago

Yes, the issue still there, any news about it? Thanks.

K0ST4S commented 3 years ago

Hello. The issue is still there, and none of the hacks or patches found on internet works. This is really frustrating, as form button is shown to be disabled, because validation cannot read the text written to the input fields by the browser. Chrome version: 87

anton-brass commented 3 years ago

I had a problem with FireFox 30.0 on iOS and its autocompletion-functionality for my login page.

I implemented following workaround:

export class LoginComponent implements OnInit, AfterViewInit {

  @ViewChild('loginFeld')
  loginFeld: ElementRef;

  @ViewChild('passwortFeld')
  passwortFeld: ElementRef;

  loginForm: FormGroup;

  ngOnInit(): void {
    this.loginForm = this.formBuilder.group({
      login: this.formBuilder.control('', Validators.required),
      passwort: this.formBuilder.control('', Validators.required),
    });
  }

  ngAfterViewInit(): void {
    setTimeout(() => this.setControlValuesFromNativeValues(), 200);
  }

  setControlValuesFromNativeValues(): void {
    this.loginForm.get('login').setValue(this.loginFeld.nativeElement.value);
    this.loginForm.get('passwort').setValue(this.passwortFeld.nativeElement.value);
  }

It takes the native values after 200ms and sets them into the reactive forms.

angular-robot[bot] commented 2 years ago

Just a heads up that we kicked off a community voting process for your feature request. There are 20 days until the voting process ends.

Find more details about Angular's feature request process in our documentation.

angular-robot[bot] commented 2 years ago

Thank you for submitting your feature request! Looks like during the polling process it didn't collect a sufficient number of votes to move to the next stage.

We want to keep Angular rich and ergonomic and at the same time be mindful about its scope and learning journey. If you think your request could live outside Angular's scope, we'd encourage you to collaborate with the community on publishing it as an open source package.

You can find more details about the feature request process in our documentation.

mrmokwa commented 2 years ago

Why is it marked as feature? IMO it's a bug and shouldn't need votes to fix it.

adamdport commented 2 years ago

I'm not sure this is isolated to Chrome. I'm having similar issues in my iOS app packaged with Cordova. My login button is disabled based on form validity, which never changes despite the password manager having filled it in. I think I'm the 20th vote so hopefully the bot chills out

dylhunn commented 2 years ago

@adamdport 21 votes is certainly plenty, I have attempted to silence the bot.

On the one hand, this is clearly a nuisance, and many apps will want this to get nice validation behavior on login controls. But on the other hand, it's possible for apps to implement on their own, in the same manner as @angular/cdk AutofillMonitor, and adding it to forms will increase bundle size for everyone, even apps without login forms.

I personally am leaning towards fixing inside the forms package, so I have pinned this on our FR triage board for further discussion at the next triage meeting.

adamdport commented 2 years ago

@dylhunn I'm unable to even get the value using Angular and was forced to resort to reading nativeElement.value, so I'm not sure that this is isolated to the required validator like this issue's title suggests. I suspect it might be a zone.js issue? A couple other related posts:

https://github.com/ionic-team/ionic-v3/issues/928 https://stackoverflow.com/questions/53555114/ios-autofill-data-in-login-forms-is-empty-in-cordova-application-using-angular

dylhunn commented 2 years ago

@adamdport Interesting, that does seem to suggest the issue scope is more severe that we thought. This is just a guess, but I suspect it should be the same fix -- if updateValueAndValidity is never fired, in addition to the validators not running, the model wouldn't get the new value.

I think the fix is to bring AutoFill detection into the core of the forms package, similar to how it's implemented in @angular/cdk AutofillMonitor. Unfortunately, @AndrewKushnir and I are both pretty busy right now, but perhaps this could be a good community PR request (i.e. mark it as "help wanted")? WDYT @AndrewKushnir?

dylhunn commented 2 years ago

We discussed this at great length in today's (Jul 29) team meeting. Here was the gist of our discussion:

mmalerba commented 2 years ago

@dylhunn Can you elaborate about it not working in all browsers? The :autofill pseudo-class seems to have support for all browsers other than IE (which we will soon drop support for): https://developer.mozilla.org/en-US/docs/Web/CSS/:autofill (I think at the time I wrote the AutofillMonitor, Chrome was the only browser with issues, so I just used the -webkit- version)

markusmo commented 2 years ago

We discussed this at great length in today's (Jul 29) team meeting. Here was the gist of our discussion:

  • It's currently possible to use cdk AutofillMonitor as demonstrated here, and call updateValueAndValidity on autofills.
  • While this issue can technically affect any input element, we'd only be interested in putting code like this in Forms, not the core. (We don't want to "paper over" browser idiosyncrasies in the framework core.)
  • The existing AutofillMonitor implementation is pretty hacky; it uses a fake animation and some CSS tricks. It also doesn't work in all browsers. This means it probably can't be used as-is.
  • We're not sure a clean implementation that works in all supported browsers is possible. This requires further research.
  • If a solution exists that isn't "hacky" and works in all supported browsers, we can add it to Forms.

We're using a different approach. We have our own directive which has to be set on input fields. There we listen to change event and set value to the FormControl, triggering validators that way.

@UntilDestroy()
@Directive({
    providers: [
        {
            provide: NgControl,
            useExisting: forwardRef(() => AutoFillWorkaroundDirective )
        }
    ],
    selector: '[iosAutofill]'
})
export class AutoFillWorkaroundDirective implements AfterViewInit {

    constructor(private elementRef: ElementRef, private ngControl: NgControl) {}

    ngAfterViewInit(): void {
        const element: HTMLElement = this.elementRef.nativeElement;
        const input = element.querySelector('input');
        if (!input) {
            return;
        }

        fromEvent(input, 'change')
            .pipe(untilDestroyed(this), debounceTime(100))
            .subscribe(() => {
                if (this.ngControl && this.ngControl.value !== input.value) {
                    this.ngControl.control?.setValue(input.value);
                }
            });
    }
}
mneige commented 2 years ago

Hi all! The Monkey patch solution from @kherock is coded in Angular 6, how are you fixing this issue recently? We have the issue with Chrome and Edge but not Firefox. Thank you!

MatiasProietti commented 2 years ago

Hello. It's important to know the difference between autofill and autocomplete.

Autofill: Automatically fill the form. (the form of autofill that affects us is the one that fills the form when the user visits the webpage). Autocomplete: Show suggestions while the user is filling the form. (filling as in typing or clicking)

Important note before reading the actual explanation: I have tested this with Chrome because it's used by almost 65% of the users, it might be different with other browsers.

Autofill

In the case of Autofill you can't do much because the data is not actually set in the input unless the user interacts with the webpage, this is why polling for values won't work.

Edit: added an stackblitz to reproduce it, you will need to use the live webpage version instead of the editor version. editor webpage

  1. Visit the website and register using the Register form (the one at the top).
  2. Refresh the webpage and make sure the credentials are automatically filled by chrome.
  3. You can see that the form is valid and the values are shown at the bottom.
  4. Open a new tab and without clicking or doing anything extra, manually write the webpage url. If an autocomplete is offered select it with the keyboard and not with the mouse.
  5. You can see that the values are filled but the form doesn't have any value.
  6. Click anywhere in the webpage, now the form is valid and the values are shown.

tip: you can visit the webpage a few times to make chrome offer the autocomplete. tip2: copy and paste seems to count as user interaction so make sure you manually write the url.

You have 3 options:

  1. Disable autofill. (I'm not sure if it's entirely possible)
  2. Adapt the form to this "problem". For example: if you have a button that is disabled when the form is invalid then remove this condition and check if the form is invalid when clicking the button.
  3. Fix and use the patch by kherock and adapt your form to it. It won't be able to give you the values because they are not set in the elements but it will let you know that the form was autofilled. Following the example from the previous point: if you have a button that is disabled when the form is invalid then add a condition that if the values were autofilled then the button is enabled.

I would recommend option 2. Option 1 might frustrate some users and option 3 is not worth the trouble as you won't be able to tell if the autofilled value is correct because you don't have access to the values and you would also have to set the autofilled value to false when the user modifies the data in the form. The implementation is not bulletproof, it might not work in the future and it might not work on some browsers. It will be a real headache to maintain that.

Autocomplete

In this case a normal form implementation should work just fine because the user already interacted with the form.

f1ght4fun commented 2 years ago

So is there any solution or any working hack for ionic6. My ion-inputs are staying empty and form is not updated until touch aka focus happens on the one of the fields

djwagner-cbc commented 2 years ago

I modified @markusmo code and it seems to work well.

@Directive({
  selector: '[appAutofillMonitor]'
})
export class AutofillMonitorDirective {

  constructor( private elementRef: ElementRef, private ngZone: NgZone) {}

  @HostListener('change', ['$event.target'])
  onChange(target) {
    this.ngZone.run(()=> {
      if (this.elementRef.nativeElement.value !== target.value) {
        this.elementRef.nativeElement.value = target.value;
      }
    });
  }

}
f1ght4fun commented 2 years ago

Nothing is helping. No matter whether u have a form or u use ngModel, whether u put all these directives floating around internet posts with autofillmonitor or with directives to react on input change. UNLESS you really tap ie focus/touch input - value does not appear. Mind you i have everything setup - associated domains, webcredentials etc

zsmatrix62 commented 1 year ago

any updates about this issue?

lukhomdingi commented 1 year ago

This is still an issue on Angular 14. Is there any kind of solution?

RoyHP commented 11 months ago

Still a problem with chrome autofill, we are seeing it in production.

Igor-Lira commented 10 months ago

I had this problem in chrome too and I got it working with AutoFillMonitor as mentioned before.

To check if a submit button is disabled based on a required validation would be:

[disabled] = "this.form.touched ? this.form.invalid : !this.autofilled"
this._autofill.monitor(this.input).subscribe(e => this.autofilled = e.isAutofilled);

Note, this is a workaround, because I don't update the validation, and I don't have the autofilled value. But at least I get to disable properly, and when user interacts with the page (first click), validation is triggered.

artuska commented 10 months ago

This issue still exists at July 2023. None of the workarounds above work. @markusmo @djwagner-cbc your solutions just does not work.

Everything works only and only after the interaction with the page, e.g. by clicking somewhere on a page.

MrHOY commented 5 months ago

Any news? Jan 2024

PRR24 commented 4 months ago

I'm not fully convinced it is Angular issue at all. If I load a page that Chrome autofills, select the input from dev console then $0.value is '' up until I press Enter after which it changes to the value prefilled by Chrome and also the app reacts expectedly.

firegreen commented 2 months ago

I don't know if it's a good practice but I use a workaround to tackle this problem, active focus in code

    var inputs = this.document.getElementsByTagName("input");
    for (var i = 0; i < inputs.length; i++) {
      inputs.item(i)!.focus();
    }
    inputs.item(0)!.focus();

Values are available after fields lose focus

caboodal commented 2 months ago

Amazing that this issue has been around for at least 6/7 years and still no fix!

playboxdesign commented 1 month ago

Now don't shoot me because we have a very big project, I am learning Angular and I may be missing the fact we have added a large import ie cdk, as someone says above can increase your project size, if anyone wants to update me why people don't like cdk that would be interesting.

As, to resolve this autofill submit button issue i used this:

ngAfterViewInit(){ setTimeout(() => { if (this.userName.nativeElement.classList.contains('cdk-text-field-autofilled')) { this.notAutofilled = false;
} else{console.log("Not Autofilled");} }, 500); }

with this on the button

[disabled]="(loginForm.invalid || loggingIn) && notAutofilled"

Is this a case that we have cdk so class 'cdk-text-field-autofilled' in our current project so I could hop on it and what you are all discussing is how to resolve this in forms when you aren't using excess modules etc.

As I say I'm learning, so be kind :)

dedalusMohantyMa commented 2 weeks ago

I'm not fully convinced it is Angular issue at all. If I load a page that Chrome autofills, select the input from dev console then $0.value is '' up until I press Enter after which it changes to the value prefilled by Chrome and also the app reacts expectedly.

Don't go down this rabbithole ... I already had a look at this and there hass been also a bug opened at Chrome, they closed it with the notion, that it is a Apple issue. Apple themselves pointed to Google and their implementation and also closed that ...