110 votes

Solution de contournement automatique du navigateur AngularJS à l'aide d'une directive

Lors de la soumission d'un formulaire dans AngularJS et d'utiliser le navigateur de mémoriser le mot de passe de la fonctionnalité, et lors d'une tentative de connexion vous laisser le navigateur de remplir le formulaire de connexion avec le nom d'utilisateur et le mot de passe, l' $scope modèle ne sera pas modifié sur la base de remplissage automatique.

La seule sale hack que j'ai trouvé est d'utiliser la directive suivante:

app.directive("xsInputSync", ["$timeout" , function($timeout) {
    return {
        restrict : "A",
        require: "?ngModel",
        link : function(scope, element, attrs, ngModel) {
            $timeout(function() {
                if (ngModel.$viewValue && ngModel.$viewValue !== element.val()) {
                    scope.apply(function() {
                        ngModel.$setViewValue(element.val());
                    });
                }
                console.log(scope);
                console.log(ngModel.$name);
                console.log(scope[ngModel.$name]);
            }, 3000);
        }
    };
}]);

Le problème est que l' ngModel.$setViewValue(element.val()); ne change pas le modèle ni la vue basée sur l' element.val() la valeur retournée. Comment puis-je le réaliser?

45voto

Ben Lesh Points 39290

Apparemment c'est un problème connu avec Angulaires et est actuellement ouvert

Je ne suis pas sûr de ce que vous pourriez faire ici à part une sorte de travail autour de vous comme vous êtes à essayer. Il semble que vous êtes sur la bonne voie. Je ne pouvais pas obtenir mon navigateur pour essayer de vous souvenir d'un mot de passe pour votre plunk, donc je ne suis pas sûr si cela va fonctionner, mais jetez un oeil:

app.directive('autoFillSync', function($timeout) {
   return {
      require: 'ngModel',
      link: function(scope, elem, attrs, ngModel) {
          var origVal = elem.val();
          $timeout(function () {
              var newVal = elem.val();
              if(ngModel.$pristine && origVal !== newVal) {
                  ngModel.$setViewValue(newVal);
              }
          }, 500);
      }
   }
});
<form name="myForm" ng-submit="login()">
   <label for="username">Username</label>
   <input type="text" id="username" name="username" ng-model="username" auto-fill-sync/><br/>
   <label for="password">Password</label>
   <input type="password" id="password" name="password" ng-model="password" auto-fill-sync/><br/>
   <button type="submit">Login</button>
</form>

Je pense que vous avez juste besoin de simplifier votre approche un peu. La seule chose que je vous recommande vraiment de est de vérifier ngModel.$pristine et assurez-vous que vous n'êtes pas d'écraser le pauvre utilisateur d'entrée. Aussi, 3 secondes est probablement trop long. Vous ne devriez pas avoir à appeler $apply () $timeout, BTW, il devrait mettre un $digest automatiquement pour vous.

Le vrai catch: votre navigateur battre Angulaire de l'exécution? Ce sujet de mon navigateur?

C'est probablement une guerre impossible à gagner, c'est pourquoi Angulaire (ou knock-out) n'a pas été en mesure de le résoudre facilement. Il n'y a aucune garantie de l'état des données dans votre entrée au moment de la directive de l'exécution initiale. Pas même au moment de l'Angulaire de l'initialisation.... C'est donc un problème difficile à résoudre.

35voto

bekos Points 426

Vous n'avez pas à utiliser un $timeout ou quelque chose comme cela. Vous pouvez utiliser un système d'événements.

Je pense que c'est plus Angularish et ne dépend pas de jQuery ou d'un événement personnalisé de la clientèle.

Par exemple sur votre gestionnaire de soumission:

$scope.doLogin = function() {
    $scope.$broadcast("autofill:update");

    // Continue with the login.....
};

Et puis vous pouvez avoir un autofill directive comme ceci:

.directive("autofill", function () {
    return {
        require: "ngModel",
        link: function (scope, element, attrs, ngModel) {
            scope.$on("autofill:update", function() {
                ngModel.$setViewValue(element.val());
            });
        }
    }
});

