web-dev-qa-db-fra.com

Angularjs Chrome dilemme autocomplete

J'ai un formulaire de connexion simple qui ne fonctionne que si vous utilisez la fonctionnalité de saisie automatique de Chrome. 

Si vous commencez à taper et utilisez la fonctionnalité de saisie automatique et qu'elle remplit automatiquement votre mot de passe, mon modèle angularjs n'a aucune valeur pour le mot de passe.

J'ai essayé de désactiver la complétion automatique en définissant l'attribut sur le formulaire autocomplete="off" mais cela ne semble pas avoir d'effet.

Comment puis-je soit: 1. Assurez-vous que je peux obtenir la valeur si quelqu'un utilise la fonctionnalité de saisie semi-automatique de Chrome? 2. Désactiver la fonctionnalité de saisie automatique de Chrome?

<form class="form-signin" name="form" ng-submit="login()" autocomplete="off">

        <h3>Login</h3>

        <input type="email" name="email" class="form-control" placeholder="Email address" ng-model="user.email" required autofocus>
        <input type="password" name="password" class="form-control" placeholder="Password" ng-model="user.password" required>

        <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>

 </form>
38
Catfish

Depuis le lien ajouté dans le commentaire: Github Issue's

// Due to browsers issue, it's impossible to detect without a timeout any changes of autofilled inputs
// https://github.com/angular/angular.js/issues/1460
// https://github.com/angular/angular.js/issues/1460#issuecomment-28662156
// Could break future Angular releases (if use `compile()` instead of `link())
// TODO support select
angular.module("app").config(["$provide", function($provide) {
    var inputDecoration = ["$delegate", "inputsWatcher", function($delegate, inputsWatcher) {
        var directive = $delegate[0];
        var link = directive.link;

        function linkDecoration(scope, element, attrs, ngModel){
            var handler;
            // By default model.$viewValue is equals to undefined
            if(attrs.type == "checkbox"){
                inputsWatcher.registerInput(handler = function(){
                    var value = element[0].checked;
                    // By default element is not checked
                    if (value && ngModel.$viewValue !== value) {
                        ngModel.$setViewValue(value);
                    }
                });
            }else if(attrs.type == "radio"){
                inputsWatcher.registerInput(handler = function(){
                    var value = attrs.value;
                    // By default element is not checked
                    if (element[0].checked && ngModel.$viewValue !== value) {
                        ngModel.$setViewValue(value);
                    }
                });
            }else{
                inputsWatcher.registerInput(handler = function(){
                    var value = element.val();
                    // By default value is an empty string
                    if ((ngModel.$viewValue !== undefined || value !== "") && ngModel.$viewValue !== value) {
                        ngModel.$setViewValue(value);
                    }
                });
            }

            scope.$on("$destroy", function(){
                inputsWatcher.unregisterInput(handler);
            });

            // Exec original `link()`
            link.apply(this, [].slice.call(arguments, 0));
        }

        // Decorate `link()` don't work for `inputDirective` (why?)
        /*
         directive.link = linkDecoration;
         */
        // So use `compile()` instead
        directive.compile = function compile(element, attrs, transclude){
            return linkDecoration;
        };
        delete directive.link;

        return $delegate;
    }];

    $provide.decorator("inputDirective", inputDecoration);
    $provide.decorator("textareaDirective", inputDecoration);
    //TODO decorate selectDirective (see binding "change" for `Single()` and `Multiple()`)
}]).factory("inputsWatcher", ["$interval", "$rootScope", function($interval, $rootScope){
    var INTERVAL_MS = 500;
    var promise;
    var handlers = [];

    function execHandlers(){
        for(var i = 0, l = handlers.length; i < l; i++){
            handlers[i]();
        }
    }

    return {
        registerInput: function registerInput(handler){
            if(handlers.Push(handler) == 1){
                promise = $interval(execHandlers, INTERVAL_MS);
            }
        },
        unregisterInput: function unregisterInput(handler){
            handlers.splice(handlers.indexOf(handler), 1);
            if(handlers.length == 0){
                $interval.cancel(promise);
            }
        }
    }
}]);
21
Pauli Price

De: Developer.mozilla.org docs Turning_off_form_autocompletion

