I'd like to call "myFunc()" inside my angular directive, how might I do this?
myApp.directive("test", function () {
return {
restrict: 'A',
template: "<div class='box'></div>",
myFunc: function() {
console.log('myFunc');
},
link: function ($scope, element, attrs) {
element.bind('click', function () {
myFunc(); //<------------- doesn't work
});
}
} // of return
});
You can't define the function as a property of your return value in your call to directive. It either needs to be defined before your return:
myApp.directive('test', function() {
var myFunc = function() {
console.log('myFunc');
};
return {
restrict: 'A',
template: '<div class="box"></div>',
link: function($scope, element, attrs) {
element.bind('click', myFunc);
}
};
};
Or the same way inside of your link function.
Just to play around :)
var app = angular.module('myApp', []);
app.controller('MainController',function() {
});
app.directive('one', function() {
return angular.extend({}, app.directive, {myfunct:function(){
alert('hello');
}});
});
app.directive('two', function(oneDirective) {
return {
link:function($scope,$element){
console.log(oneDirective[0].myfunct)
$element.on('click',oneDirective[0].myfunct);
}
};
});
or use the method binding "&":
app.directive('myDir', function() {
return {
scope: {
callback: "&"
},
link:function($scope,$element){
element.bind('click', function () {
$scope.evalAsync(function() {
$scope.callback({param1: value, param2: value2});
})
});
}
};
});
Usage:
<my-dir callback="myControllerMethod(param1, param2)"></my-dir>
Related
I have a directive Foo in Directive Bar i am trying to call a function in Foo
but it is not working.
http://jsfiddle.net/4d9Lfo95/3/
example fiddle is created.
angular.module('ui', []).directive('uiFoo',
function() {
return {
restrict: 'E',
template: '<p>Foo</p>',
link: function($scope, element, attrs) {
$scope.message = function() {
alert(1);
};
},
controller: function($scope) {
this.message = function() {
alert("Foo Function!");
}
}
};
}
).directive('uiBar',
function() {
return {
restrict: 'E',
template: '<button ng-click="callFunction()">Bar</button> <ui-foo></ui-foo>',
require: 'uiFoo',
scope: true,
link: function($scope, element, attrs, uiFooController) {
$scope.callFunction = function() {
alert('Bar Function');
uiFooController.message();
}
}
};
}
);angular.module('myApp', ['ui']);
where as the UI looks like this
<div ng-app="myApp"> <ui-bar> </ui-bar></div>
You left out this error message:
Controller 'uiFoo', required by directive 'uiBar', can't be found!
The problem is that the require hierarchy searches up the tree, not down it. So, ui-bar is trying to find a uiFoo directive controller either on itself or (with the ^ symbol) in one of it's ancestors, not one of it's children.
If you want to call a method from the child directive, just use the scope: http://jsfiddle.net/4d9Lfo95/5/
angular.module('ui', []).directive('uiFoo',
function() {
return {
restrict: 'E',
template: '<p>Foo</p>',
controller: function($scope) {
$scope.message = function() {
alert("Foo Function!");
}
}
};
}
).directive('uiBar',
function() {
return {
restrict: 'E',
template: '<button ng-click="callFunction()">Bar</button> <ui-foo></ui-foo>',
scope: true,
controller: function($scope) {
$scope.callFunction = function() {
alert('Bar Function');
$scope.message();
}
}
};
}
);
Update:
https://plnkr.co/edit/kbz5uimNZ6vKTiT9QB6a?p=preview is created for testing.
I have the following directive egFiles to handle the status of <input type="file" id="newFile" eg-files="model.files" has-file="model.hasFile" /> for file uploading.
(function () {
'use strict';
angular.module('myApp').directive('egFiles', egFiles);
function egFiles() {
var directive = {
link: link,
restrict: 'A',
scope: {
files: '=egFiles',
hasFiles: '='
}
};
return directive;
function link(scope, element, attrs) {
element.bind('change', function () {
scope.$apply(function () {
if (element[0].files) {
scope.files.length = 0; // Breakpoint not hit here
angular.forEach(element[0].files, function (f) {
scope.files.push(f);
});
scope.hasFiles = true;
}
});
});
if (element[0].form) {
angular.element(element[0].form)
.bind('reset', function () {
scope.$apply(function () {
scope.files.length = 0;
scope.hasFiles = false;
});
});
}
}
}
})();
The following the Anguar 1.5 component to consume the directive.
(function (undefined) {
'use strict';
angular.module('myApp')
.component('doc', {
template: '...
<input type="file" id="newFile" eg-files="model.files" has-file="model.hasFile" />
....',
bindings: {
id: '<',
"$router": '<'
},
controllerAs: 'model',
controller: [controller]
});
function controller() {
var model = this;
model.$routerOnActivate = function (next, previous) {
model.id = next.params.id;
}
model.hasFile = false;
model.uploading = false;
}
})();
I set the break point in the function bound to change in directive egFiles. However the break point is never hit when I select a file by click the button?
I am trying to write a unit test for the toggleDetails function defined inside the following AngularJS directive:
angular.module('hadoopApp.cluster.cluster-directive', [])
.directive('cluster', [function() {
return {
templateUrl:'components/cluster/cluster.html',
restrict: 'E',
replace: true,
scope: {
clusterData: '=',
showDetails: '='
},
link: function(scope, element, attrs) {
scope.toggleDetails = function() {
console.log('Test');
scope.showDetails = !scope.showDetails;
};
},
// Default options
compile: function(tElement, tAttrs){
if (!tAttrs.showDetails) { tAttrs.showDetails = 'false'; }
}
};
}]);
And this is the unit test:
'use strict';
describe('hadoopApp.cluster module', function() {
// Given
beforeEach(module('hadoopApp.cluster.cluster-directive'));
var compile, mockBackend, rootScope;
beforeEach(inject(function($compile, $httpBackend, $rootScope) {
compile = $compile;
mockBackend = $httpBackend;
rootScope = $rootScope;
}));
var dummyCluster;
beforeEach(function() {
dummyCluster = {
id:"189",
name:"hadoop-189",
exitStatus:0
};
mockBackend.expectGET('components/cluster/cluster.html').respond(
'<div><div ng-bind="clusterData.name"></div></div>');
});
it('should toggle cluster details info', function() {
var scope = rootScope.$new();
scope.clusterData = dummyCluster;
// When
var element = compile('<cluster' +
' cluster-data="clusterData" />')(scope);
scope.$digest();
mockBackend.flush();
// Then
var compiledElementScope = element.isolateScope();
expect(compiledElementScope.showDetails).toEqual(false);
// When
console.log(compiledElementScope);
compiledElementScope.toggleDetails();
// Then
expect(compiledElementScope.showDetails).toEqual(true);
});
afterEach(function() {
mockBackend.verifyNoOutstandingExpectation();
mockBackend.verifyNoOutstandingRequest();
});
});
The test fails when calling compiledElementScope.toggleDetails() because the toggleDetails function is undefined:
TypeError: undefined is not a function
Printing the content of the isolated scope inside compiledElementScope I can see that in fact the function is not included in the object.
So, it looks like the toggleDetails function is not included in the isolated scope but I don't know why.
If you use the compile function within a directive, the link function is ignored. You should return the function within the compile method:
compile: function (tElement, tAttrs) {
if (!tAttrs.showDetails) {
tAttrs.showDetails = 'false';
}
return {
post: function (scope, element, attrs) {
console.log('Test');
scope.toggleDetails = function () {
console.log('Test');
scope.showDetails = !scope.showDetails;
};
}
};
}
Also, in order to make the test work, you should add:
scope.showDetails = false;
And the binding to the directive (because you require two values):
var element = compile('<cluster' +
' cluster-data="clusterData" show-details="showDetails" />')(scope);
Jsfiddle: http://jsfiddle.net/phu7sboz/
My code is as simple:
.controller('Ctrl', ['$scope', '$timeout', function ($scope, $timeout) {
$timeout(function () {
$scope.x = 5;
}, 2000);
}])
.directive('ngHey', ['$parse', function ($parse) {
return {
'restrict': 'A',
'scope': true,
'link': function($scope, el, atr) {
var go = function () {
if ($parse(atr.ngHey)()) {
alert('oiiiiiii');
}
};
atr.$observe('ngHey', function (val) {
if (val) {
go();
}
});
}
};
}]);
//view.html
<div ng-controller="Ctrl">
<span ng-hey="x > 3"></span>
</div>
I would like to be able to fire when directive expression changes and when it's true or false, but at the moment the alert never happen...
It works only if i do something like:
<div ng-controller="Ctrl">
<span ng-hey="{{x > 3}}"></span>
</div>
which is not what i would like, i would like the directive to execute expressions as for ng-if or ng-hide etc...
Any tip or help appreciated,
thanks
You can't use $observe in this case, as it Observes an interpolated attribute. (documentation). In this case you can use $watch on the scope like this:
.directive('ngHey', ['$parse',
function($parse) {
return {
scope: true,
link: function($scope, el, atr) {
var go = function(value) {
if (value) {
alert('oiiiiiii');
}
};
$scope.$watch(atr.ngHey, function(val) {
if (val) {
go(val);
}
});
}
};
}
]);
Demo: http://plnkr.co/edit/XakjA2I7lpJdo9YAZFEH?p=preview
UPD. Based on the OP's comments, updated directive looks like:
.directive('ngHey', ['$parse',
function($parse) {
return {
scope:{ngHey: '='},
link: function($scope, el, atr) {
var go = function(value) {
if ($scope.ngHey) {
alert('oiiiiiii');
}
};
$scope.$watch('ngHey', function(val) {
if (val) {
go();
}
});
}
};
}
]);
Note, how you can use $scope.ngHey in this case, not need to $eval attribute.
Demo: http://plnkr.co/edit/XakjA2I7lpJdo9YAZFEH?p=preview
JSFIDDLE DEMO
As the $timeout is setting the value of x later, the condition inside the directive checking for attribute was returning false always. So use $watch to check the condition in go() whenever x changes.
var myApp = angular.module('myApp',[]);
myApp.directive('ngHey', function () {
return {
'restrict': 'A',
'scope': true,
'link': function($scope, el, attr) {
var go = function () {
if ($scope.$eval(attr.ngHey)) {
alert('oiiiiiii');
}
};
$scope.$watch('x', function (val) {
if (val) {
go();
}
});
}
};
});
Instead of $parse use $scope.$eval and also similarly instead of $observe use $watch.
Hi I have a variable on my scope named loadingdata. It will have the values true or false to determine if data is loading or not. I would like to put an attribute on an element to disable it if data is loading. Here is the code I already have but it is not working:
module.directive('disableWhenLoadingData', function () {
return {
restrict: 'A',
scope: {},
link: function ($scope, element, attrs) {
$scope.$watch('loadingData', function(newValue, oldValue) {
element.attr('disabled', newValue);
});
}
};
});
any ideas
You can use Angular's own ngDisabled directive instead of writing your own.
Service:
module.factory('GetDataService', function ($http) {
return {
getCustomers: function() {
return $http({ url: '/someurl', method: 'GET'});
}
}
});
Directive:
module.directive('disableWhenLoadingData', function (GetDataService) {
return {
restrict: 'A',
scope: {},
link: function ($scope, element, attrs) {
$scope.loadingData = true;
GetDataService.getCustomers().success(function (data) {
$scope.loadingData = false;
});
}
};
});
Generally I set $scope.loading in my controller, and my button or whatever i set ng-disabled.
In my controller:
$scope.loadData = function () {
$scope.loading = true;
$http
.get('url')
.success(function (ret) {
$scope.loading = false;
});
}
In my view:
<button ng-disabled="loading" ng-click="loadData()">{{loading? 'loading Data' : 'Submit'}}</button>