Equivalent document.querySelector inside Mutation Observer dynamic elements - jquery-selectors

I'm using Mutation Observer to listen to the new added elements and it's working fine for all elements but for Switchery jQuery Plugin it doesn't because Switchery get element by [document.querySelector]
This is my code...
MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
var observerjQueryPlugins = function (repeaterWrapper) {
_.each(repeaterWrapper, function (repeaterItem, index) {
var jq_nodes = $(repeaterItem.addedNodes);
jq_nodes.each(function () {
// Color Picker (Working Good)
$(this).find('.element-wrapper.element-wpcolor .color-picker').wpColorPicker();
// Switchery using document.querySelector and i need to know the
// equivalent way to do it inside this loop .. like that
// Of course this code is WRONG
sw_current = $(this).find('.switchery-element');
var switchery = new Switchery( sw_current, {
disabled: false,
size: '',
color: '#8ce196',
secondaryColor: '#ddd',
jackColor: '#fff',
jackSecondaryColor: '#fff'
});
});
});
}
new MutationObserver(observerjQueryPlugins).observe(document.body, {
childList: true,
subtree: true,
attributes: false,
characterData: false
});
Thanks for your help.

It seems that Switchery is not a jQuery Plugin. That's why the sample code in GitHub uses document.querySelector to acquire element to apply it.
As you use jQuery to find elements, you need to use jQuery.each in order to process every DOM element for Switchery.
The following is an example to find element by jQuery and apply Switchery to each element. so, do the similar thing after sw_current = $(this).find('.switchery-element'); in your code.
$(function() {
sw_current = $(this).find('.switchery-element');
sw_current.each(function() {
var switchery = new Switchery(this);
})
});
#import url("http://abpetkov.github.io/switchery/dist/switchery.min.css");
<input type="checkbox" class="switchery-element" checked />Check 1
<p/>
<input type="checkbox" class="switchery-element" checked />Check 2
<p/>
<input type="checkbox" class="switchery-element" checked />Check 3
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://abpetkov.github.io/switchery/dist/switchery.min.js"></script>

Related

Comparing two input values in a form validation with AngularJS