Si un auteur souhaite empêcher le remplissage automatique des champs de mot de passe dans les pages de gestion des utilisateurs où un utilisateur peut spécifier un nouveau mot de passe pour quelqu'un d'autre qu'eux-mêmes, autocomplete = "new-password" devrait être spécifié, bien que le support pour cela n’a pas été mis en œuvre dans tous navigateurs encore.

Alors, qu'est-ce qui le fait fonctionner pour moi:

  • set autocomplete = "new-password" dans le champ mot de passe
  • définissez autocomplete = "off" dans le champ Nom d'utilisateur.

J'espère que cela fonctionne pour vous aussi :)

12
ptgamr

Comme indiqué ici, https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form

L'interface utilisateur de Google Chrome pour les demandes de saisie semi-automatique varie en fonction de si la complétion automatique est désactivée sur les éléments d'entrée ainsi que sur leurs forme. Plus précisément, lorsqu'un formulaire a la saisie semi-automatique désactivée et que son Le champ autocomplete de l'élément input n'est pas défini, alors si l'utilisateur demande Pour les suggestions de remplissage automatique de l'élément d'entrée, Chrome peut afficher un message disant "la saisie semi-automatique a été désactivée pour ce formulaire." Sur le D’autre part, si le formulaire et l’élément d’entrée ont une complétion automatique désactivé, le navigateur n’affiche pas ce message. Pour ça Pour cette raison, vous devez désactiver la complétion automatique pour chaque entrée ayant complétion automatique personnalisée.

Vous devez définir autocomplete = "off" à la fois sur form et input

Je ne pense pas que cela soit lié à AngularJS

11
allenhwkim

J'ai eu le même problème et j'ai trouvé une solution très simple qui utilise simplement jQuery pour saisir la valeur sur submit. Dans mon contrôleur, j'ai les éléments suivants:

$scope.username = "";
$scope.password = "";

$scope.login = function(){
    $scope.username = $("#username").val();
    $scope.password = $("#password").val();
    // Proceed as normal
};

Il y a certains inconvénients, si vous devez effectuer une validation, etc., mais sinon, c'est bien pour les formulaires plus petits comme celui-ci.

7
Dachande663

Vous pouvez regarder la valeur du champ email et chaque fois que la valeur de ce champ change, vous pouvez déclencher un événement "change" dans le champ du mot de passe. Ces événements déclenchent toute la magie ng-model sur ce champ et mettent à jour le modèle.

module.directive("autocompleteFor", function () {
    return {
        restrict: "A",
        link: function ($scope, $element, $attrs) {
            $scope.$watch($attrs.autocompleteFor, function () {
                $element.triggerHandler("change");
            })
        }
    }
});

Avec cette directive, vous pouvez gérer ce scénario comme ceci:

<input type="email" name="email" ng-model="user.email">
<input type="password" autocomplete-for="user.email" name="password" ng-model="user.password"   required>
                       -----------------------------
2
kfis

Pour désactiver la saisie semi-automatique/automatique à partir d'une entrée, tapez simplement: - autocomplete = "false" au lieu de autocomplete = "off"!

2
Rafael Ferreira

La directive ci-dessous a fonctionné pour moi. C'est une solution simple et propre. J'espère que cela pourra aider!

Réf .: Solution de contournement automatique du navigateur AngularJS à l’aide d’une directive

Voici une solution beaucoup moins astucieuse que les autres solutions présentées et sémantiquement saine AngularJS: VictorBlog.com

myApp.directive('formAutofillFix', function() {
  return function(scope, elem, attrs) {
    // Fixes Chrome bug: https://groups.google.com/forum/#!topic/angular/6NlucSskQjY
    elem.prop('method', 'POST');

    // Fix autofill issues where Angular doesn't know about auto-filled inputs
    if(attrs.ngSubmit) {
      setTimeout(function() {
        elem.unbind('submit').submit(function(e) {
          e.preventDefault();
          elem.find('input, textarea, select').trigger('input').trigger('change').trigger('keydown');
          scope.$apply(attrs.ngSubmit);
        });
      }, 0);
    }
  };
});

Ensuite, vous attachez simplement la directive à votre formulaire:

