Подтвердить что ты не робот

Угловой автозаполнение браузера AngularJS с помощью директивы

При отправке формы в 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()); не изменяет модель или представление на основе возвращаемого значения element.val(). Как я могу это сделать?

4b9b3361

Ответ 1

По-видимому, это известная проблема с Angular и в настоящее время открыта

Я не уверен, что вы могли бы сделать здесь, кроме какой-то работы, как вы пытаетесь. Кажется, ты на правильном пути. Я не мог заставить мой браузер попытаться запомнить пароль для вашего плунжера, поэтому я не уверен, что это сработает, но посмотрите:

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>

Думаю, вам просто нужно немного упростить свой подход. Единственное, что я определенно рекомендую, это проверить ngModel.$pristine и убедиться, что вы не перезаписываете некоторые плохие пользовательские данные. Кроме того, 3 секунды, вероятно, слишком длинны. Вам не нужно вызывать $apply() в $timeout, BTW, он должен автоматически вызывать $digest для вас.

Реальный улов: будет ли ваш браузер бить Angular до исполнения? Как насчет моего браузера?

Это, вероятно, непростая война, поэтому Angular (или нокаут) не смог ее легко решить. Там нет гарантии состояния данных на вашем входе во время первоначального выполнения директивы. Даже во время инициализации Angular.... Так что это сложная проблема.

Ответ 2

Вам не нужно использовать $timeout или что-то вроде этого. Вы можете использовать систему событий.

Я думаю, что он более угловат и не зависит от jQuery или пользовательского захвата событий.

Например, в обработчике отправки:

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

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

И тогда вы можете иметь директиву autofill следующим образом:

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

Наконец, ваш HTML будет выглядеть так:

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

Ответ 3

Вот решение, которое гораздо менее хакерское, чем другие представленные решения, и семантически звучит. 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);
    }
  };
});

Затем вы просто присоединяете директиву к своей форме:

<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>

Ответ 4

Грязный код, проверьте, исправлена ​​ли проблема https://github.com/angular/angular.js/issues/1460#issuecomment-18572604 перед использованием этого кода. Эта директива запускает события, когда поле заполняется не только перед отправкой (это необходимо, если вам нужно обрабатывать ввод перед отправкой)

 .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);
                   }
               };
});

Ответ 5

Не нужно больше взломать Angular dev tbosch сделанный a polyfill, который запускает событие изменения, когда браузер меняет поля формы без запуска события изменения:

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

Пока они не будут создавать это в коде Angular, так как это исправление для браузера, а также работает без Angular (например, для простых приложений jQuery).

"polyfill будет проверять изменения загрузки документа, а также когда вход остается (только в том же виде). ​​Однако вы можете запустить проверку вручную, если хотите.

Проект имеет модульные тесты, а также полуавтоматические тесты, поэтому у нас наконец-то есть место для сбора всех разных вариантов использования вместе с требуемыми настройками браузера.

Обратите внимание: этот polyfill работает с обычными приложениями AngularJS с приложениями AngularJS/jQuery, а также с обычными приложениями jQuery, которые не используют Angular. "

Его можно установить с помощью

bower install autofill-event --save

Добавьте script autofill-event.js после jQuery или Angular на странице.

Это сделает следующее:

  • после DOMContentLoaded: проверьте все поля ввода
  • поле осталось: проверьте все остальные поля в том же виде

API (для ручного запуска проверки):

  • $el.checkAndTriggerAutoFillEvent(): выполнить проверку всех элементов DOM в данном элементе jQuery/jQLite.

Как это работает

  • Помните все изменения в элементах ввода пользователем (прослушивание событий изменений), а также JavaScript (путем перехвата $el.val() для элементов jQuery/jQLite). Это измененное значение сохраняется в элементе в частной собственности.

  • Проверка элемента для автоматического заполнения: сравнить текущее значение элемента с запоминаемым значением. Если он отличается, активируйте событие изменения.

Зависимости

AngularJS или jQuery (работает с одним или обоими)

Дополнительная информация и источник на странице github.

Оригинал Angular Выпуск № 1460 на Github можно прочитать здесь.

Ответ 6

Похоже на четкое прямое решение. Нет необходимости в jQuery.

UPDATE:

  • Модель обновляется только тогда, когда значение модели не равно фактическому вводу значение.
  • Проверка не останавливается при первом автозаполнении. Если вы хотите использовать например, для другой учетной записи.

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();
        }
    }
}]);

Ответ 7

Ну, самый простой способ эмулировать поведение браузера, поэтому, если есть проблема с событием изменения, просто запустите его самостоятельно. Гораздо проще.

Директива

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>

Вы можете сделать некоторые варианты, например, поместить директиву в форму и запустить событие на всех входах с классом .dirty в форме submit.

Ответ 8

Решение 1 [Использование $timeout]:

Директива

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>