I am trying to do a form validation using AngularJS. I am especially interested in comparing two values. I want the user to confirm some data he entered before he continues. Lets say I have the code below:
<p>
Email:<input type="email" name="email1" ng-model="emailReg">
Repeat Email:<input type="email" name="email2" ng-model="emailReg2">
<p>
and then I can use validation with:
<span ng-show="registerForm.email1.$error.required">Required!</span>
<span ng-show="registerForm.email1.$error.email">Not valid email!</span>
<span ng-show="emailReg !== emailReg2">Emails have to match!</span> <-- see this line
registerForm.$valid will react correctly as to the text in inputs except I do not know how to use comparison within this validation to force the emails to be the same before allowing the user to submit the form.
I would love to have a solution without custom directives, but if this can't be achieved without it I will deal with it. Here is an answer that addresses similar issue with a custom directive.
Any help appreciated, thank you
You should be able to use ng-pattern/regex for comparing 2 input values
Email:<input type="email" name="email1" ng-model="emailReg">
Repeat Email:<input type="email" name="email2" ng-model="emailReg2" ng-pattern="emailReg">
and validation with:
<span ng-show="registerForm.email2.$error.pattern">Repeat Email should have the same value with email!</span>
One way to achieve this is with a custom directive. Here's an example using a custom directive (ng-match in this case):
<p>Email:<input type="email" name="email1" ng-model="emailReg">
Repeat Email:<input type="email" name="email2" ng-model="emailReg2" ng-match="emailReg"></p>
<span data-ng-show="myForm.emailReg2.$error.match">Emails have to match!</span>
NOTE: It's not generally recommended to use ng- as a prefix for a custom directive because it may conflict with an official AngularJS directive.
Update
It's also possible to get this functionality without using a custom directive:
HTML
<button ng-click="add()></button>
<span ng-show="IsMatch">Emails have to match!</span>
Controller
$scope.add = function() {
if ($scope.emailReg != $scope.emailReg2) {
$scope.IsMatch=true;
return false;
}
$scope.IsMatch=false;
}
trainosais - you are right, validation should be done on a directive level. It's clean, modular and allows for reusability of code. When you have basic validation like that in a controller you have write it over and over again for different forms. That's super anti-dry.
I had a similar problem recently and sorted it out with a simple directive, which plugs in to the parsers pipeline, therefore stays consistent with Angular architecture. Chaining validators makes it very easy to reuse and that should be considered the only solution in my view.
Without further ado, here's the simplified markup:
<form novalidate="novalidate">
<label>email</label>
<input type="text"
ng-model="email"
name="email" />
<label>email repeated</label>
<input ng-model="emailRepeated"
same-as="email"
name="emailRepeated" />
</form>
And the JS code:
angular.module('app', [])
.directive('sameAs', function() {
return {
require: 'ngModel',
link: function(scope, elem, attrs, ngModel) {
ngModel.$parsers.unshift(validate);
// Force-trigger the parsing pipeline.
scope.$watch(attrs.sameAs, function() {
ngModel.$setViewValue(ngModel.$viewValue);
});
function validate(value) {
var isValid = scope.$eval(attrs.sameAs) == value;
ngModel.$setValidity('same-as', isValid);
return isValid ? value : undefined;
}
}
};
});
The directive hooks into the parsers pipeline in order to get notified of any changes to the view value and set validity based on comparison of the new view value and the value of the reference field. That bit is easy. The tricky bit is sniffing for changes on the reference field. For that the directive sets a watcher on the reference value and force-triggeres the parsing pipeline, in order to get all the validators run again.
If you want to play with it, here is my pen:
http://codepen.io/jciolek/pen/kaKEn
I hope it helps,
Jacek
I recently wrote a custom directive which can be generic enough to do any validation. It take a validation function from the current scope
module.directive('customValidator', [function () {
return {
restrict: 'A',
require: 'ngModel',
scope: { validateFunction: '&' },
link: function (scope, elm, attr, ngModelCtrl) {
ngModelCtrl.$parsers.push(function (value) {
var result = scope.validateFunction({ 'value': value });
if (result || result === false) {
if (result.then) {
result.then(function (data) { //For promise type result object
ngModelCtrl.$setValidity(attr.customValidator, data);
}, function (error) {
ngModelCtrl.$setValidity(attr.customValidator, false);
});
}
else {
ngModelCtrl.$setValidity(attr.customValidator, result);
return result ? value : undefined; //For boolean result return based on boolean value
}
}
return value;
});
}
};
}]);
To use it you do
<input type="email" name="email2" ng-model="emailReg2" custom-validator='emailMatch' data-validate-function='checkEmailMatch(value)'>
<span ng-show="registerForm.email2.$error.emailMatch">Emails have to match!</span>
In you controller then you can implement the method, that should return true or false
$scope.checkEmailMatch=function(value) {
return value===$scope.emailReg;
}
The advantage is that you do not have to write custom directive for each custom validation.
When upgrading angular to 1.3 and above I found an issue using Jacek Ciolek's great answer:
Add data to the reference field
Add the same data to the field with the directive on it (this field is now valid)
Go back to the reference field and change the data (directive field remains valid)
I tested rdukeshier's answer (updating var modelToMatch = element.attr('sameAs') to var modelToMatch = attrs.sameAs to retrieve the reference model correctly) but the same issue occurred.
To fix this (tested in angular 1.3 and 1.4) I adapted rdukeshier's code and added a watcher on the reference field to run all validations when the reference field is changed. The directive now looks like this:
angular.module('app', [])
.directive('sameAs', function () {
return {
require: 'ngModel',
link: function(scope, element, attrs, ctrl) {
var modelToMatch = attrs.sameAs;
scope.$watch(attrs.sameAs, function() {
ctrl.$validate();
})
ctrl.$validators.match = function(modelValue, viewValue) {
return viewValue === scope.$eval(modelToMatch);
};
}
};
});
Updated codepen
No need a function or a directive. Just compare their $modelValue from the view:
ng-show="formName.email.$modelValue !== formName.confirmEmail.$modelValue"
More detailed example:
<span ng-show="(formName.email.$modelValue !== formName.confirmEmail.$modelValue)
&& formName.confirmEmail.$touched
&& !formName.confirmEmail.$error.required">Email does not match.</span>
Please note that the ConfirmEmail is outside the ViewModel; it's property of the $scope. It doesn't need to be submitted.
use ng-pattern, so that ng-valid and ng-dirty can act correctly
Email:<input type="email" name="email1" ng-model="emailReg">
Repeat Email:<input type="email" name="email2" ng-model="emailReg2" ng-pattern="emailReg">
<span ng-show="registerForm.email2.$error.pattern">Emails have to match!</span>
#Henry-Neo's method was close, it just needed more strict Regex rules.
<form name="emailForm">
Email: <input type="email" name="email1" ng-model="emailReg">
Repeat Email: <input type="email" name="email2" ng-model="emailReg2" ng-pattern="(emailReg)">
</form>
By including the regex rule inside parentheses, it will match the entire string of emailReg to emailReg2 and will cause the form validation to fail because it doesn't match.
You can then drill into the elements to find out which part is failing.
<p ng-show="emailForm.$valid">Form Valid</p>
<p ng-show="emailForm.email1.$error">Email not valid</p>
<p ng-show="emailForm.email1.$valid && emailForm.email1.$error.pattern">
Emails Do Not Match
</p>
This module works well for comparing two fields. Works great with Angular 1.3+. Simple to use
https://www.npmjs.com/package/angular-password
It also allows saving the module as a generic. Just include them in packages list of your module.
Here is my simple version of the custom validator directive:
angular.module('app')
.directive('equalsTo', function () {
return {
require: 'ngModel',
link: function (scope, elm, attrs, ngModel) {
scope.$watchGroup([attrs.equalsTo, () => ngModel.$modelValue], newVal => {
ngModel.$setValidity('equalsTo', newVal[0] === newVal[1]);
});
}
};
})
Here is an angular 1.3 version of the sameAs directive:
angular.module('app').directive('sameAs', [function() {
'use strict';
return {
require: 'ngModel',
restrict: 'A',
link: function(scope, element, attrs, ctrl) {
var modelToMatch = element.attr('sameAs');
ctrl.$validators.match = function(modelValue, viewValue) {
return viewValue === scope.$eval(modelToMatch);
};
}
};
}]);
Mine is similar to your solution but I got it to work. Only difference is my model. I have the following models in my html input:
ng-model="new.Participant.email"
ng-model="new.Participant.confirmEmail"
and in my controller, I have this in $scope:
$scope.new = {
Participant: {}
};
and this validation line worked:
<label class="help-block" ng-show="new.Participant.email !== new.Participant.confirmEmail">Emails must match! </label>
Thanks for the great example #Jacek Ciolek. For angular 1.3.x this solution breaks when updates are made to the reference input value. Building on this example for angular 1.3.x, this solution works just as well with Angular 1.3.x. It binds and watches for changes to the reference value.
angular.module('app', []).directive('sameAs', function() {
return {
restrict: 'A',
require: 'ngModel',
scope: {
sameAs: '='
},
link: function(scope, elm, attr, ngModel) {
if (!ngModel) return;
attr.$observe('ngModel', function(value) {
// observes changes to this ngModel
ngModel.$validate();
});
scope.$watch('sameAs', function(sameAs) {
// watches for changes from sameAs binding
ngModel.$validate();
});
ngModel.$validators.sameAs = function(value) {
return scope.sameAs == value;
};
}
};
});
Here is my pen: http://codepen.io/kvangrae/pen/BjxMWR
You have to look at the bigger problem. How to write the directives that solve one problem. You should try directive use-form-error. Would it help to solve this problem, and many others.
<form name="ExampleForm">
<label>Password</label>
<input ng-model="password" required />
<br>
<label>Confirm password</label>
<input ng-model="confirmPassword" required />
<div use-form-error="isSame" use-error-expression="password && confirmPassword && password!=confirmPassword" ng-show="ExampleForm.$error.isSame">Passwords Do Not Match!</div>
</form>
Live example jsfiddle
I need to do this just in one form in my entire application and i see a directive like a super complicate for my case, so i use the ng-patter like some has point, but have some problems, when the string has special characters like .[\ this broke, so i create a function for scape special characters.
$scope.escapeRegExp(str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
}
and in the view
<form name="ExampleForm">
<label>Password</label>
<input ng-model="password" required />
<br>
<label>Confirm password</label>
<input ng-model="confirmPassword" required ng-pattern="escapeRegExp(password)"/>
</form>
Of course for very simple comparisons you can always use ngMin/ngMax.
Otherwise, you can go with a custom directive, and there is no need of doing any $watch or $observe or $eval or this fancy $setValidity back and forth. Also, there is no need to hook into the postLink function at all. Try to stay out of the DOM as much as possible, as it is against the angular spirit.
Just use the lifecycle hooks the framework gives you. Add a validator and $validate at each change. Simple as that.
app.directive('sameAs', function() {
return {
restrict: 'A',
require: {
ngModelCtrl: 'ngModel'
},
scope: {
reference: '<sameAs'
},
bindToController: true,
controller: function($scope) {
var $ctrl = $scope.$ctrl;
//add the validator to the ngModelController
$ctrl.$onInit = function() {
function sameAsReference (modelValue, viewValue) {
if (!$ctrl.reference || !modelValue) { //nothing to compare
return true;
}
return modelValue === $ctrl.reference;
}
$ctrl.ngModelCtrl.$validators.sameas = sameAsReference;
};
//do the check at each change
$ctrl.$onChanges = function(changesObj) {
$ctrl.ngModelCtrl.$validate();
};
},
controllerAs: '$ctrl'
};
});
Your plunker.
I've modified method of Chandermani to be compatible with Angularjs 1.3 and upper. Migrated from $parsers to $asyncValidators.
module.directive('customValidator', [function () {
return {
restrict: 'A',
require: 'ngModel',
scope: { validateFunction: '&' },
link: function (scope, elm, attr, ngModelCtrl) {
ngModelCtrl.$asyncValidators[attr.customValidator] = function (modelValue, viewValue) {
return new Promise(function (resolve, reject) {
var result = scope.validateFunction({ 'value': viewValue });
if (result || result === false) {
if (result.then) {
result.then(function (data) { //For promise type result object
if (data)
resolve();
else
reject();
}, function (error) {
reject();
});
}
else {
if (result)
resolve();
else
reject();
return;
}
}
reject();
});
}
}
};
}]);
Usage is the same

How to trigger ng-model value in angularjs if browser auto fill scripts filled out the forms [duplicate]

When submitting a form in AngularJS and use the browser remember password functionality, and in a subsequent login attempt you let the browser fill in the login form with the username and password, the $scope model won't be changed based on the autofill.
The only dirty hack I found is to use the following directive:
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);
}
};
}]);
The problem is that the ngModel.$setViewValue(element.val()); doesn't change the model nor the view based on the element.val() returned value. How can I accomplish that?
Apparently this is a known issue with Angular and is currently open
I'm not sure what you could do here besides some sort of work around like you're trying. It seems you're on the right track. I couldn't get my browser to try to remember a password for your plunk, so I'm not sure if this will work but have a look:
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>
I think you just need to simplify your approach a bit. The one thing I definitely recommend is to check ngModel.$pristine and make sure you're not overwriting some poor user's input. Also, 3 seconds is probably too long. You shouldn't have to call $apply() in a $timeout, BTW, it should queue a $digest for you automatically.
The real catch: Will your browser beat Angular to execution? What about my browser?
This is probably an unwinnable war, which is why Angular (or Knockout) hasn't been able to solve it readily. There's no guarantee of the state of the data in your input at the time of the directive's initial execution. Not even at the time of Angular's initialization.... So it's a tricky problem to solve.
Here is a solution that is far less hacky than other solutions presented and is semantically sound 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);
}
};
});
Then you simply attach the directive to your form:
<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>
You don't have to use a $timeout or anything like this. You can use an event system.
I think it's more Angularish and does not depend on jQuery or custom event catching.
For example on your submit handler:
$scope.doLogin = function() {
$scope.$broadcast("autofill:update");
// Continue with the login.....
};
And then you can have an autofill directive like this:
.directive("autofill", function () {
return {
require: "ngModel",
link: function (scope, element, attrs, ngModel) {
scope.$on("autofill:update", function() {
ngModel.$setViewValue(element.val());
});
}
}
});
Finally, your HTML will be like:
<input type="text" name="username" ng-model="user.id" autofill="autofill"/>
No need to hack anymore! Angular dev tbosch made a polyfill that triggers a change event when the browser changes form fields without triggering a change event:
https://github.com/tbosch/autofill-event
For now they won't build this into the Angular code, as this is a bugfix for the browser, and also works without Angular (e.g. for plain jQuery apps).
"The polyfill will check for changes on document load and also when an input is left (only in the same form). However, you can trigger the check manually if you want to.
The project has unit tests as well as semi automatic tests, so we finally have a place to collect all the different use case together with the required browser settings.
Please note: This polyfill works with plain AngularJS apps, with AngularJS/jQuery apps but also with plain jQuery apps that do not use Angular."
It can be installed with:
bower install autofill-event --save
Add the script autofill-event.js after jQuery or Angular in your page.
This will do the following:
after DOMContentLoaded: check all input fields
a field is left: check all other fields in the same form
API (to manually trigger the check):
$el.checkAndTriggerAutoFillEvent(): Execute the check for all DOM elements in the given jQuery / jQLite element.
How it works
Remember all changes to input elements by the user (listening for change events) and also by JavaScript (by intercepting $el.val() for jQuery / jQLite elements). That changed value is stored on the element in a private property.
Checking an element for auto fill: Compare the current value of the element with the remembered value. If it's different, trigger a change event.
Dependencies
AngularJS or jQuery (works with either one or both)
More info and source on the github page.
Original Angular Issue #1460 on Github can be read here.
Dirty code, check if issue https://github.com/angular/angular.js/issues/1460#issuecomment-18572604 is fixed before using this code.
This directive triggers events when field is filled, not only before submit (it's necessary if you have to handle input before submit)
.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);
}
};
});
Seems like clear straight ahead solution. No jQuery needed.
UPDATE:
Model is updated only when model value isn't equal to actual input
value.
Checking doesn't stop on first autofill. In case if you wish to use
another account for example.
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 [Using $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 [Using angular events]:
Ref: Becko's answer
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 [Using relay method calls]:
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>
Well, the easiest way it's to emulate the browser's behavior, so if there is a problem with the change event, just fire it yourself. Much simpler.
Directive:
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>
You can do some variations, like putting the directive on the form and firing the event on all inputs with the .dirty class on form submit.
This is jQuery way :
$(window).load(function() {
// updates autofilled fields
window.setTimeout(function() {
$('input[ng-model]').trigger('input');
}, 100);
});
This is Angular way :
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 />
Here's another workaround that's less hacky, but requires some extra code in the controller.
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()
Controller:
controllers.controller 'FormController', [->
$scope.submitForm = ->
username = $scope.getUsername?() ? $scope.username
# HTTP stuff...
]
This is the only solution I've found that allowed all of my Angular' validations to work as designed including disable/enable of submit button. Installs with bower and 1 script tag. Bazinga!
https://github.com/tbosch/autofill-event
Changing the model value, instead of using a timeout function worked for me.
Here is my 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;
}
}
};
}]);
One-liner workaround in the submit handler (requires jQuery):
if (!$scope.model) $scope.model = $('#input_field').val();
I force a $setValue(val()) on submit: (this works without 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);
I am very new to Angularjs, but I found a simple solution to that problem=>
Force Angular to reevaluate expression... by changing it!
(of course you need to remember the initial value to revert to initial state)
Here is the way it goes in your controller function for submitting the form:
$scope.submit = function () {
var oldpassword = $scope.password;
$scope.password = '';
$scope.password = oldpassword;
//rest of your code of the submit function goes here...
where of course, the value entered in your password input has been set by windows and not by user.
You can try this 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');
}
}
}
});
A minor modification to this answer (https://stackoverflow.com/a/14966711/3443828): use an $interval instead of a $timeout so you don't have to race the browser.
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
}
});
This is the solution I ended up using in my forms.
.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'
};
}]);
And the form's template will be
<form autofill-sync name="user.loginForm" class="login-form" novalidate ng-submit="signIn()">
<!-- Input fields here -->
</form>
This way I was able to run any parsers/formatters I have on my ng-model and have the submit functionality transparent.
Solution without 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);
}])
This is a simple fix that works for all the cases I've tested in both Firefox and Chrome. Note that with the top answer (directive w/ timeout) I had issues with -
Browser back / forward buttons, don't re-fire page load events (so the fix doesn't apply)
Loading of credentials some time after page load. e.g. in Firefox, double click on the login box and select from stored credentials.
Need a solution that updates before form submission since I disable the Login button until valid input provided
This fix is obviously very dumb and hacky, but it works 100% of the time -
function myScope($scope, $timeout) {
// ...
(function autoFillFix() {
$timeout(function() {
$('#username').trigger('change');
$('#password').trigger('change');
autoFillFix(); }, 500);
})();
}
None of these solutions worked for my use case. I have some form fields that use ng-change to watch for change. Using $watch is no help as it is not triggered by autofill. Since I have no submit button there is no easy way to run some of the solutions and I was not successful using intervals.
I ended up disabling autofill - not ideal but a lot less confusing to the user.
<input readonly onfocus="this.removeAttribute('readonly');">
Found the answer here
If you are using jQuery you could do this on form 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();
}
If you want to keep it simple just get the value using javascript
In your angular js controller :
var username = document.getElementById('username').value;

