I'm using a jQuery-ui datapicker and have defined a directive (credit: http://www.abequar.net/posts/jquery-ui-datepicker-with-angularjs) to instantiate it. I'd now like to add an html attribute that defines the function to call to determine whether a day should be selectable in the datepicker. How would I do this?
The js:
//Directive for showing the jquery-ui datepicker
myApp.directive('datepicker', function() {
return {
restrict: 'A',
require : 'ngModel',
link : function (scope, element, attrs, ngModelCtrl) {
$j(function(){
//Instantiate the datepicker
element.datepicker({
dateFormat:'dd/mm/yy',
beforeShowDay:function(date) {
//TODO Call function defined in attrs, passing the date object to it
theDefinedFunction(date)
},
onSelect:function (date) {
ngModelCtrl.$setViewValue(date);
scope.$apply();
}
});
});
}
}
});
The html:
<input type="text" datepicker show-days="myShowDaysFunction()" />
The myShowDaysFunction() would be defined in the controller.
(Edit) - Controller function:
$scope.myShowDaysFunction = function(date) {
console.log("I get called"); //Logs to the console
console.log(date); //Logs undefined to the console
}
Thanks.
You need to create an isolate scope on your directive and take the function as a scope variable.
myApp.directive('datepicker', function() {
return {
restrict: 'A',
require : 'ngModel',
scope: {
showDays: '&'
},
link : function (scope, element, attrs, ngModelCtrl) {
$j(function(){
//Instantiate the datepicker
element.datepicker({
dateFormat:'dd/mm/yy',
beforeShowDay:function(date) {
//TODO Call function defined in attrs, passing the date object to it
scope.showDays({date: date});
},
onSelect:function (date) {
ngModelCtrl.$setViewValue(date);
scope.$apply();
}
});
});
}
}
});
Markup
<input type="text" datepicker show-days="myShowDaysFunction(date)" />
Controller Function
$scope.myShowDaysFunction = function(date) {
alert(date);
}
Plunker Exmaple
http://plnkr.co/edit/kRc76icPUa9qTkPH4UKm?p=preview
Related
Im trying to call the validation function from directive in the controller. Is it possible?
My directive is something like this:
app.directive('evenNumber', function(){
return{
require:'ngModel',
link: function(scope, elem, attrs, ctrl){
ctrl.$parsers.unshift(checkForEven);
function checkForEven(viewValue){
if (parseInt(viewValue)%2 === 0) {
ctrl.$setValidity('evenNumber',true);
}
else{
ctrl.$setValidity('evenNumber', false);
}
return viewValue;
}
}
};
});
I want to call the function checkForEven from the controller.
$scope.copyFileContentToEditor = function(){
$scope.code = $scope.content;
// TODO call the checkForEven directive function to validate the $scope.code
}
Is it possible to do that? Any suggestions?
You may need to define a link between your controller and your directive to get them know each other.
app.controller('MainCtrl', function($scope) {
$scope.name = 'World'; // this is just used for your directive model, it is not the part of the answer
$scope.vm = {} // define this to create a shared variable to link the controller and directive together.
});
app.directive('evenNumber', function(){
return{
require:'ngModel',
scope: {'vm': '='},
restrict: 'A',
link: function(scope, elem, attrs, ctrl){
function checkForEven(){
alert('I get called.')
}
scope.vm.checkForEven = checkForEven; // once the directive's method is assigned back to "vm", so you could trigger this function from your controller by call this vm.checkForEven;
}}})
HTML
<div ng-model="name" even-number vm="vm"></div>
Plunker Example
Add the function as a property of the ngModel controller:
app.directive('evenNumber', function(){
return{
require:'ngModel',
link: function(scope, elem, attrs, ctrl){
ctrl.$parsers.unshift(checkForEven);
//ADD checkForEven to ngModel controller
ctrl.checkForEven = checkForEven;
function checkForEven(viewValue){
if (parseInt(viewValue)%2 === 0) {
ctrl.$setValidity('evenNumber',true);
}
else{
ctrl.$setValidity('evenNumber', false);
}
return viewValue;
}
}
};
});
Then name the form and input element:
<form name="form1">
<input name="input1" ng-model="vm.input1" even-number />
</form>
The controller can then reference it where it attaches to scope:
$scope.copyFileContentToEditor = function(){
$scope.code = $scope.content;
//CALL the function
$scope.form1.input1.checkForEven($scope.vm.input1);
}
I am using Angular Bootstrap Selectpicker.
I am using angular version 1.4.7.
For select picker they provide directive,
angular.module('angular-bootstrap-select', [])
.directive('selectpicker', ['$parse', function ($parse) {
return {
restrict: 'A',
require: '?ngModel',
priority: 10,
compile: function (tElement, tAttrs, transclude) {
tElement.selectpicker($parse(tAttrs.selectpicker)());
tElement.selectpicker('refresh');
return function (scope, element, attrs, ngModel) {
if (!ngModel) return;
scope.$watch(attrs.ngModel, function (newVal, oldVal) {
scope.$evalAsync(function () {
if (!attrs.ngOptions || /track by/.test(attrs.ngOptions)) element.val(newVal);
element.selectpicker('refresh');
});
});
ngModel.$render = function () {
scope.$evalAsync(function () {
element.selectpicker('refresh');
});
}
};
}
};
}]);
Select picker looks like
<select class="form-control" data-style="btn-default"
data-live-search="true" selectpicker multiple
data-selected-text-format="count>2"
data-collection-name="users"
ng-model="selectedUsers"
ng-options="user.name for user in users">
</select>
Above case, selectedUser will have tickmarks and If I change value for selectedUsers from controller. It not show tick marks for updated selectedUsers options.
When I select multiple options it shows tick mark for selected options.
Then if I refresh view then though ng-model have previous values still it don't show tick mark for values in ng-model.
Plunker
I simplified your code to work. Check this Demo
.directive('selectpicker', ['$parse', selectpickerDirective]);
function selectpickerDirective($parse) {
return {
restrict: 'A',
priority: 1000,
link: function (scope, element, attrs) {
//New change
scope.$watch(attrs.ngModel, function(n, o){
element.selectpicker('val', $parse(n)());
element.selectpicker('refresh');
});
}
};
}
EDIT 1
Look this: http://plnkr.co/edit/4TiSJwKtcln9z39cxHZ3?p=preview
The library bootstrap-select is compatible with jquery, but not compatible with angular. For this you can got it utilizing advanced artifice of programmer. rsrsrs. Look in my example. I simulate click of user in element.
angular.element("li[data-original-index='1']").find("a").click();
EDIT 2
Change selectpickerDirective to:
function selectpickerDirective($parse) {
return {
restrict: 'A',
priority: 1000,
link: function (scope, element, attrs) {
scope.$watch(attrs.ngModel, function(n, o){
if(n)
element.selectpicker('val', ["string:" + n[0].name]);
element.selectpicker('refresh');
});
}
};
}
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;
});
}
};
}])
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