Javascript AngularJS浏览器使用指令自动填充解决方案

Javascript AngularJS浏览器使用指令自动填充解决方案,javascript,mvvm,angularjs,Javascript,Mvvm,Angularjs,在AngularJS中提交表单并使用浏览器记住密码功能时,在随后的登录尝试中,您让浏览器使用用户名和密码填写登录表单,$scope模型不会根据自动填充更改 我发现的唯一肮脏的黑客行为是使用以下指令: app.directive("xsInputSync", ["$timeout" , function($timeout) { return { restrict : "A", require: "?ngModel", link : funct

在AngularJS中提交表单并使用浏览器记住密码功能时,在随后的登录尝试中,您让浏览器使用用户名和密码填写登录表单,
$scope
模型不会根据自动填充更改

我发现的唯一肮脏的黑客行为是使用以下指令:

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);
        }
    };
}]);
问题是,
ngModel.$setViewValue(element.val())
不会根据
元素.val()
返回的值更改模型或视图。我如何才能做到这一点?

我不确定你在这里除了做些你正在尝试的工作之外还能做些什么。看来你走对了方向。我无法让我的浏览器尝试为您的plunk记住密码,因此我不确定这是否有效,但请查看:

app.directive('autoFillSync',函数($timeout){
返回{
要求:'ngModel',
链接:功能(范围、要素、属性、ngModel){
var origVal=elem.val();
$timeout(函数(){
var newVal=elem.val();
if(ngModel.$pristine&&origVal!==newVal){
ngModel.$setViewValue(newVal);
}
}, 500);
}
}
});

用户名

密码
登录
我认为你只需要简化一下你的方法。我绝对推荐的一件事是检查
ngModel.$pristine
,确保您没有覆盖一些糟糕的用户输入。另外,3秒可能太长了。您不必在$timeout中调用$apply(),顺便说一句,它应该自动为您排队等待$digest

真正的问题:你的浏览器是否会在执行上超越角度?我的浏览器呢?


这可能是一场无法取胜的战争,这就是为什么Angular(或Knockout)无法轻易解决这场战争的原因。在指令初始执行时,无法保证输入中数据的状态。甚至在Angular的初始化时都没有。。。。所以这是一个需要解决的棘手问题。

脏代码,请在使用此代码之前检查问题是否已修复。 此指令在字段填充时触发事件,而不仅仅是在提交之前(如果您必须在提交之前处理输入,则有必要这样做)


这里有另一个解决方法,它不太黑客,但需要在控制器中添加一些额外的代码

HTML:

控制器:

controllers.controller 'FormController', [->
    $scope.submitForm = ->
        username = $scope.getUsername?() ? $scope.username
        # HTTP stuff...
]

嗯,最简单的方法是模拟浏览器的行为,因此如果更改事件有问题,只需自己启动即可。简单得多

指令:

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:


您可以做一些修改,比如将指令放在表单上,并在表单提交时使用.dirty类对所有输入触发事件。

我在提交时强制使用$setValue(val()):(这在不使用jQuery的情况下有效)

var ValidSubmit=['$parse',函数($parse){
返回{
编译:函数编译(远程通讯、tAttrs、转置){
返回{
post:功能postLink(范围、元素、iAttrs、控制器){
var form=element.controller('form');
表格.$submitted=false;
var fn=$parse(iAttrs.validSubmit);
元素。on('submit',函数(事件){
作用域$apply(函数(){
var inputs=element.find('input');
对于(变量i=0;i
提交处理程序中的一个线性解决方案(需要jQuery):


如果您使用的是jQuery,则可以在表单提交中执行此操作:

HTML:


这是jQuery的方式:

$(window).load(function() {
   // updates autofilled fields
   window.setTimeout(function() {
     $('input[ng-model]').trigger('input');
   }, 100);
 });
 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)
        }
    }
}]);
这是一种有角度的方式:

$(window).load(function() {
   // updates autofilled fields
   window.setTimeout(function() {
     $('input[ng-model]').trigger('input');
   }, 100);
 });
 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 /> 

您不必使用
$timeout
或类似的方法。您可以使用事件系统

我认为它更像Angularish,不依赖于jQuery或自定义事件捕获

例如,在提交处理程序上:

$scope.doLogin=function(){
$scope.$broadcast(“自动填充:更新”);
//继续登录。。。。。
};
然后您可以有一个如下的
autofill
指令:

指令(“自动填充”,函数(){ 返回{ 要求:“ngModel”, 链接:功能(范围、元素、属性、模型){ 作用域:$on(“自动填充:更新”,函数(){ ngModel.$setViewValue(element.val()); }); } } }); 最后,您的HTML将如下所示:


似乎是明确的直截了当的解决方案。不需要jQuery

更新:

  • 仅当模型值不等于实际输入时,才更新模型 价值观
  • 检查不会在第一次自动填充时停止。如果您希望使用 另一个帐户
    if (!$scope.model) $scope.model = $('#input_field').val();
    
    <form ng-submit="submit()">
        <input id="email" ng-model="password" required 
               type="text" placeholder="Your email">
        <input id="password" ng-model="password" required 
               type="password" placeholder="Password">
    </form>
    
     $scope.submit = function() {
         $scope.password = $('#password').val();
    }
    
    $(window).load(function() {
       // updates autofilled fields
       window.setTimeout(function() {
         $('input[ng-model]').trigger('input');
       }, 100);
     });
    
     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)
            }
        }
    }]);
    
    <input type="number" autofill /> 
    
    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();
            }
        }
    }]);
    
        $scope.submit = function () {
                    var oldpassword = $scope.password;
                    $scope.password = '';
                    $scope.password = oldpassword;
    //rest of your code of the submit function goes here...
    
    yourapp.directive('autofill',function () {
    
        return {
            scope: true,
            require: 'ngModel',
            link: function (scope, elem, attrs, ctrl) {
                var origVal = elem.val();
                if (origVal != '') {
                    elem.trigger('input');
                }
            }
        }
    });
    
    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
        }
      });
    
    .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'
      };  
    }]);
    
    <form autofill-sync name="user.loginForm" class="login-form" novalidate ng-submit="signIn()">
        <!-- Input fields here -->
    </form>
    
    .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);
        }])
    
    function myScope($scope, $timeout) {
        // ...
        (function autoFillFix() {
            $timeout(function() { 
                $('#username').trigger('change'); 
                $('#password').trigger('change'); 
                autoFillFix(); }, 500);                    
        })();
    }
    
    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);
          }
        };
    });
    
    <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>
    
    app.directive("autofill", function () {
        return {
            require: "ngModel",
            link: function (scope, element, attrs, ngModel) {
                scope.$on("autofill:update", function() {
                    ngModel.$setViewValue(element.val());
                });
            }
        };
    });
    
    <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>
    
    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
                }
    
            }
        };
    });
    
    <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>
    
    module.directive('autoFill', [ function() {
        return {
            require: 'ngModel',
            link:function(scope, element, attr, ngModel) {
                var origVal = element.val();
                if(origVal){
                    ngModel.$modelValue = ngModel.$modelValue || origVal;
                }
            }
        };
    }]);
    
    bower install autofill-event --save
    
    <input readonly onfocus="this.removeAttribute('readonly');">