Was wondering how I should handle async functions in $parsers.
The below code doesn't update the scope.
I'm using AngularJS 1.2 so can't make use of the new and fancy 1.3 features.
http://plnkr.co/edit/uk9VMipYNphzk8l7p9iZ?p=preview
Markup:
<input type="text" name="test" ng-model="test" parse>
Directive:
app.directive('parse', function($timeout) {
return {
require: 'ngModel',
link: function(scope, element, attrs, ctrl) {
ctrl.$parsers.unshift(function(viewValue) {
$timeout(function() {
return viewValue;
});
});
}
};
});
If you are looking for async validation function, I did something like that some time ago and release it as a library. Check the custom-remote-validator directive here.
The basic idea was use ngModelController $setValidity after receiving validation result from server. This is the directive source code
.directive('customRemoteValidator', [function () {
return {
restrict: 'A',
require: 'ngModel',
link: function (scope, elm, attr, ngModelCtrl) {
var validateFunctionNames = attr["remoteValidateFunctions"].split(",");
var validatorNames = attr["customRemoteValidator"].split(",");
ngModelCtrl.$parsers.push(function (value) {
angular.forEach(validateFunctionNames, function (functionName, index) {
if (!scope[functionName]) {
console.log('There is no function with ' + functionName + ' available on the scope. Please make sure the function exists on current scope or its parent.');
} else {
var result = scope[functionName](value);
if (result.then) {
result.then(function (data) { //For promise type result object
ngModelCtrl.$setValidity(validatorNames[index], data);
}, function (error) {
ngModelCtrl.$setValidity(validatorNames[index], false);
});
}
}
});
return value;
});
}
};
}])
Related
When the inputbox will change, then I'd like to set my form to dirty. I want to make it using directive. This is what I already did.
HTML:
<form name="myForm">
<type="text" ng-model="myModel" set-form-dirty>
</form>
Directive:
app.directive('setFormDirty', function () {
return {
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
scope.$watch(function () {
return ngModel.$modelValue;
}, function(newValue) {
console.log(newValue);
// Here I should set form to dirty. But I don't know how?
});
}
};
});
#EDIT
I need this because myModel is changing by different function. And that function won't set form to $dirty
You can watch the modelvalue on the ngModelController.
.directive('setFormDirty', function(){
return {
require: 'ngModel',
link: function (scope, element, attrs, ngModel) {
scope.$watch(function(){
return ngModel.$modelValue;
},function(newValue, oldValue){
//Check stuff here and use below code to set it to dirty
ngModel.setDirty();
});
}
};
Solved in this way:
angular.module("qs.forms").directive("setFormDirty", function () {
return {
require: ["ngModel", "^form"],
link: function (scope, element, attrs, ctrls) {
scope.$watch(function () {
return ctrls[0].$modelValue;
}, function (newValue) {
if (newValue) {
ctrls[1].$dirty = true;
}
});
}
};
});
Form could be passed by require: ["ngModel", "^form"],
I have a form that has a username field and other fields, this field has an async validator that checks whether that username is available when you try to submit the form (there are other fields on the form using async validation). I use this directive to validate and submit the form (only if it's valid):
<form validation-submit="submit()"></form>
app.directive('validationSubmit', ['$parse', function($parse) {
return {
restrict: 'A',
require: '?form',
compile: function($element, attr) {
var submitFn = $parse(attr.validationSubmit);
return function link(scope, element, attrs, FormController) {
var submit = function(event) {
scope.$apply(function() {
if (! FormController.$valid) {
return;
}
submitFn(scope, {$event: event}));
});
};
element.bind('submit', submit);
scope.$on('$destroy', function() {
return element.off('submit', submit);
});
}
}
};
}]);
The problem is this directive is not waiting for the pending async validations to finish. How can I change this directive to only submit after all async validations finished and passed?
Recently I've created a couple of directives that were checking whether email or phone number is available. I found solution that helps me. It's $asyncValidators .
app.directive('validationSubmit', ['$parse', function($parse) {
return {
restrict: 'A',
require: '?form',
link: function(scope, element, attrs, FormController) {
/**
*returns promise
**/
FormController.$asyncValidators.validEmail = function (modelValue) {
return $q(function (resolve, reject) {
//Here you should make query to your server and find out wether username valid or not
//For example it could be:
$http('http://your_api_server/Available').then(function (response) {
if (response.data.Available) {
resolve();
} else {
reject();
}
}, function (err) {
reject(err.data);
});
});
};
}
};
}]);
You should add attribute name to the form:
<form validation-submit="submit()" name="myForm"></form>
And now you have opportunity to check system properties of form in your controller:
$scope.submit = function(){
//Here will be your logic
if($scope.myForm.$valid ||
$scope.myForm.$invalid ||
$scope.myForm.$pending ||
$scope.myForm.$submitted){
//do whatever you want
}
}
I have a directive that is designed to be assigned to a normal text input.
<input type="text" ng-model="fooModel" foo-input size="30"
placeholder="insert value"></input>
I have lots of validation functions for it like testing the precision of the numbers and I use a $parsers to control the value that is submitted.
myApp.directive('fooInput', function () {
return {
restrict: 'A',
require: 'ngModel',
controller: function ($scope, $element, $attrs) {
this.errorMessage = ""
},
link: function (scope, element, attrs, ctrl)
return ctrl.$parsers.push(function (inputValue) {
var originalVal = element.val();
if (!testForOverPrecision(numericVal)) {
//do something here to set the directive as invalid
}
if (originalVal != inputValue) {
ctrl.$setViewValue(res);
ctrl.$render();
}
});
I have 2 questions:
How do I get this to work with the isValid service and do I have to have a controller scope for the error message
Is it correct for me to push the $parser inside a return statement
I am using Angular 1.2x and I created a directive to determine if the text contains the # symbol.
.directive('noAt', function() {
return {
require: 'ngModel',
link: function(scope, elm, attrs, ctrl) {
ctrl.$parsers.unshift(function(viewValue) {
if (/#/.test(viewValue)) {
ctrl.$setValidity('noAt', false);
return undefined;
} else {
ctrl.$setValidity('noAt', true);
return viewValue;
}
});
}
};
})
I am using ng-repeat and setting a model with it similar to the following
<div ng-repeat="thing in things" ng-model="thing" my-directive>
{{thing.name}}
</div>
then in my directive it looks something like this
.directive("myDirective, function () {
return {
require: 'ngModel',
link: function(scope, lElement, attrs, model) {
console.log(model.name);// this gives me 'NAN'
}
}
})
My question is how can I access the values in the model? I tried model.$modelValue.name but that did not work.
If you want to bind in a scoped value then you can use the '=' in an isolated. This will appear on the scope of your directive. To read the ng-model directive, you can use =ngModel:
.directive("myDirective", function () {
return {
scope: {
model: '=ngModel'
}
link: function(scope) {
console.log(scope.model.name); // will log "thing"
}
}
});
.directive("myDirective", function () {
return {
require: 'ngModel',
link: function(scope, lElement, attrs, model) {
console.log(attrs.ngModel); // will log "thing"
}
}
})
If your directive does not have isolated or child scope then you can do this:
.directive('someDirective', function() {
return {
require: ['^ngModel'],
link: function(scope, element, attrs, ctrls) {
var ngModelCtrl = ctrls[0];
var someVal;
// you have to implement $render method before you can get $viewValue
ngModelCtrl.$render = function() {
someVal = ngModelCtrl.$viewValue;
};
// and to change ngModel use $setViewValue
// if doing it in event handler then scope needs to be applied
element.on('click', function() {
var val = 'something';
scope.$apply(function() {
ngModelCtrl.$setViewValue(val);
});
});
}
}
});
Not sure how to phrase the question so please edit if you can come up with something better. I have the following directive:
app.directive('foo', function() {
return {
restrict: 'A',
require: "?ngModel",
link: function (scope, element, attrs, controller) {
scope.$watch(attrs.ngModel, function () {
console.log("Changed to " + scope[attrs.ngModel]);
});
}
};
});
When I have this it works great and logs properly
<input type="text" ng-model="bar" />
app.controller('fooController', function($scope) {
$scope.bar = 'ice cream';
});
It doesn't work when I try it this way around. It keeps logging 'Changed to undefined'
<input type="text" ng-model="model.bar" />
app.controller('fooController', function($scope) {
$scope.model = { bar: 'ice cream' };
});
How do I make it work for both scenarios. It seems the right thing to do seeing as angular lets you use both.
I looked at ngModel directive and found a function called ngModelGet. Uses $parse.
app.directive('foo', function($parse) {
return {
restrict: 'A',
require: "?ngModel",
link: function (scope, element, attrs, controller) {
var ngModelGet = $parse(attrs.ngModel);
scope.$watch(attrs.ngModel, function () {
console.log("Changed to " + ngModelGet(scope));
});
}
};
});
your can use
var ngModelCtrl = controller;
ngModelCtrl.$viewValue
replace
scope[attrs.ngModel]
here is ngModelCtrl sdk