<form ng-submit="submitLoginForm()" form-autofill-fix>
  <div>
    <input type="email" ng-model="email" ng-required />
    <input type="password" ng-model="password" ng-required />
    <button type="submit">Log In</button>
  </div>
</form>
2
FullStackDev

la solution alternative est juste de se débarrasser de l'élément form et d'utiliser ng-form à la place, elle désactive toutes les interférences du navigateur

<div ng-form="yourFormName" class="form-signin" ng-submit="login()">
1
Matej Janovčík

Je me suis retrouvé avec une solution différente que je ne vois pas encore ici. D'après ce que j'ai trouvé, la valeur du mot de passe n'est pas exposée au modèle (ou peut-être même à l'API js) jusqu'à ce que l'utilisateur interagisse avec la page. En cliquant sur le bouton de connexion, l'interaction est suffisante pour que la valeur soit disponible, et la liaison de données aboutira suffisamment tôt pour que le gestionnaire de clic sur le bouton puisse accéder au mot de passe du modèle. Donc, si je pouvais détecter que le navigateur était rempli automatiquement, je pourrais activer le bouton de connexion même si mon modèle n'avait pas encore été mis à jour. J'ai donc écrit un simple service d'assistance pour savoir si Chrome avait automatiquement renseigné des entrées:

utilApp.service('autoFillDetectionService', [function () {
    return {
        hasAutoFillInputs: function () {
            try{
                return !!$(':-webkit-autofill').length;
            }
            catch (x) {
                // IE gets here, it/jquery complains about an invalid pseudo-class
                return false;
            }
        }
    };
}]);

À partir du contrôleur de connexion, j'ai un intervalle vérifiant si des champs de saisie sont marqués comme remplissage automatique et, le cas échéant, activez le bouton de connexion.

0
bmm6o

Ma solution pour Chrome 35.0, Firefox 30.0, 1.2.18 angulaire (page de connexion avec gestionnaire de mots de passe, remplissage automatique, méthode angulaire et redirection):

Comment le navigateur sait-il quand demander à l'utilisateur de sauvegarder le mot de passe?

0
110maor

--- N'EST PLUS PERTINENT ---

J'ai pu désactiver la saisie semi-automatique (assez bizarrement) en ajoutant ce qui suit.

<form ... novalidate>
<input ... formnovalidate />

Référence ce Plunker

0
Cory Silva

Ce pourrait être une solution beaucoup plus simple au problème. 

  1. Angularjs ne pouvait pas "voir" la valeur 
  2. Prenez la valeur via DOM (jQuery) puis remettez-la dans Angularjs.

`` `

angular.module('someModule').directive('chromeAutofillHack', function()
{
    return {
        require: '^ngModel',
        restrict: 'A',
        priority: 500, // set higher priority then other custom directives
        link: function(scope, element, attrs , ngModelCtrl)
        {
            ngModelCtrl.$parsers.unshift(function(email)
            {
                if (!email) { // only do this when angular think there is no value
                    email = $(element).val();
                    ngModel.$setViewValue(email);
                }
                return email;
            });
        }
    };
});

`` `

0
Joel Chu

Dans mon cas, j'ai défini la propriété autocomplete = "off" dans le formulaire et l'entrée.

<form autocomplete="off">
   <input type="text" autocomplete="off">
</form>
0
Kate.spot

Vieille question, mais peu importe

Je suis tombé sur le même problème et j'ai une petite solution "bidouille". Ce problème est survenu à différents endroits de mon application. J'ai donc créé une directive relative à la réutilisation.

module.directive("fakeAutocomplete", [
  function () {
    return {
      restrict: "EA",
      replace: true,
      template: "<div><input/><input type=\"password\"/></div>",
      link: function (scope, elem, attrs) {
        elem.css({
          "overflow": "hidden",
          "width": "0px",
          "height": "0px"
        });
      }
    }
  }
]);

Et ajoutez simplement

<fake-autocomplete></fake-autocomplete>

Au début de votre formulaire, le navigateur détectera les faux champs comme ceux devant compléter automatiquement. Mettre simplement display:none sur les champs ne semble également plus fonctionner, je l’ai testé.

0
Louis Boux