Enfin, votre code HTML sera comme:

<input type="text" name="username" ng-model="user.id" autofill="autofill"/>

35voto

Ezekiel Victor Points 2512

Voici une solution qui est beaucoup moins hacky que les autres solutions présentées et est sémantiquement son AngularJS: http://victorblog.com/2014/01/12/fixing-autocomplete-autofill-on-angularjs-form-submit/

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 autofilled 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 il vous suffit de fixer 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>

11voto

TheRebel Points 341

Pas besoin de hack! Angulaire dev tbosch fait un polyfill qui déclenche un événement de changement lorsque le navigateur changements de champs de formulaire sans le déclenchement d'un événement de changement:

https://github.com/tbosch/autofill-event

Pour l'instant ils ne sont pas dans l'angle de code, comme c'est une correction de bug pour le navigateur, et fonctionne aussi sans Angulaire (par exemple, pour la plaine de jQuery pour les applications).

"Le polyfill va rechercher les modifications sur le document de charge et également lorsqu'une entrée est à gauche (dans la même forme). Cependant, vous pouvez déclencher le vérifier manuellement si vous le souhaitez.

Le projet a des tests unitaires ainsi que des semi automatique de tests, de sorte que nous avons enfin un endroit pour rassembler tous les différents cas d'utilisation, de concert avec les paramètres du navigateur.

Veuillez noter: Ce polyfill fonctionne avec la plaine AngularJS apps, avec AngularJS/jQuery applications, mais aussi avec la plaine jQuery applications qui n'utilisent pas Angulaire."

Il peut être installé:

bower install autofill-event --save

Ajouter le script autofill-event.js après jQuery ou Angulaire dans votre page.

Cela va faire ce qui suit:

  • après DOMContentLoaded: vérifier tous les champs de saisie
  • un champ est laissé: vérifiez tous les autres champs de la même forme

API (pour déclencher manuellement la case):

  • $el.checkAndTriggerAutoFillEvent(): Exécuter la vérification de tous les éléments du DOM dans le jQuery / jQLite élément.

Comment ça marche

  1. Rappelez-vous toutes les modifications apportées aux éléments d'entrée par l'utilisateur (à l'écoute des événements de changement) et aussi par JavaScript (en interceptant $el.val() de jQuery / jQLite éléments). Qui a changé la valeur est stockée sur l'élément dans une propriété privée.

  2. La vérification d'un élément de remplissage automatique: Comparer la valeur actuelle de l'élément avec la rappeler valeur. Si c'est différent, déclencher un événement de changement.

Dépendances

AngularJS ou jQuery (fonctionne avec l'un ou les deux)

Plus d'infos et source sur la page github.

D'origine Angulaire de la Question n ° 1460 sur Github peut être lu ici.

10voto

OZ_ Points 7398

Code sale, vérifiez si le problème https://github.com/angular/angular.js/issues/1460#issuecomment-18572604 est corrigé avant d’utiliser ce code. Cette directive déclenche des événements lorsque le champ est rempli, pas seulement avant la soumission (nécessaire si vous devez gérer les entrées avant la soumission).

  .directive('autoFillableField', function() {
    return {
                   restrict: "A",
                   require: "?ngModel",
                   link: function(scope, element, attrs, ngModel) {
                       setInterval(function() {
                           var prev_val = '';
                           if (!angular.isUndefined(attrs.xAutoFillPrevVal)) {
                               prev_val = attrs.xAutoFillPrevVal;
                           }
                           if (element.val()!=prev_val) {
                               if (!angular.isUndefined(ngModel)) {
                                   if (!(element.val()=='' && ngModel.$pristine)) {
                                       attrs.xAutoFillPrevVal = element.val();
                                       scope.$apply(function() {
                                           ngModel.$setViewValue(element.val());
                                       });
                                   }
                               }
                               else {
                                   element.trigger('input');
                                   element.trigger('change');
                                   element.trigger('keyup');
                                   attrs.xAutoFillPrevVal = element.val();
                               }
                           }
                       }, 300);
                   }
               };
});
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X