Call jQuery plugin function in AngularJS - javascript

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

Related

How to set watcher for input.$error object with directive in AngularJS

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>

what if I want to create directive that match only for custom element && atribute

let assume that I want to create directive that matched only for element that match amInput[type=dropdown] how can I do that?
I can for example:
.directive('amInput',function () {
return {
restrict: "E",
scope: {
data:'#'
},
compile:function(tElement, tAttrs){
if (tAttrs.type != 'dropdown') return;
return function link ($scope, element, attr, ctrl) {
var parseResult = parse($scope.data);
}
}
}
});
but if I define another directive with isolate scope for am-input[type=checkbox]
.directive('amInput',function () {
return {
restrict: "E",
scope: {
data2:'#'
},
compile:function(tElement, tAttrs){
if (tAttrs.type != 'checkbox') return;
return function link ($scope, element, attr, ctrl) {
var parseResult = parse($scope.data2);
}
}
}
});
angular#$compile throw exception about two directives define isolate scope.
Error: [$compile:multidir] Multiple directives [amInput, amInput] asking for new/isolated scope on: <am-input type="checkbox"></am-input>
directive name should be unique (as long as they match the same restrict)
in your case you should probably merge them into one.
(just as a reference, this might help: Angular directive name: only lower case letters allowed?)
Ok, I end up with that solution that make the differntes between the directives by postlink. when prelink is for everythig is similar between them:
directive('amInput',function () {
var template = {
'dropdown' :require('./dropdown.drv.html'),
'checkbox-group' :require('./checkbox-group.drv.html')
};
var postLinkPerType = {
'dropdown': require('./postLink-OneFromMany'),
'checkbox-group':require('./postLink-ManyFromMany')
};
return {
restrict: "E",
require: 'ngModel',
scope:{
selectAll:'='
//,...
},
transclude:true,
controller: function(scope, element, attr) {
/*for binding stuff that use by post links */
},
controllerAs:'inputCtrl',
bindToController:false,
template: function (element, attr) {
return template[attr.type];
},
compile: function (element, attr, ctrl, transcludeFn) {
return {
pre: function (scope, element, attr, ctrl, transcludeFn) {
/*******************************************/
/* some general/init code for all collection*/
/*******************************************/
/* init options list */
if( attr.data ){
/***********/
/* some code just if there is some attribute*/
/***********/
}
},
/*the little different code that make every directive in collection different*/
/*different by TYPE attribute*/
post: postLinkPerType[attr.type]
}
}
}
})

how to watch a date object in angularJS

Is it possible to watch a date in angularJS ?
I have an ng-model that contains a date and i want to watch it so when it change of month,year or day i got a notification.
scope.$watch('ngModel',function(newVal){
console.log(newVal);
},true);
I have a controller where i give a date object to a directive.
And now i want to watch that date object.
module.directive('Datepicker',function(){
return {
restrict:'E',
scope:{
ngModel:"="
},
link: function(scope, element, attrs) {
//..other code here
scope.$watch('ngModel', function(newVal) {
console.log(ngModel);
}, true);
}
}
});
I believe you are in directive then you should use attrs.ngModel instead of ngModel only, it should be inside link/compile function of directive
Code
app.directive('myDirective', function() {
return {
'restrict': 'E',
scope: { ngModel: '='},
link: function(scope, element, attrs, ngModel) {
//..other code here
scope.$watch('$parent.'+ attrs.ngModel, function(newVal) {
console.log(newVal);
}, true);
}
};
});
you ngModel should be available as $scope:
function myController($scope){
$scope.today = new Date()
$scope.$watch('today',function(newVal,oldVal){
//code goes here
},true)
}

Angularjs directive

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.

Parser function does not get called for change in input textbox

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.

Categories