Validate fields after user has left a field

With AngularJS, I can use ng-pristine or ng-dirty to detect if the user has entered the field. However, I want to do client-side validation only after the user has left the field area. This is because when a user enters a field like e-mail or phone, they will always get an error thrown until they've completed typing out their full e-mail, and this is not an optimal user experience.
Example
UPDATE:
Angular now ships with a custom blur event:
https://docs.angularjs.org/api/ng/directive/ngBlur
From version 1.3.0 this can easily be done with $touched, which is true after the user has left the field.
<p ng-show="form.field.$touched && form.field.$invalid">Error</p>
https://docs.angularjs.org/api/ng/type/ngModel.NgModelController
Angular 1.3 now has ng-model-options, and you can set the option to { 'updateOn': 'blur'} for example, and you can even debounce, when the use is either typing too fast, or you want to save a few expensive DOM operations (like a model writing to multiple DOM places and you don't want a $digest cycle happening on every key down)
https://docs.angularjs.org/guide/forms#custom-triggers and https://docs.angularjs.org/guide/forms#non-immediate-debounced-model-updates
By default, any change to the content will trigger a model update and
form validation. You can override this behavior using the
ngModelOptions directive to bind only to specified list of events.
I.e. ng-model-options="{ updateOn: 'blur' }" will update and validate
only after the control loses focus. You can set several events using a
space delimited list. I.e. ng-model-options="{ updateOn: 'mousedown
blur' }"
And debounce
You can delay the model update/validation by using the debounce key
with the ngModelOptions directive. This delay will also apply to
parsers, validators and model flags like $dirty or $pristine.
I.e. ng-model-options="{ debounce: 500 }" will wait for half a second
since the last content change before triggering the model update and
form validation.
I solved this by expanding on what #jlmcdonald suggested. I created a directive that would automatically be applied to all input and select elements:
var blurFocusDirective = function () {
return {
restrict: 'E',
require: '?ngModel',
link: function (scope, elm, attr, ctrl) {
if (!ctrl) {
return;
}
elm.on('focus', function () {
elm.addClass('has-focus');
scope.$apply(function () {
ctrl.hasFocus = true;
});
});
elm.on('blur', function () {
elm.removeClass('has-focus');
elm.addClass('has-visited');
scope.$apply(function () {
ctrl.hasFocus = false;
ctrl.hasVisited = true;
});
});
elm.closest('form').on('submit', function () {
elm.addClass('has-visited');
scope.$apply(function () {
ctrl.hasFocus = false;
ctrl.hasVisited = true;
});
});
}
};
};
app.directive('input', blurFocusDirective);
app.directive('select', blurFocusDirective);
This will add has-focus and has-visited classes to various elements as the user focuses/visits the elements. You can then add these classes to your CSS rules to show validation errors:
input.has-visited.ng-invalid:not(.has-focus) {
background-color: #ffeeee;
}
This works well in that elements still get $invalid properties etc, but the CSS can be used to give the user a better experience.
I managed to do this with a pretty simple bit of CSS. This does require that the error messages be siblings of the input they relate to, and that they have a class of error.
:focus ~ .error {
display:none;
}
After meeting those two requirements, this will hide any error message related to a focused input field, something that I think angularjs should be doing anyway. Seems like an oversight.
This seems to be implemented as standard in newer versions of angular.
The classes ng-untouched and ng-touched are set respectively before and after the user has had focus on an validated element.
CSS
input.ng-touched.ng-invalid {
border-color: red;
}
Regarding #lambinator's solution... I was getting the following error in angular.js 1.2.4:
Error: [$rootScope:inprog] $digest already in progress
I'm not sure if I did something wrong or if this is a change in Angular, but removing the scope.$apply statements resolved the problem and the classes/states are still getting updated.
If you are also seeing this error, give the following a try:
var blurFocusDirective = function () {
return {
restrict: 'E',
require: '?ngModel',
link: function (scope, elm, attr, ctrl) {
if (!ctrl) {
return;
}
elm.on('focus', function () {
elm.addClass('has-focus');
ctrl.$hasFocus = true;
});
elm.on('blur', function () {
elm.removeClass('has-focus');
elm.addClass('has-visited');
ctrl.$hasFocus = false;
ctrl.$hasVisited = true;
});
elm.closest('form').on('submit', function () {
elm.addClass('has-visited');
scope.$apply(function () {
ctrl.hasFocus = false;
ctrl.hasVisited = true;
});
});
}
};
};
app.directive('input', blurFocusDirective);
app.directive('select', blurFocusDirective);
It might work for you to write a custom directive that wraps the javascript blur() method (and runs a validation function when triggered); there's an Angular issue that has a sample one (as well as a generic directive that can bind to other events not natively supported by Angular):
https://github.com/angular/angular.js/issues/1277
If you don't want to go that route, your other option would be to set up $watch on the field, again triggering validation when the field is filled out.
To pick up further on the given answers, you can simplify input tagging by using CSS3 pseudo-classes and only marking visited fields with a class to delay displaying validation errors until the user lost focus on the field:
(Example requires jQuery)
JavaScript
module = angular.module('app.directives', []);
module.directive('lateValidateForm', function () {
return {
restrict: 'AC',
link: function (scope, element, attrs) {
$inputs = element.find('input, select, textarea');
$inputs.on('blur', function () {
$(this).addClass('has-visited');
});
element.on('submit', function () {
$inputs.addClass('has-visited');
});
}
};
});
CSS
input.has-visited:not(:focus):required:invalid,
textarea.has-visited:not(:focus):required:invalid,
select.has-visited:not(:focus):required:invalid {
color: #b94a48;
border-color: #ee5f5b;
}
HTML
<form late-validate-form name="userForm">
<input type="email" name="email" required />
</form>
based on #nicolas answer.. Pure CSS should the trick, it will only show the error message on blur
<input type="email" id="input-email" required
placeholder="Email address" class="form-control" name="email"
ng-model="userData.email">
<p ng-show="form.email.$error.email" class="bg-danger">This is not a valid email.</p>
CSS
.ng-invalid:focus ~ .bg-danger {
display:none;
}
Here is an example using ng-messages (available in angular 1.3) and a custom directive.
Validation message is displayed on blur for the first time user leaves the input field, but when he corrects the value, validation message is removed immediately (not on blur anymore).
JavaScript
myApp.directive("validateOnBlur", [function() {
var ddo = {
restrict: "A",
require: "ngModel",
scope: {},
link: function(scope, element, attrs, modelCtrl) {
element.on('blur', function () {
modelCtrl.$showValidationMessage = modelCtrl.$dirty;
scope.$apply();
});
}
};
return ddo;
}]);
HTML
<form name="person">
<input type="text" ng-model="item.firstName" name="firstName"
ng-minlength="3" ng-maxlength="20" validate-on-blur required />
<div ng-show="person.firstName.$showValidationMessage" ng-messages="person.firstName.$error">
<span ng-message="required">name is required</span>
<span ng-message="minlength">name is too short</span>
<span ng-message="maxlength">name is too long</span>
</div>
</form>
PS. Don't forget to download and include ngMessages in your module:
var myApp = angular.module('myApp', ['ngMessages']);
ng-model-options in AngularJS 1.3 (beta as of this writing) is documented to support {updateOn: 'blur'}. For earlier versions, something like the following worked for me:
myApp.directive('myForm', function() {
return {
require: 'form',
link: function(scope, element, attrs, formController) {
scope.validate = function(name) {
formController[name].isInvalid
= formController[name].$invalid;
};
}
};
});
With a template like this:
<form name="myForm" novalidate="novalidate" data-my-form="">
<input type="email" name="eMail" required="required" ng-blur="validate('eMail')" />
<span ng-show="myForm.eMail.isInvalid">Please enter a valid e-mail address.</span>
<button type="submit">Submit Form</button>
</form>
Use field state $touched The field has been touched for this as shown in below example.
<div ng-show="formName.firstName.$touched && formName.firstName.$error.required">
You must enter a value
</div>
You can dynamically set the has-error css class (assuming you're using bootstrap) using ng-class and a property on the scope of the associated controller:
plunkr: http://plnkr.co/edit/HYDlaTNThZE02VqXrUCH?p=info
HTML:
<div ng-class="{'has-error': badEmailAddress}">
<input type="email" class="form-control" id="email" name="email"
ng-model="email"
ng-blur="emailBlurred(email.$valid)">
</div>
Controller:
$scope.badEmailAddress = false;
$scope.emailBlurred = function (isValid) {
$scope.badEmailAddress = !isValid;
};
If you use bootstrap 3 and lesscss you can enable on blur validation with the following less snippet:
:focus ~ .form-control-feedback.glyphicon-ok {
display:none;
}
:focus ~ .form-control-feedback.glyphicon-remove {
display:none;
}
.has-feedback > :focus {
& {
.form-control-focus();
}
}
outI used a directive. Here is the code:
app.directive('onBlurVal', function () {
return {
restrict: 'A',
link: function (scope, element, attrs, controller) {
element.on('focus', function () {
element.next().removeClass('has-visited');
element.next().addClass('has-focus');
});
element.on('blur', function () {
element.next().removeClass('has-focus');
element.next().addClass('has-visited');
});
}
}
})
All my input control has a span element as the next element, which is where my validation message is displayed and so the directive as an attribute is added to each input control.
I also have (optional).has-focus and has-visited css class in my css file which you see being referenced in the directive.
NOTE: remember to add 'on-blur-val' exactly this way to your input control without the apostrophes
By using ng-focus you can achieve your goal. you need to provide ng-focus in your input field. And while writing your ng-show derivatives you have to write a logic not equal too. Like the below code:
<input type="text" class="form-control" name="inputPhone" ng-model="demo.phoneNumber" required ng-focus>
<div ng-show="demoForm.inputPhone.$dirty && demoForm.inputPhone.$invalid && !demoForm.inputPhone.$focused"></div>
We can use onfocus and onblur functions. Would be simple and best.
<body ng-app="formExample">
<div ng-controller="ExampleController">
<form novalidate class="css-form">
Name: <input type="text" ng-model="user.name" ng-focus="onFocusName='focusOn'" ng-blur="onFocusName=''" ng-class="onFocusName" required /><br />
E-mail: <input type="email" ng-model="user.email" ng-focus="onFocusEmail='focusOn'" ng-blur="onFocusEmail=''" ng-class="onFocusEmail" required /><br />
</form>
</div>
<style type="text/css">
.css-form input.ng-invalid.ng-touched {
border: 1px solid #FF0000;
background:#FF0000;
}
.css-form input.focusOn.ng-invalid {
border: 1px solid #000000;
background:#FFFFFF;
}
</style>
Try here:
http://plnkr.co/edit/NKCmyru3knQiShFZ96tp?p=preview

Mvvm with knockout : array binding and changing inner objects state

I have an array in my View Model. Items of this array are objects of Person that has two properties. when I bind this to a template it's okay. but when I change the state of one of the properties it does not reflect in UI.
what did I do wrong ?
<script type="text/html" id="person-template">
<p>Name: <span data-bind="text: name"></span></p>
<p>
Is On Facebook ?
<input type="checkbox" data-bind="checked: IsOnFacebook" />
</p>
</script>
<script type="text/javascript">
var ppl = [
{ name: 'Pouyan', IsOnFacebook: ko.observable(true) },
{ name: 'Reza', IsOnFacebook: ko.observable(false) }
];
function MyViewModel() {
this.people = ko.observableArray(ppl),
this.toggle = function () {
for (var i = 0; i < ppl.length; i++) {
ppl[i].IsOnFacebook = false;
}
}
}
ko.applyBindings(new MyViewModel());
</script>
when I press the button I want to make changes in People.IsOnFacebook property. the changes will be made successfully but the UI does not show.
You should call it like a function. Like:
ppl[i].IsOnFacebook(false);
This because the ko.observable() returns a function. It's not a property you call anymore but a function call. So in the background they will update your UI. To retreive a property that is observable. You should also use the function call.
Please see this tutorial: http://learn.knockoutjs.com/#/?tutorial=intro

Enforcing Case Insensitive uniqueness on fields at a given nesting level using angularjs

<FORM>
<DIV class="outer-class">
<INPUT class="toValidate" type = "text"/>
<INPUT class="somethingElse" type= "text"/>
<INPUT class="toValidate" type ="text"/>
</DIV>
<DIV class="outer-class">
<INPUT class="toValidate" type = "text"/>
<INPUT class="somethingElse" type= "text"/>
<INPUT class="toValidate" type ="text"/>
</DIV>
<INPUT type="submit"/>
</FORM>
My question is: How do I ensure that for the form to be valid, the nested toValidates have a unique value but only within the same outer div?
I am guessing this logic should go in an OuterClassDirective, but I can't seem to figure out what the logic should look like?
Any advice would be appreciated.
Thanks!
What about this. Your outerClassDirective should have a controller, which will store used values in an array. It will transclude the input fields in its body. Your toValidate directive requires outerClassDirective and adds the model value to the array, making it invalid if exists.
Here is a try (untested):
app.directive('outerClass', function() {
var values = [];
var valid = true;
return {
template: '<div ng-transclude></div>',
transclude: true,
replace: true,
require: 'ngModel',
controller: function() {
this.addValue: function(value) {
valid = values.indexOf(value) > -1;
values.push(value);
};
},
link: function(scope, elm, attrs, ctrl) {
ctrl.$setValidity('toValidate', valid)
}
};
});
app.directive('toValidate', function() {
return {
require: '^outerClass',
link: function(scope, elm, attrs, ctrl) {
ctrl.addValue(attrs.value);
}
}
};
});
The 'tabs' and 'pane' directives on the Angular home page solve a similar issue -- the child 'pane' directives need to communicate with the parent 'tabs' directive.
Define a controller on the outerclass directive, and then define a method on the controller (use this not $scope). Then require: '^outerclass' in the toValidate directive. In the toValidate link function, you can $watch for value changes and call the method on the outerclass controller to pass the value up. Do the validation in the outerclass directive.
See also 'this' vs $scope in AngularJS controllers.