Решение 2 [Использование событий angular]:

Ссылка: Ответ Бекко

Директива

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>

Решение 3 [Использование вызовов меток ретрансляции]:

Директива

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>

Ответ 9

Это способ jQuery:

$(window).load(function() {
   // updates autofilled fields
   window.setTimeout(function() {
     $('input[ng-model]').trigger('input');
   }, 100);
 });

Это Angular способ:

 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 /> 

Ответ 10

Вот еще одно обходное решение, которое менее хаки, но требует дополнительного кода в контроллере.

HTML:

<form ng-submit="submitForm()" ng-controller="FormController">
    <input type="text" ng-model="username" autocomplete-username>
    <input type="submit">
</form>

Директива (CoffeeScript):

directives.directive 'autocompleteUsername', ->
    return (scope, element) ->
        scope.getUsername = ->
            element.val()

Контроллер:

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

Ответ 11

Обходной путь однострочного в обработчике отправки (требуется jQuery):

if (!$scope.model) $scope.model = $('#input_field').val();

Ответ 12

Это единственное решение, которое я нашел, чтобы все мои проверки Angular работали так, как было разработано, в том числе отключить/включить кнопку отправки. Устанавливается с помощью беседки и 1 тега script. Bazinga!

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

Ответ 13

Изменение значения модели вместо использования функции тайм-аута работало для меня.

Вот мой код:

module.directive('autoFill', [ function() {
    return {
        require: 'ngModel',
        link:function(scope, element, attr, ngModel) {
            var origVal = element.val();
            if(origVal){
                ngModel.$modelValue = ngModel.$modelValue || origVal;
            }
        }
    };
}]);

Ответ 14

Я заставляю $setValue (val()) на submit: (это работает без 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);

Ответ 15

Я очень новичок в Angularjs, но я нашел простое решение этой проблемы = > Force Angular переоценить выражение... путем его изменения! (конечно, вам нужно запомнить начальное значение, чтобы вернуться в исходное состояние) Вот как это делается в вашей функции контроллера для отправки формы:

    $scope.submit = function () {
                var oldpassword = $scope.password;
                $scope.password = '';
                $scope.password = oldpassword;
//rest of your code of the submit function goes here...

где, конечно, значение, введенное в ваш ввод пароля, было задано окнами, а не пользователем.

Ответ 16

Вы можете попробовать этот код:

yourapp.directive('autofill',function () {

    return {
        scope: true,
        require: 'ngModel',
        link: function (scope, elem, attrs, ctrl) {
            var origVal = elem.val();
            if (origVal != '') {
                elem.trigger('input');
            }
        }
    }
});

Ответ 17

Небольшая модификация этого ответа (fooobar.com/questions/55584/...): используйте $interval вместо $timeout, поэтому вам не нужно запускать браузер.

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
    }
  });

Ответ 18

Это решение, которое я использовал в своих формах.

.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>

Таким образом, я смог запустить любые синтаксические анализаторы/форматы, которые у меня есть на моей ng-модели, и обеспечить прозрачность представления.

Ответ 19

Решение без директив:

.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);
    }])

Ответ 20

Это простое исправление, которое работает для всех случаев, которые я тестировал как в Firefox, так и в Chrome. Обратите внимание, что с верхним ответом (директива w/timeout) у меня были проблемы с -

  • Кнопки назад/назад браузера, не перезапускайте события загрузки страницы (так что исправление не применяется).
  • Загрузка учетных данных через некоторое время после загрузки страницы. например в Firefox дважды щелкните окно входа и выберите из сохраненных учетных данных.
  • Нужно решение, которое обновляется до отправки формы, так как я отключу кнопку "Вход", пока не будет введен правильный ввод

Это исправление, очевидно, очень глупое и хакерское, но работает 100% времени -

function myScope($scope, $timeout) {
    // ...
    (function autoFillFix() {
        $timeout(function() { 
            $('#username').trigger('change'); 
            $('#password').trigger('change'); 
            autoFillFix(); }, 500);                    
    })();
}

Ответ 21

Ни одно из этих решений не использовалось для моего использования. У меня есть поля формы, которые используют ng-change для просмотра изменений. Использование $watch не помогает, поскольку оно не запускается автозаполнением. Поскольку у меня нет кнопки отправки, нет простого способа запуска некоторых решений, и мне не удалось использовать интервалы.

Я закончил отключать автозаполнение - не идеально, но гораздо менее запутанным для пользователя.

<input readonly onfocus="this.removeAttribute('readonly');">

Нашел ответ здесь

Ответ 22

Если вы используете jQuery, вы можете сделать это на форме submit:

HTML:

<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>

JS:

 $scope.submit = function() {
     $scope.password = $('#password').val();
}

Ответ 23

Если вы хотите сохранить его просто, просто получите значение с помощью javascript

В вашем контроллере js angular:

var username = document.getElementById('username'). value;