Recently a user complained about the log in form not saving the credentials.
This is a common problem with AJAX and single-page applications. But I decided to get to the bottom with it.
The UserApp “dashboard” is built with the JavaScript framework AngularJS, and it doesn’t play along very well with the “save password” feature. These are the issues I addressed:
- The form cannot be dynamically inserted into the DOM.
- The form must perform an actual POST request.
- When the browser autofills the fields, the scope doesn’t get updated.
Firefox was easy, all it required was that the form had a name attribute.
But when Firefox autofills the fields the scope doesn’t get updated. So I googled and
found some hacks for it. But for the scenario I tried to solve, this felt overkill. Because that was needed was the login and password form values after the form had been submitted. So no need for any fancy two-way bindings with some ugly hacks on top of it. Thats why I went with this solution. Good ol’ fashion DOM access (with jQuery):
$scope.login = function() {
$scope.user = {
login: $("#login").val(),
password: $("#password").val()
};
...
return false;
};
Now it works in Firefox. What about Chrome?
Well, it turns out that Chrome only shows the save password dialog if the form submit actually passes, which would destroy our AJAX request.
Here’s a solution to the problem:
- When the form get posted, do the AJAX request to authenticate the user in ng-submit and return with false to abort the form submission.
- If it succeeds, save the session in a cookie and just post the form again and return with true this time.
- When the page reloads it finds the session cookie and redirects the user to the home screen.
Sure, it makes the page to reload but it’s only once when logging in to the app. Make sure that the form gets posted to same page.
Solution? Add a hidden form to index.html and copy the other form’s parameters into it when it’s getting submitted.
I wrapped this up with a directive:
app.directive("ngLoginSubmit", function(){
return {
restrict: "A",
scope: {
onSubmit: "=ngLoginSubmit"
},
link: function(scope, element, attrs) {
$(element)[0].onsubmit = function() {
$("#login-login").val($("#login", element).val());
$("#login-password").val($("#password", element).val());
scope.onSubmit(function() {
$("#login-form")[0].submit();
});
return false;
};
}
};
});
Hidden form on index.html:
Log in form template:
The login controller:
$scope.login = function(submit) {
$scope.user = {
login: $("#login").val(),
password: $("#password").val()
};
function ajaxCallback() {
submit();
}
return false;
};
Now it works fine, except that every time you press F5 the browser asks if you really want to resend the “form”. This is pretty annoying, so I fixed it by creating a new page, pre-login.html, and posted the hidden form to it instead. This page then redirects back to the app (index.html).
Everybody happy!
reference : http://timothy.userapp.io/post/63412334209/form-autocomplete-and-remember-password-with-angularjs