I am new to parsers and formatters. I have a directive that will be doing validation on change of the model.One way to do this is the $watch but from what I understand that is not a good way since it allows the model to be updated.
So I was looking at parsers and tried this code
app.directive('myDirective', function($compile) {
return {
restrict: 'E',
require: 'ngModel',
scope: {
},
link: function($scope, elem, attr, ctrl) {
console.debug($scope);
ctrl.$formatters.push(function(value) {
console.log("hello1");
return value;
});
ctrl.$parsers.unshift(function(value) {
debugger;
console.log("hello");
return value;
});
}
};
});
But the parser function is never called. The formatter is called once. Please see the plunkr .Can anyone tell me what I am doing wrong ,why is the parser function not getting called when i type in the textbox ?
This is a late response, but for reference:
I think you where missing the "glue" that will call the $parsers while ui changes occurs. This should be something like:
app.directive('myDirective', function($compile) {
return {
restrict: 'E',
require: 'ngModel',
scope: {
},
link: function($scope, elem, attr, ctrl) {
console.debug($scope);
ctrl.$formatters.push(function(value) {
console.log("hello1");
return value;
});
ctrl.$parsers.unshift(function(value) {
return value;
});
scope.$watch('directive model here', function() {
ctrl.$setViewValue(<build model from view here>);
});
}
};
});
For a full reference please see this (awesome) post.
Your link function is not called because the associated DOM element is not changing, just the model is. This works:
HTML:
This scope value <input ng-model="name" my-directive>
JS:
app.directive('myDirective', function($compile) {
return {
require: 'ngModel',
link: function($scope, elem, attr, ctrl) {
ctrl.$parsers.unshift(function(value) {
console.log("hello");
});
}
};
});
See here for more on when the link function is called.
Related
I have a form that contain date inputs and select elements,
I tried to use ngChange but I found it not very useful when handling dates errors.
So I'm trying to set a watcher in a directive for every input.$error in order to display error message to the user.
my directive:
module.directive('validator', [function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elem, attr, ctrl) {
scope.input = ctrl;
scope.$watch('input.$error', function() {
console.log(scope.input);
}, true);
}
}
}]);
The problem with the directive is that the scope.$watch fires only when an error object of dates is changing, and the scope.input of every input becomes similar to the ctrl of the changed date input.
JSFiddle
Update your directive to watch if the input is invalid
module.directive('validator', [function() {
return {
restrict: 'A',
require: '^form',
link: function(scope, elem, attr, form) {
var inputName = attr.name;
console.log(inputName, form[inputName].$error)
function watcherForRequired(){
return form[inputName].$error.required;
}
function watcherForMin(){
return form[inputName].$error.min;
}
scope.$watch(watcherForRequired, function(required) {
console.log("required", required);
})
scope.$watch(watcherForMin, function(min) {
console.log("min error", min);
})
}
}
also update ng-min to min="{{loan.loaned}}"
here is JSFiddle
After deep checking of the directive behavior. I find out that the scope.input of the last element infect all the others scopes.
So I set the $watch to listen to ctrl.$error, and it solved the problem.
The directive:
module.directive('validator', [function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, elem, attr, ctrl) {
scope.$watch(function() {return ctrl.$error}, function() {
if (ctrl.$invalid) {
console.log(attr.name, 'invalid');
}
else {
console.log(attr.name, 'valid');
}
}, true);
}
}
}]);
Updated JSFiddle
You're doing this to display error messages? Why not simply do this in the template?
<form name="myform">
<input ng-model="myform.model.user-name" name="user-name" id="my-form-user-name" />
<div ng-messages="myform.user-name.$error">
<p ng-if="myform.user-name.$invalid">My Error Message Here</p>
</div>
</div>
I am use jQuery plugin select2 and want call select2 function in AngularJS. I know that can use angular-ui/ui-select, but page design require jQuery plugin select2.
I want to change the value in the model Angular and change the value of the select2.
I create Angular directive and I want to call select2 function, but do not know how to implement it correctly:
angular.module("myModule").directive("select2Value", [
function() {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
scope.$watch(function() {
element.select2().val(ngModel.$modelValue).change();
});
}
};
}
]);
I have this error:
Error: [$rootScope:inprog] $digest already in progress
For fix this error I am use this solution: https://stackoverflow.com/a/36219812/6746305 :
angular.module("myModule").directive("select2Value", [
"$timeout", function($timeout) {
return {
restrict: 'A',
require: 'ngModel',
link: function(scope, element, attrs, ngModel) {
var timeout;
changeValue(function() {
element.select2().val(ngModel.$modelValue).change();
});
timeout = void 0;
scope.$watch(function() {}, function(newVal, oldVal) {
if (timeout) {
$timeout.cancel(timeout);
}
timeout = $timeout(changeValue, 1000);
});
}
};
}
]);
But this not work: I am change value in Model Angular, but this value not change in select2.
I am find solution for change value in jQuery:
angular.module("myModule").directive("select2Value", [
"$timeout", function($timeout) {
return {
restrict: 'A',
priority: 0,
link: function(scope, element, attr, ngModel) {
return scope.$watch(attr.ngModel, function(value) {
return $timeout(function() {
return element.val(value).change();
});
});
}
};
}
]);
Solution find here (thank you this blog): http://blog.virtualzone.de/2015/08/angularjs-watching-for-changes-in-ng-model-and-determine-linked-dom-element.html
I'm injecting insecure html into some <div>, like this:
<div class="category-wrapper" ng-bind-html="content"></div>
this html has angularjs "code" ($scope.content is loaded with something like this):
<script type='text/javascript' src='giveus.js'></script>
<div class="giveus-wrapper" ng-controller="GiveUsController">{{variable1}}</div>
Note that this snippet has ng-controller. GiveUsController is lazy loaded at the same time that the embedded html (not in head). There is no error declaring this controller because It has been already tested.
My controller is as easy as:
angular.module("tf").controller('GiveUsController', function ($scope, $http)
{
console.debug("GiveUsController loaded");
$scope.variable1 = "hi!";
}
there is no console debug nor variable1 assignment
It looks like there is no controller binding to that <div>.
I don't know how I can inject html with angular controller and make it work...
Any idea?
You could do what you are wanting with a bit of manual html compilation. Here is an approach that is essentially a directive wrapper for the $compile service. Observe the following example and usage...
<div class="category-wrapper" ng-html="content"></div>
.controller('ctrl', function($scope) {
$scope.content = '<div class="giveus-wrapper" ng-controller="GiveUsController">{{variable1}}</div>'
})
.controller('GiveUsController', function($scope) {
console.log('hello from GiveUsController')
$scope.variable1 = 'I am variable 1'
})
.directive('ngHtml', ['$compile', function ($compile) {
return function (scope, elem, attrs) {
if (attrs.ngHtml) {
elem.html(scope.$eval(attrs.ngHtml));
$compile(elem.contents())(scope);
}
scope.$watch(attrs.ngHtml, function (newValue, oldValue) {
if (newValue && newValue !== oldValue) {
elem.html(newValue);
$compile(elem.contents())(scope);
}
});
};
}]);
JSFiddle Link - demo
Angular for itself don't bind the ng-directives that are added into the DOM.
The $sce.compile or $compile helps angular to read which elements are added into the actual DOM, also for use the $compile you must use a directive.
Should be like that:
var m = angular.module(...);
m.directive('directiveName', function factory(injectables) {
return = {
priority: 0,
template: '<div></div>', // or // function(tElement, tAttrs) { ... },
transclude: false,
restrict: 'A',
templateNamespace: 'html',
scope: false,
controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
controllerAs: 'stringIdentifier',
bindToController: false,
require: 'siblingDirectiveName', 'optionalDirectiveName', '?^optionalParent'],
compile: function compile(tElement, tAttrs, transclude) {
return {
pre: function preLink(scope, iElement, iAttrs, controller) { ... },
post: function postLink(scope, iElement, iAttrs, controller) { ... }
}
},
};
});
and where you want
$compileProvider.directive('compile', function($compile) {
return function(scope, element, attrs) {
scope.$watch(
function(scope) {
return scope.$eval(attrs.compile);
},
function(value) {
element.html(value);
$compile(element.contents())(scope);
}
);
};
You have to compile the HTML content, i got this using a directive:
.directive('comunBindHtml', ['$compile', function ($compile) {
return function(scope, element, attrs) {
scope.$watch(
function(scope) {
// watch the 'compile' expression for changes
return scope.$eval(attrs.compile);
},
function(value) {
// when the 'compile' expression changes
// assign it into the current DOM
element.html(value);
// compile the new DOM and link it to the current
// scope.
// NOTE: we only compile .childNodes so that
// we don't get into infinite loop compiling ourselves
$compile(element.contents())(scope);
}
);
};
}])
Hope it helps :)
I want to have an attribute directive a bit similar to ng-model. I just want to additionally bind an input fields value to a scope variable (just in one direction input field -> scope variable). So I have just tried this directive but I can not get the directive called anyway.
script:
.directive('passivemodel', function () {
return {
restrict: "A",
scope: {
passivemodel: '='
},
link: function (scope, element, attrs) {
scope.$watch('passivemodel', function(newPassivemodel, oldPassivemodel) {
console.log("passive model", newPassivemodel);
});
}
};
})
html:
<input data-passivemodel="keyword">
Edit:
Hmmm .. based on vilo20 answer I am running into a very strange behavior.
while this code is working very well:
<input data-ng-model="keyword" data-passivemodel="keyword">
this one does not (note the value of passivemodel):
<input data-ng-model="keyword" data-passivemodel="keyword2">. Sure I have defined the variable in the controller.
Controller:
.controller('SearchController', function($scope, $routeParams, $search) {
$scope.search = new $search();
$scope.keyword = "";
$scope.keyword2 = "";
})
Edit2: here is a fiddle http://jsfiddle.net/HB7LU/12107/
try this:
.directive('passivemodel', function () {
return {
restrict: "A",
scope: {
passivemodel: '='
},
link: function (scope, element, attrs) {
console.log("passive model", scope.passivemodel);
$scope.$watch('passivemodel', function(newPassivemodel, oldPassivemodel) {
//put your logic when passivemodel changed
});
}
};
})
Hope it helps
Edit: Here is a plunker http://plnkr.co/edit/T039I02ai5rBbiTAHfzv?p=preview
Use the scope attribute:
.directive('passivemodel', function () {
return {
restrict: "A",
scope: {
"passivemodel": "="
},
link: function (scope, element, attrs) {
console.log("access passivemodel: ", scope.passivemodel);
}
};
})
Finally it was as simple as that:
.directive('modelRed', [function(){
return {
require: 'ngModel',
restrict: 'A',
scope: {},
link: function (scope, element, attrs, ngModel) {
scope.$watch(function () {
return ngModel.$modelValue;
}, function(newValue) {
scope.$parent[attrs.modelRed] = newValue;
//console.log(attrs.modelRed, newValue, scope);
});
}
}
}])
And in the html:
<p><input type="text" data-ng-model="testInput" data-model-red="redInput">{{redInput}}</p>
Of course you have to define testInput and redInput in the $scope object.
how do I share a method between two directives? When I try it now the console says scope.triggerInput(); is undefined; If I replace the scope.triggerinput with just a string it works. So I guess I do it somewhere wrong in the attribute.
app.directive('myImage', function () {
return{
restrict: 'A',
template: ' <img ng-src="images/needed/noPhoto.png"class="hover">',
link: function (scope, element, attr) {
element.bind('click', function () {
scope.triggerInput();
});
}
};
});
app.directive('myFileInput', function (httpPostFactory) {
return{
restrict: 'A',
scope: {
triggerInput: '='
},
link: function (scope, element, attr) {
scope.triggerInput = function () {
console.log('triggerInput');
element[0].click();
};
}
};
});
html
<div data-my-image></div>
<input data-my-file-input type="file" name="file" triggerInput='triggerInput()'>
Very good question! If I understand your situation correctly, Angular Service is what you need to use for situations like these.
app.service('clickResponder',function(){
var srvc={};
srvc.triggerInput=function(){
// Do your stuff here.
}
return srvc;
}
Now, to use this service, its very simple just inject it on your directive like below:
app.directive('myImage', function (clickResponder) {
return{
restrict: 'A',
template: ' <img ng-src="images/needed/noPhoto.png"class="hover">',
link: function (scope, element, attr) {
element.bind('click', function () {
clickResponder.triggerInput();
});
}
};
});
Thats all you need to do. Hope this helps you in some way!