Lorsque vous soumettez un formulaire dans AngularJS et utilisez la fonctionnalité de mémorisation du mot de passe du navigateur, vous devez laisser le navigateur remplir le formulaire de connexion avec le nom d'utilisateur et le mot de passe. Le modèle $scope
ne sera pas modifié en fonction du remplissage automatique.
Le seul bidouillage 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 la fonction ngModel.$setViewValue(element.val());
ne modifie ni le modèle ni la vue basée sur la valeur renvoyée par element.val()
. Comment puis-je accomplir cela?
Apparemment, c’est un problème connu avec Angular et il est actuellement ouvert
Je ne sais pas ce que vous pourriez faire ici, à part un travail comme vous essayez. Il semble que vous soyez sur la bonne voie. Je n'arrive pas à faire en sorte que mon navigateur essaie de retenir un mot de passe pour votre plunk, alors je ne sais pas si cela fonctionnera, mais jetez un coup d'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 un peu votre approche. La seule chose que je recommande vraiment est de vérifier ngModel.$pristine
et de vous assurer que vous n'écrasez pas les données d'un mauvais utilisateur. En outre, 3 secondes est probablement trop long. Vous ne devriez pas avoir à appeler $ apply () dans un délai d'attente de $, BTW, il devrait automatiquement mettre un $ digest en file d'attente.
Le vrai problème: votre navigateur battra-t-il Angular à l'exécution? Qu'en est-il de mon navigateur?
C'est probablement une guerre impossible à gagner, c'est pourquoi Angular (ou Knockout) n'a pas été en mesure de la résoudre rapidement. Il n'y a aucune garantie de l'état des données dans votre entrée au moment de l'exécution initiale de la directive. Pas même au moment de l'initialisation d'Angular ... C'est donc un problème délicat à résoudre.
Voici une solution beaucoup moins astucieuse que les autres solutions présentées et sémantiquement saine 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, 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>
Vous n'êtes pas obligé d'utiliser un $timeout
ou quoi que ce soit du genre. Vous pouvez utiliser un système d'événements.
Je pense que c'est plus angulaire et ne dépend pas de la capture d'événements jQuery ou personnalisés.
Par exemple sur votre gestionnaire d'envoi:
$scope.doLogin = function() {
$scope.$broadcast("autofill:update");
// Continue with the login.....
};
Et vous pouvez alors avoir une directive autofill
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 ressemblera à:
<input type="text" name="username" ng-model="user.id" autofill="autofill"/>
Plus besoin de bidouiller! Angular dev TBOSCH a créé un polyfill qui déclenche un événement change lorsque le navigateur modifie les champs de formulaire sans déclencher d'événement:
_ { https://github.com/tbosch/autofill-event } _
Pour l'instant, ils ne l'intégreront pas dans le code angulaire, car il s'agit d'un correctif pour le navigateur et fonctionne également sans Angular (par exemple, pour les applications jQuery simples).
"Le polyfill vérifiera les modifications apportées lors du chargement du document et lorsqu’une entrée est laissée (sous la même forme). Cependant, vous pouvez déclencher la vérification manuellement si vous le souhaitez.
Le projet comporte des tests unitaires ainsi que des tests semi-automatiques, nous avons donc enfin un endroit pour collecter tous les cas d'utilisation différents avec les paramètres de navigateur requis.
Remarque: ce polyfill fonctionne avec les applications simples AngularJS, AngularJS/jQuery mais également avec les applications jQuery simples qui n'utilisent pas Angular. "
Il peut être installé avec:
bower install autofill-event --save
Ajoutez le script autofill-event.js
after jQuery ou Angular dans votre page.
Cela fera ce qui suit:
API (pour déclencher manuellement la vérification):
$el.checkAndTriggerAutoFillEvent()
: Exécute la vérification de tous les éléments DOM dans l'élément jQuery/jQLite donné.Mémorisez toutes les modifications apportées aux éléments en entrée par l'utilisateur (écoute des événements de modification) et également par JavaScript (en interceptant $ el.val () pour les éléments jQuery/jQLite). Cette valeur modifiée est stockée sur l'élément dans une propriété privée.
Vérification du remplissage automatique d'un élément: Compare la valeur actuelle de l'élément avec la valeur mémorisée. Si c'est différent, déclenchez un événement de changement.
AngularJS ou jQuery (fonctionne avec un ou les deux)
Plus d'infos et source sur la page github .
Numéro angulaire original n ° 1460 sur Github à lire ici.
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, non seulement avant de soumettre (c'est nécessaire si vous devez gérer l'entrée avant de soumettre)
.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);
}
};
});
Cela semble être une solution claire et directe. Pas besoin de jQuery.
METTRE À JOUR:
app.directive('autofillable', ['$timeout', function ($timeout) {
return {
scope: true,
require: 'ngModel',
link: function (scope, elem, attrs, ctrl) {
scope.check = function(){
var val = elem[0].value;
if(ctrl.$viewValue !== val){
ctrl.$setViewValue(val)
}
$timeout(scope.check, 300);
};
scope.check();
}
}
}]);
Solution 1 [Utilisation de $ timeout]:
Directive:
app.directive('autoFillSync', function($timeout) {
return {
require: 'ngModel',
link: function(scope, elem, attrs, model) {
var origVal = elem.val();
$timeout(function () {
var newVal = elem.val();
if(model.$pristine && origVal !== newVal) {
model.$setViewValue(newVal);
}
}, 500);
}
};
});
HTML:
<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>
Solution 2 [Utilisation d'événements angulaires]:
Réf .: Réponse de Becko
Directive:
app.directive("autofill", function () {
return {
require: "ngModel",
link: function (scope, element, attrs, ngModel) {
scope.$on("autofill:update", function() {
ngModel.$setViewValue(element.val());
});
}
};
});
HTML:
<form name="myForm" ng-submit="login()">
<label for="username">Username</label>
<input type="text" id="username" name="username" ng-model="username" autofill/><br/>
<label for="password">Password</label>
<input type="password" id="password" name="password" ng-model="password" autofill/><br/>
<button type="submit">Login</button>
</form>
Solution 3 [Utilisation des appels de méthode relais]:
Directive:
app.directive('autoFill', function() {
return {
restrict: 'A',
link: function(scope,element) {
scope.submit = function(){
scope.username = element.find("#username").val();
scope.password = element.find("#password").val();
scope.login();//call a login method in your controller or write the code here itself
}
}
};
});
HTML:
<form name="myForm" auto-fill ng-submit="submit()">
<label for="username">Username</label>
<input type="text" id="username" name="username" ng-model="username" />
<label for="password">Password</label>
<input type="password" id="password" name="password" ng-model="password" />
<button type="submit">Login</button>
</form>
Eh bien, le moyen le plus simple consiste à imiter le comportement du navigateur. Par conséquent, s'il y a un problème avec l'événement change, déclenchez-le vous-même. Beaucoup plus simple.
Directif:
yourModule.directive('triggerChange', function($sniffer) {
return {
link : function(scope, elem, attrs) {
elem.on('click', function(){
$(attrs.triggerChange).trigger(
$sniffer.hasEvent('input') ? 'input' : 'change'
);
});
},
priority : 1
}
});
HTML:
<form >
<input data-ng-model="user.nome" type="text" id="username">
<input data-ng-model="user.senha" type="password" id="password" >
<input type="submit" data-ng-click="login.connect()" id="btnlogin"
data-trigger-change="#password,#username"/>
</form>
Vous pouvez faire certaines variations, comme mettre la directive sur le formulaire et déclencher l'événement sur toutes les entrées avec la classe .dirty lors de l'envoi du formulaire.
Ceci est la façon jQuery:
$(window).load(function() {
// updates autofilled fields
window.setTimeout(function() {
$('input[ng-model]').trigger('input');
}, 100);
});
Ceci est la manière angulaire:
app.directive('autofill', ['$timeout', function ($timeout) {
return {
scope: true,
require: 'ngModel',
link: function (scope, elem, attrs, ctrl) {
$timeout(function(){
$(elem[0]).trigger('input');
// elem.trigger('input'); try this if above don't work
}, 200)
}
}
}]);
HTML
<input type="number" autofill />
Voici une autre solution de contournement moins efficace, mais nécessitant du code supplémentaire dans le contrôleur.
HTML:
<form ng-submit="submitForm()" ng-controller="FormController">
<input type="text" ng-model="username" autocomplete-username>
<input type="submit">
</form>
Directive (CoffeeScript):
directives.directive 'autocompleteUsername', ->
return (scope, element) ->
scope.getUsername = ->
element.val()
Manette:
controllers.controller 'FormController', [->
$scope.submitForm = ->
username = $scope.getUsername?() ? $scope.username
# HTTP stuff...
]
C’est la seule solution que j’ai trouvée qui permette à toutes les validations de mon Angulaire de fonctionner comme prévu, y compris les boutons désactiver/activer le bouton Envoyer. Installe avec bower et 1 balise de script. Bazinga!
Solution de contournement à une ligne dans le gestionnaire de soumission (requiert jQuery):
if (!$scope.model) $scope.model = $('#input_field').val();
Changer la valeur du modèle au lieu d'utiliser une fonction de délai d'attente a fonctionné pour moi.
Voici mon code:
module.directive('autoFill', [ function() {
return {
require: 'ngModel',
link:function(scope, element, attr, ngModel) {
var origVal = element.val();
if(origVal){
ngModel.$modelValue = ngModel.$modelValue || origVal;
}
}
};
}]);
Je force un $ setValue (val ()) sur submit: (cela fonctionne sans jQuery)
var ValidSubmit = ['$parse', function ($parse) {
return {
compile: function compile(tElement, tAttrs, transclude) {
return {
post: function postLink(scope, element, iAttrs, controller) {
var form = element.controller('form');
form.$submitted = false;
var fn = $parse(iAttrs.validSubmit);
element.on('submit', function(event) {
scope.$apply(function() {
var inputs = element.find('input');
for(var i=0; i < inputs.length; i++) {
var ele = inputs.eq(i);
var field = form[inputs[i].name];
field.$setViewValue(ele.val());
}
element.addClass('ng-submitted');
form.$submitted = true;
if(form.$valid) {
fn(scope, {$event:event});
}
});
});
scope.$watch(function() { return form.$valid}, function(isValid) {
if(form.$submitted == false) return;
if(isValid) {
element.removeClass('has-error').addClass('has-success');
} else {
element.removeClass('has-success');
element.addClass('has-error');
}
});
}
}
}
}
}]
app.directive('validSubmit', ValidSubmit);
Il s’agit d’une solution simple qui fonctionne dans tous les cas que j’ai testés sous Firefox et Chrome. Notez qu'avec la réponse du haut (directive w/timeout), j'ai eu des problèmes avec -
Ce correctif est évidemment très stupide et hacky, mais il fonctionne 100% du temps -
function myScope($scope, $timeout) {
// ...
(function autoFillFix() {
$timeout(function() {
$('#username').trigger('change');
$('#password').trigger('change');
autoFillFix(); }, 500);
})();
}
Une modification mineure à cette réponse ( https://stackoverflow.com/a/14966711/3443828 ): utilisez un intervalle $ au lieu d'un délai d'attente $ pour ne pas avoir à courir avec le navigateur.
mod.directive('autoFillSync', function($interval) {
function link(scope, element, attrs, ngModel) {
var origVal = element.val();
var refresh = $interval(function() {
if (!ngModel.$pristine) {
$interval.cancel(refresh);
}else{
var newVal = element.val();
if (origVal !== newVal) {
ngModel.$setViewValue(newVal);
$interval.cancel(refresh);
}
}
}, 100);
}
return {
require: 'ngModel',
link: link
}
});
Vous pouvez essayer ce code:
yourapp.directive('autofill',function () {
return {
scope: true,
require: 'ngModel',
link: function (scope, elem, attrs, ctrl) {
var origVal = elem.val();
if (origVal != '') {
elem.trigger('input');
}
}
}
});
Je suis très nouveau dans Angularjs, mais j’ai trouvé une solution simple à ce problème => Forcer Angular à réévaluer l’expression ... en le modifiant! (Bien sûr, vous devez vous rappeler la valeur initiale pour revenir à la valeur initiale. état) Voici comment cela se passe dans la fonction de votre contrôleur pour soumettre le formulaire:
$scope.submit = function () {
var oldpassword = $scope.password;
$scope.password = '';
$scope.password = oldpassword;
//rest of your code of the submit function goes here...
où bien sûr, la valeur entrée dans votre mot de passe a été définie par Windows et non par l'utilisateur.
C'est la solution que j'ai finalement utilisée dans mes formulaires.
.directive('autofillSync', [ function(){
var link = function(scope, element, attrs, ngFormCtrl){
element.on('submit', function(event){
if(ngFormCtrl.$dirty){
console.log('returning as form is dirty');
return;
}
element.find('input').each(function(index, input){
angular.element(input).trigger('input');
});
});
};
return {
/* negative priority to make this post link function run first */
priority:-1,
link: link,
require: 'form'
};
}]);
Et le modèle de formulaire sera
<form autofill-sync name="user.loginForm" class="login-form" novalidate ng-submit="signIn()">
<!-- Input fields here -->
</form>
De cette façon, j'ai pu exécuter tous les analyseurs/formateurs que j'ai sur mon modèle et avoir la fonctionnalité d'envoi transparente.
Aucune de ces solutions n'a fonctionné pour mon cas d'utilisation. Certains champs de formulaire utilisent ng-change pour surveiller les modifications. Utiliser $watch
n'est pas une aide, car il n'est pas déclenché par le remplissage automatique. Comme je n'ai pas de bouton d'envoi, il n'existe pas de moyen facile d'exécuter certaines des solutions et je n'ai pas réussi à utiliser les intervalles.
J'ai fini par désactiver le remplissage automatique - pas idéal mais beaucoup moins déroutant pour l'utilisateur.
<input readonly onfocus="this.removeAttribute('readonly');">
Trouvé la réponse ici
Solution sans directives:
.run(["$window", "$rootElement", "$timeout", function($window, $rootElement, $timeout){
var event =$window.document.createEvent("HTMLEvents");
event.initEvent("change", true, true);
$timeout(function(){
Array.apply(null, $rootElement.find("input")).forEach(function(item){
if (item.value.length) {
item.$$currentValue = item.value;
item.dispatchEvent(event);
}
});
}, 500);
}])