I want to have a call back function on a directive I wrote, without creating an isolated scope. The directive simply makes the element resizeable, and thus doesn't need a separate scope (and it's desirable NOT to have isolated scope in this case, I think).
What is the best way to do this? I tried,
.controller("MyController", function ($scope) {
$scope.myFunc = function () {
console.log("test");
};
})
.directive('resizeable', function ($document, $parse) {
return function (scope, element, attr) {
var func = $parse(attr.onFunc);
}
}
with
<div class="main" resizeable="" on-Func="myFunc()">
How to do this?
Try this JSFiddle.
You're close, here is what I'd do:
.directive('resizeable', ['$document', '$parse', function ($document, $parse) {
return {
link: function ($scope, $element, $attrs) {
var func = $parse($attrs.resizeable);
// do stuff...
func($scope);
}
};
}]);
And the HTML would be:
<div data-resizeable="myFunc()"></div>
Here is a working demo: http://plnkr.co/edit/as6Tylj0zPpjVsUaDDC6?p=preview
on-Func="myFunc"
Without parenthesis. And in the directive:
var func = scope[attr.onFunc];
The directive is in the same scope as the controller. So you can directly refernece the function.
Related
I'm trying to figure out AngularJS directives. I've got the following JSFiddle with an example of something I'm trying to do. https://jsfiddle.net/7smor9o4/
As you can see in the example, I expect the vm.alsoId variable to be equal to vm.theId. In the template vm.theId displays the correct value but vm.alsoId does not.
What am I doing wrong? How could I accomplish my goal.
If it helps the final idea is to execute something as the following:
function directive(service) {
var vm = this;
vm.entity = null;
init();
function init() {
service.getEntity(vm.theId).then(function (entity) {
vm.entity = entity;
});
}
}
As you've noticed, the bindToController bindings are not immediately available in the controller's constructor function (unlike $scope, which are). What you're looking for is a feature introduced with Angular 1.5: Lifecycle Hooks, and specifically $onInit.
You had the right idea; simply replace your init function definition and invocation as follows:
vm.$onInit = function () {
service.getEntity(vm.theId).then(function (entity) {
vm.entity = entity;
});
};
And here is your updated fiddle.
(Alternatively, without this solution, you'd have needed a watch.)
Angular recommends that you bind a controller "only when you want to expose an API to other directives. Otherwise use link."
Here's a working fiddle using the link function.
angular.module('app', [])
.directive('directive', directive);
angular.element(function() {
angular.bootstrap(document, ['app']);
});
function directive() {
return {
restrict: 'E',
scope: {
theId: '<'
},
template: `
alsoId: <span ng-bind="alsoId"></span>
theId: <span ng-bind="theId"></span>`,
link: link
};
}
function link(scope, element, attrs) {
init();
function init() {
scope.alsoId = scope.theId;
}
}
Im learning about Angular directives and I can't wrap my head around the scope topic. Suppose I have this custom directive which is named parentDirective. It has a controller property and a link property, as follows:
angular.module("app").directive("parentDirective", function () {
return {
restrict: "E",
templateUrl: "dirs/parent.html",
scope:{
character: "="
},
controller: function ($scope) {
$scope.getData = function (data) {
console.log(data);
}
},
link: function (scope,elem, attrs) {
elem.bind("click", function (e) {
//get object here?
});
scope.getData = function (data) {
console.log(data);
}
}
}
});
Its template is defined as follows:
<p ng-click="getData(character)">
{{character.name}}
</p>
I can get the character object in the controller function through the $scope variable and I have access to the same data in the link function through scope. Whats the difference between the two methods in this regard? Second question, Is it possible to bind a click to the directive and get the object like this:
elem.bind("click", function (e) {
//get object here?
});
Scope is specific to current directive instance and is the same object in both functions.
For defining methods on the scope, there's no difference if they are defined in controller or link function, unless there is race condition that requires the method to be defined as early as possible. For this reason it makes sense to define scope methods in controller.
Event handler doesn't differ from any other function, it is
elem.on("click", function (e) {
scope.$apply(function () {
scope.character...
});
});
scope.$apply(...) wrapper doesn't hurt anyway, but the necessity of it depends on what happens with scope.character.
The directive can have only controller and no link. Current Angular versions (1.5+) suggest the style where bindToController + controllerAs are used instead of scope bindings as common ground for directives and components.
Then the directive may look like
restrict: "E",
template: '<p>{{$ctrl.character.name}}</p>',
controllerAs: '$ctrl',
bindToController: { character: "=" },
controller: function ($element, $scope) {
var self = this;
self.getData = function (data) { ... };
$element.on("click", function (e) {
scope.$apply(function () {
self.character...
});
});
}
link function may appear as $postLink controller hook, but here it is not needed.
I have a page with two directives. I need to invoke a function in one directive from the other. I have added the function to the $element object of the first directive and used jQuery to invoke it from the other. Is this the right approach or should I be using a context object shared by both directives?
//inside directive 1 link fn
$element[0].foo = function(){
console.log("test");
}
...
//inside directive 2 link fn
$('.className').foo()
The two directives are elements on a page with a shared controller. Each has an isolated scope. This seems to work well. Are there any reasons why I should not do this?
are the 2 directives sharing the same controller? If so, you could call a function on the controller from one directive which would "notify" the other. Or, you could use events, check this answer here
This is not the way you should do it. Try to avoid using $element, it does DOM manipulations which are slow and Angular takes care of by itself. To invoke a function in directiveB, triggered from directiveA, you better make a service.
angular.service('Communication', function () {
var _listeners = [];
return {
addListener: function (callback) {
_listeners.push(callback);
},
removeListener: function () {/* Clean up */},
invokeListeners: function (data) {
_listeners.forEach(function (listener) {
listener(data);
});
}
}
});
angular.directive('directiveB', ['Communication', function (Communication) {
return {
restrict: 'AE',
scope: {},
controller: directiveB
};
function directiveB (scope, element, attrs) {
Communication.addEventListener(function (data) { /* Do stuff with data */ });
}
}]);
angular.directive('directiveA', ['Communication', function (Communication) {
return {
restrict: 'AE',
scope: {},
controller: directiveA
};
function directiveA (scope, element, attrs) {
// trigger event in directive B
Communication.invokeListeners('test');
}
}]);
i have a function which i call more than once ( in different directives).
So is there a way to call a function in every directive?
One solution would be to make the function as a service.
But the service does need a return value, doesn´t it?
this.changeDesign = function (currentstep) {
//do something
};
this method I call many times.
You should consider to avoid using global variables or put something on the global scope. Maybe it works now, but if your project gets bigger, you will probably get a lot of problems. More infos for example: https://gist.github.com/hallettj/64478
Here is an example, how you can use a factory, which you can inject into your directives (coding style inspired by John Papa https://github.com/johnpapa/angular-styleguide#factories):
(function () {
"use strict";
angular.module('my-app').provider('MyFactory', MyFactory);
MyFactory.$inject = [];
function MyFactory() {
var service = {
changeDesign: myChangeDesignImpl
};
function myChangeDesignImpl() { }
this.$get = function() {
return service ;
};
}
})();
You can now inject your service into a directive like this:
(function () {
"use strict";
angular.module('my-app').directive('MyDirective', MyDirective);
MyDirective.$inject = ["MyFactory"];
function MyDirective(MyFactory) {
var directive = {
restrict: 'E',
template: "/template.html",
link: link
};
return directive;
function link(scope, el, attr) {
MyFactory.changeDesign();
}
}
})();
you can do it in a directive that you will delcare on your elements.
angular.module('myDesignModule', [])
.directive('myDesignDirective', function() {
return {
link: function(scope, element, attrs, ctrl){
element.addClass("myClass");
}
};)
I'm quite new to Angular, so I'm pretty sure there's something I'm missing here.
I just read an article about isolated scopes, with expressions in particular using & symbol.
This is the article, which includes a fiddle.
I'm trying to do something more simple, I want to use also a function but don´t want to use a button to do the calling, I'd like to directly call the function. I reduced the code in the fiddle to something similar to what I want but it has no effect, the function is not being called, or it is but is not doing what I expect.
If I uncomment line #12 I 'Hello' is displayed, and if comment then I expected 'as' to be displayed in the textbox, but instead it's cleared. What am I missing here?
Thanks,
PS. I can´t seem to make the snipped work, but the fiddle works fine.
var myModule = angular.module('myModule', [])
.directive('myComponent', function () {
return {
restrict: 'E',
scope: {
isolatedExpressionFoo: '&'
}
};
})
.controller('MyCtrl', ['$scope', function ($scope) {
$scope.foo = 'Hello!';
//$scope.isolatedExpressionFoo();
$scope.updateFoo = function () {
$scope.foo = 'as';
}
}]);
<div ng-controller="MyCtrl">
<input ng-model="foo">
<my-component isolated-expression-foo="updateFoo()" />
</div>
The $scope from the controller and the directive are two different ones. The controller $scope has no access to the isolatedExpressionFoo function.
To instantly call the function provided to your directive (from within the directive) you will need to implement a controller for said directive.
E.g.
var myModule = angular.module('myModule', [])
.directive('myComponent', function () {
return {
restrict:'E',
scope:{
isolatedExpressionFoo:'&'
},
controller: function($scope){
$scope.isolatedExpressionFoo();
}
};
})
.controller('MyCtrl', ['$scope', function ($scope) {
$scope.foo = 'Hello!';
$scope.updateFoo = function () {
$scope.foo = 'as';
}
}]);