I'm trying to put together an Angular directive that will be a replacement for adding
ng-disabled="!canSave(schoolSetup)"
On a form button element where canSave is a function being defined in a controller something like the following where the parameter is the name of the form.
$scope.canSave = function(form) {
return form.$dirty && form.$valid;
};
Ideally I'd love the directive on the submit button to look like this.
can-save="schoolSetup"
Where the string is the name of the form.
So... how would you do this? This is as far as I could get...
angular.module('MyApp')
.directive('canSave', function () {
return function (scope, element, attrs) {
var form = scope.$eval(attrs.canSave);
function canSave()
{
return form.$dirty && form.$valid;;
}
attrs.$set('disabled', !canSave());
}
});
But this obviously doesn't bind properly to the form model and only works on initialisation. Is there anyway to bind the ng-disabled directive from within this directive or is that the wrong approach too?
angular.module('MyApp')
.directive('canSave', function () {
return function (scope, element, attrs) {
var form = scope.$eval(attrs.canSave);
scope.$watch(function() {
return form.$dirty && form.$valid;
}, function(value) {
value = !!value;
attrs.$set('disabled', !value);
});
}
});
Plunker: http://plnkr.co/edit/0SyK8M
You can pass the function call to the directive like this
function Ctrl($scope) {
$scope.canSave = function () {
return form.$dirty && form.$valid;
};
}
app.directive('canSave', function () {
return {
scope: {
canSave: '&'
},
link: function (scope, element, attrs) {
attrs.$set('disabled', !scope.canSave());
}
}
});
This is the template
<div ng-app="myApp" ng-controller="Ctrl">
<div can-save="canSave()">test</div>
</div>
You can see the function is called from the directive. Demo
Related
my directive:
(function () {
var directive = function ($compile, $http, $rootScope, $translate) {
return {
restrict: 'E',
scope: {
baseUrl: '#rmsUrl',
},
link: function (scope, element, attrs) {
$rootScope.languageSwitcher = {
toggle: true,
changeLanguage: function () {
if ($rootScope.languageSwitcher.toggle) {
$translate.use('ENG');
} else {
$translate.use('FRE');
}
}
}
$rootScope.$on('oauth2:authSuccess', function (data) {
var html2 = 'French <label class="switch">'
html2 += '<input type="checkbox" ng-model="languageSwitcher.toggle" ng-change="languageSwitcher.changeLanguage()" /><div></div>'
html2 += '</label>English'
element.html(html2);
$compile(element.contents())(scope);
});
}
};
};
angular.module('testingApp')
.directive('rmsLanguageToggler', directive);
}());
my index file contain the directive:
<rms-language-toggler rms-url='blah blah blah'></rms-language-toggler>
my question:
The html render correctly, however changing/clicking the checkbox doesn't trigger the function: $rootScope.languageSwitcher.changeLanguage()
I found out why now. Turns out the ng-change and ng-model inside the directive is still referencing to the inner isolated scope. I have to use $parent. to access the outer model, i.e. $parent.languageSwitcher.toggle
Hi I am working on angularjs. I am facing an issue in directive.
I have set the scope.user.name="amin shah" on link/click event
and want to access this in controller how is this possible?
var dataSourceDirective = angular.module('mydirective', []);
dataSourceDirective.directive('dir', function () {
return {
restrict: 'C',
scope: true,
link: function ($scope, element, attrs) {
element.bind('click', function () {
$scope.user.name ="amin shah";
$scope.$apply();
$('.sourceType_panel').hide();
$('#sourceType_1_panel').show();
});
}
}
});
controller code
$scope.demo = function () {
console.log($scope.user);`
},
You need to create Isolated scope in your directive.
The given controller should be parent of this directive.
var dataSourceDirective = angular.module('mydirective', []);
dataSourceDirective.directive('dir', function () {
return {
restrict: 'C',
scope: {user:"=user"},
link: function ($scope, element, attrs) {
element.bind('click', function () {
$scope.user.name ="amin shah";
});
}
}
});
In html :
<div ng-copntroller='yourCtrl'>
<dir user="user"></dir>
</div>
In Controller you should initialize the user.
OR
you use $broadcast & $emit if the parent is controller.
Withing link function of directive you can use $rootScope.$emit('user_name_update',user);
And in the controller you can listen this event
$scope.$on('user_name_update',function(data){
console.log(user) // its should give your updated `user` object
})
First of all you should correct your link method and I think you shouldn't need child sope at there. So you should delete your scope bind in directive too. You can reach parent scope with link method.
app.directive('dir', function () {
return {
restrict: 'E',
link: function (scope, element, attrs) {
element.bind('click', function () {
scope.user.name ="amin shah";
scope.$apply();
});
}
}
});
and in your controller you can define scope variable like that:
app.controller('MainCtrl', function($scope) {
$scope.user = {
name: ''
}
});
also you should add this directive to HTML :
<dir>Element</dir>
<p>{{user.name}}</p>
here is the working plunkr you should click Element than you can see your name from directive but in parent scope
https://plnkr.co/edit/umTdfukZ22hARoLjxdL3?p=preview
I have a directive.This directive handles drag and drop of child element. The list of the child elements will be generated by the directive from an given array of data. To render each Element I'd like to give the directive a render. At this time I use a fiter. But I think that is not a job for a filter. Is there a way to use an other directive for it?
directive:
module.directive('dndSortable', ['$filter', '$log',
function ($filter, $log) {
return {
restrict: 'A',
scope: {
dndValues: '=',
dndRenderFilter: '#'
},
link: function (scope, element) {
angular.forEach(dndValues, function(value){
//my first idea was to use a fiter this will create a element
//but I think thats not the function of fiters
//maybe there is a way to use a directive here
var dndElement = $filter(scope.dndRenderFilter)(value);
// ... add event handling for dnd to the dndElement
});
}
}
}]);
filter:
module.filter('mySortListRenderFilter', function () {
return function (imageValue) {
var img,
prefix = 'http://prefix/url';
if (!angular.isUndefined(imageValue) || !imageValue.hasOwnProperty('scr')) {
img = angular.element('<img>');
img.attr('src', prefix + imageValue.src);
img.addClass('teaser-gallery-image');
}
return img;
};
});
html:
<div
dndSortable
dnd-values="mySortableList"
dnd-render-filter="mySortListRenderFilter">
</div>
I have a custom directive:
.directive('myDirective', function() {
return {
scope: {ngModel:'='},
link: function(scope, element) {
element.bind("keyup", function(event) {
scope.ngModel=0;
scope.$apply();
});
}
}
});
This works as planned, setting the variables to 0 on keyup, but it doesn't reflect the changes on the input themselves. Also when initialized, the values of the model are not in the input. Here is an example:
http://jsfiddle.net/prXm3/
What am I missing?
You need to put a watcher to populate the data since the directive creates an isolated scope.
angular.module('test', []).directive('myDirective', function () {
return {
scope: {
ngModel: '='
},
link: function (scope, element, attrs) {
scope.$watch('ngModel', function (val) {
element.val(scope.ngModel);
});
element.bind("keyup", function (event) {
scope.ngModel = 0;
scope.$apply();
element.val(0); //set the value in the dom as well.
});
}
}
});
Or, you can change the template to
<input type="text" ng-model="$parent.testModel.inputA" my-directive>
the data will be populated thought it will break your logic to do the event binding.
So it is easier to use the watcher instead.
Working Demo
I'm trying to understand directives, and I'm having problems with two way data binding.
My directive will be used to submit a form when "enter" is pressed in a textarea.
I found a solution in another SO thread (see the code below in the scope definition of the directive), but I don't like it because it means that if I change the model name, I need to change the directive as well..
--> Here is the problem in codepen.io
Here is the html part:
<div ng-app="testApp" ng-controller="MyController">
<textarea ng-model="foo" enter-submit="submit()"></textarea><br/>
Binding: {{foo}}
</div>
Here is the javascript part:
var app = angular.module('testApp', []);
function MyController($scope) {
$scope.foo = "bar"
$scope.submit = function() {
console.log("Submitting form");
}
}
app.directive('enterSubmit', function () {
return {
restrict: 'A',
scope: {
submitFn: '&enterSubmit',
foo: '=ngModel' // <------------------- dont't like this solution
},
link: function (scope, elem, attrs) {
elem.bind('keydown', function(event) {
var code = event.keyCode || event.which;
if (code === 13) {
if (!event.shiftKey) {
event.preventDefault();
scope.submitFn();
}
}
});
}
}
});
Thanks for your help !
When multiple directives are used on an element, normally you don't want any of them to use an isolate scope, since that forces all of them to use the isolate scope. In particular, isolate scopes should not be used with ng-model – see Can I use ng-model with isolated scope?.
I suggest creating no new scope (the default, i.e., scope: false):
app.directive('enterSubmit', function () {
return {
restrict: 'A',
//scope: {
// submitFn: '&enterSubmit',
// foo: '=ngModel' // <------------------- dont't like this solution
//},
link: function (scope, elem, attrs) {
elem.bind('keydown', function(event) {
var code = event.keyCode || event.which;
if (code === 13) {
if (!event.shiftKey) {
event.preventDefault();
scope.$apply(attrs.enterSubmit);
}
}
});
}
}
});