Cannot pass directive scope values to 3rd party directive attributes. (text-angular) - angularjs

Basically, I have an input block, that uses Text-Angular* and I needed to wrap it into its own parent directive for easier manual configuration. I am trying to pass values into my directive's scope attributes, down to Text-Angular's directive, but it won't pickup what it needs in its own custom attributes, from the parent one. (it's for synchronization and saving its own state with the entered input from the user) I simply need to bridge the communication between parent and children elements that are separate from one another, while being able to communicate with Text-Angular.
(the third party is named Text-Angular and is linked via CDN)
This is the way I tried things. The problem mainly concerns ng-model.
HTML
mainfile.html
<div class="col-sm-12 p-l-0 form-group">
<label for="value" translate="text_language"></label>
<text-angular-wrap ng-model="vm.value.info" id="value" name="value"></text-angular-wrap>
</div>
template-file
It is wrapped in its own controller file.
<div ng-app="textAngularApp" ng-controller="textAngularEditor">
<text-angular ta-toolbar="[['h1','h2','h3'],['bold','italics','underline'],['ul','ol'],['indent','outdent']]" ng-model="ngModel" id="{{id}}" name="{{name}}">
</text-angular>
<pre>name: {{name}}</pre>
<pre>id: {{id}}</pre>
<pre>ng-model: {{ngModel}}</pre>
</div>
textAngularWrap.directive.js
(function () {
'use strict';
angular.module("file.UI")
.directive("textAngularWrap", function() {
return {
restrict: "AE",
scope: {
name: "#",
id: "#",
ngModel: "=",
},
templateUrl: helper.directive('TextAngularTemplate'),
require: "ngModel",
link: function($scope, elem, attr, ctrl) {
console.log($scope, elem, attr, ctrl);
$scope.name = attr.name,
$scope.id = attr.id;
$scope.ngModel = attr.ngModel;
}
}
});
})();
textAngular.js
(function () {
'use strict';
var textAngular = angular.module("textAngularApp", ['textAngular']);
textAngular.config(['$provide', function ($provide) {
$provide.decorator('taOptions', ['$delegate', function (taOptions) {
taOptions.defaultTagAttributes.a.target = '_blank';
taOptions.defaultTagAttributes.a.content = '';
return taOptions;
}]);
}]);
textAngular.controller('textAngularEditor', ['$scope', 'textAngularManager',
function textAngularEditor($scope, textAngularManager) {
$scope.htmlcontent = null;
$scope.disabled = false;
}]);
})();
I tried everything I could find, to trying to pass it down to the controller file, to trying to pass it in diverse ways with the directive, to play around with parent and children linking and nothing works.
ng-model in text-angular simply won't pickup whatever I pass down from the parent directive. It stays the same as "ngModel" in the inspector.
I'm out of ideas. How do I pass stuff down to a directive I cannot access?

Related

Require a directive's controller that is nested in a parent directive, AngularJS

I'm wondering if there is a way to require the controller of a directive that exists/is nested somewhere as a common parent's child directive in AngularJS.
Directive Structure
Suppose I have the following structure for my directives:
<parent-directive>
<ul>
<li some-nested-directive ng-repeat="dir in directives"></li>
</ul>
<settings-menu></settings-menu>
</parent-directive>
Directive Definition
/*
* some-nested-directive directive definition
*/
.directive('someNestedDirective', function(){
// ...
return {
restrict: 'A',
controller: someNestedDirectiveController
};
});
/*
* settings-menu directive definition
*/
.directive('settingsMenu', function(){
return {
restrict: 'AE',
require: [], // how to require the nested-directive controller here?
link: function(scope, iElement, attrs, ctrls){
// ...
}
};
})
I've already checked out this SO question which states how to require controllers of directives that exist along the same line in a hierarchy.
But my question is regarding a way to do the same in a hierarchy of directives that NOT necessarily exist along the same line. And if this is not possible, what is a proper workaround for it. Any help would be appreciated.
EDIT
Also, can any of the prefixes for require (or a combination of them) be used to achieve the same?
One approach is to use parent directive as a way to pass references between controllers:
var mod = angular.module('test', []);
mod.directive('parent', function() {
return {
restrict: 'E',
transclude: true,
template: '<div>Parent <div ng-transclude=""></div></div>',
controller: function ParentCtrl() {}
}
});
mod.directive('dirA', function() {
return {
restrict: 'E',
template: '<div>Dir A <input type="text" ng-model="name"></div>',
require: ['dirA', '^^parent'],
link: function(scope, element, attrs, ctrls) {
//here we store this directive controller into parent directive controller instance
ctrls[1].dirA = ctrls[0];
},
controller: function DirACtrl($scope) {
$scope.name = 'Dir A Name';
this.name = function() {
return $scope.name;
};
}
}
});
mod.directive('dirB', function() {
return {
restrict: 'E',
template: '<div>Dir A <button ng-click="click()">Click</button></div>',
require: ['dirB', '^^parent'],
link: function(scope, element, attrs, ctrls) {
//let's assign parent controller instance to this directive controller instance
ctrls[0].parent = ctrls[1];
},
controller: function DirBCtrl($scope) {
var ctrl = this;
$scope.click = function() {
//access dirA controller through parent
alert(ctrl.parent.dirA.name());
};
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app='test'>
<parent>
<dir-a></dir-a>
<dir-b></dir-b>
</parent>
</div>
When using above approach it also makes sense to encapsulate how the dirA controller is stored inside parent controller i.e. by using a getter property or by exposing only the required properties of dirA controller.
I aggree with miensol's reply and I recommend that approach but in some cases you may need something like that;
<parent-directive>
<ul>
<some-nested-directive id="snd1" ng-repeat="dir in directives"></some-nested-directive>
</ul>
<settings-menu some-nested-directive-id="snd1"></settings-menu>
You can access the scope of some-nested-directive using its id from the settings-menu;
$("#" + scope.someNestedDirectiveId).scope()
Once I used this approach to cascade the values of a dropdown according to the choise of another independent dropdown.

Unable to pass controller and directive scope into child directive using require

I am trying to pass the controller scope of parent controller and parent directive into a child directive but facing an error saying that the controller is not available. Here is a plunk for that
http://plnkr.co/edit/aahgOK9oFFjcP2y5VkVa?p=preview
HTML:
<div ng-controller="MainCtrl as mc">
<new-dir>
<data-customer-details customer="mc.customers[0]" logger="mc.logger()" version-age="{{mc.age}}"></data-customer-details>
</new-dir>
</div>
OK, so I tinkered with your plunker a bit. I couldn't get it working using Controller As...I had to change it over to $scope injection on the main controller. Then I created a new scope on newDir by setting scope: true.
You don't actually need to require the MainCtrl because these directives are automatically children of that scope anyway.
I changed your 'MainCtrl' to this:
angular.module('plunker').controller('MainCtrl', function ($scope) {
$scope.name = 'World';
$scope.customers = [{
"name": "angularjs 1.4",
"version": "1.4"
}, {
"name": "angularjs 1.3",
"version": "1.3"
}, {
"name": "angularjs 1.2",
"version": "1.2"
}];
$scope.age = 30;
$scope.logger = function() {
console.log('clicked');
}
$scope.ctrlScopeVariable = 'im in controller scope';
})
Minor change to newDir:
function newDir (){
return {
scope: true, //you need this
controller: function($scope){
$scope.val= 'someval';
console.log($scope.$parent.ctrlScopeVariable)
},
link: function(scope, el, attr, ctrl) {
console.log(scope.$parent.name)
}
}
}
And the last directive:
function CustomerDetails() {
var directive = {
scope: {
customer: '=',
logger: '&',
myNewAge: '#versionAge'
},
restrict: 'EA',
require: ['^newDir'],
controllerAs: 'cd',
templateUrl: 'customer-details.html',
link: linkFunction,
controller: function($scope){
console.log($scope.$parent.$parent.ctrlScopeVariable);
var cd = this;
cd.newval = 'new val';
}
};
function linkFunction(scope, elem, attributes, controllers, transclude) {
console.dir(controllers);
scope.fromMainCtrl = scope.$parent.$parent.ctrlScopeVariable
}
return directive;
}
The Plunker
I added a binding to the customer details template that passes in the $scope.ctrlScopeVariable from the main controller, so you can see the MainCtrl scope is accessible form the child directive.
In regards to require, the relevant documentation is here, I think:
If it is necessary to reference the controller or any functions bound
to the controller's scope in the template, you can use the option
controllerAs to specify the name of the controller as an alias. The
directive needs to define a scope for this configuration to be used.
This is particularly useful in the case when the directive is used as
a component.
Looking back at myPane's definition, notice the last argument in its
link function: tabsCtrl. When a directive requires a controller, it
receives that controller as the fourth argument of its link function.
Taking advantage of this, myPane can call the addPane function of
myTabs.
Essentially, you can use it to reference a parent controller on which you need to access some functions or something. Notably, it becomes available under whatever alias you give it as the fourth argument of your link function.
EDIT:
In this Plunker I added a function to the controller of newDir, required newDir in the CustomerDetail directive, and then called that function in the CustomerDetail link function:
CustomerDetails directive:
//some stuff
require: '^newDir',
//some stuff
link: function(scope, el, attr, newDirCtrl) {
console.log(newDirCtrl.doubleNum(100));
}
newDir controller:
controller: function($scope){
this.doubleNum = function(num) {
return num*2
}
// some stuff
}
First you need to declare a variable as callback function:
var MainCtrlFn = function() { .... }
Then, you can set it as parameter to angularJS:
angular.module('plunker').controller('MainCtrl', MainCtrlFn);

AngularJS : Directive Isolated Scoping

I'm developing a AngularJS Directive that is a table. The parent most control ng-table needs to have a isolated scope for its options and model.
However, the children should 'inherit' that scope so that you don't have to pass tons of options to a very interconnected group of components.
For example:
<div ng-table="tableOptions" ng-model="results">
<div ng-table-header>
<div ng-table-header-column="column"
ng-repeat="column in tableOptions.columns">
{{column.name}}
</div>
</div>
<div ng-table-body>
<div ng-table-row="row"
ng-repeat="row in $results">
<div ng-table-cell="column"
ng-repeat="column in tableOptions.columns">
{{row[column.id]}}
</div>
</div>
</div>
<div ng-table-footer></div>
</div>
In the above example, ng-table-header, ng-header-column, etc all need to access properties from the parent control ng-table. Furthermore, all the directives replace and transclude.
I know I could broadcast events to the children/parent directives but is there a better way to restrict scope at the parent level and auto pass it to the children?
Oh dude, you have forced me to look how forms and inputs directives are implemented in angular.
So, angularjs has similar thing to what you want to implement - ng-form directive with ng-input.
When you use form or ng-form directive, it creates controller(directives can do it) and transclude html-code will inherit it. ng-input(input) asks for it in require option.
require: ['ngModel', '^?form', '^?ngModelOptions'],
So you get this controller in link function. So... you can call functions, do things, you know.
You can do it like this(just an idea, this is how it works):
.directive('coolTable', function() {
return {
...
controller: function($scope) {
$scope.columns = []; /* Here you will have all table columns, so you can send it's data to service and do any work */
$scope.registerColumn = function(column) {
$scope.columns.push(column);
};
},
...
};
})
.directive('column', function() {
return {
...
require: '^?coolTable',
...
link: function(scope, element, attrs, coolTable) {
coolTable.registerColumn(this);
},
};
})
<coolTable>
<column></column>
<column></column>
<column></column>
</coolTable>
I was able to find a work around looking at angular-ui code that created their own transclude directive to apply scope.
so my master table directive starts off like:
module.directive('ngTable', function ($rootScope, $timeout, $window, $compile) {
return {
restrict: 'AE',
transclude: true, // NOTE THIS
replace: true, // NOTE THIS
templateUrl: 'common/components/table/views/table.tpl.html',
scope: {
ngModel: "=",
tableOptions: "=ngTable"
},
controller: 'ngTableCtrl',
link: function ($scope, $element, $attributes, controller, ngModel) {
....
}
}
});
then I create my own transclude directive like:
module.directive('tableTransclude', function () {
return {
link: function ($scope, $element, $attrs, controller, $transclude) {
$transclude($scope, function (clone) {
$element.empty();
$element.append(clone);
});
}
};
})
and the usage in the template of table looks like:
<div table-transclude />
And presto!!! A little hacky but gets the job done. I understand why Angular did this but in some cases you need this type of scoping.

In an AngularJs directive, how do I call its controller's function contained within an directive element's attribute?

I am trying to call a function in a controller, which is part of a custom angular directive, following is the code,
Method 1: (Doesn't work: Controller's function doesn't write to the console)
HTML:
<div ng-app="MyApp">
<my-directive callback-fn="ctrlFn(arg1)"></my-directive>
</div>
JS:
var app = angular.module('MyApp', []);
app.directive('myDirective', function() {
return {
restrict: 'E',
scope: { someCtrlFn: '&callbackFn' },
link: function(scope, element, attrs) {
scope.someCtrlFn({arg1: 22});
},
controller: function ($scope) {
$scope.ctrlFn = function(test) {
console.log(test);
}
}
}
});
When I remove the directive's controller from it and and create a new controller it works,
Method 2: (Works: Controller's function does write to the console)
HTML:
<div ng-app="MyApp" ng-controller="Ctrl">
<my-directive callback-fn="ctrlFn(arg1)"></my-directive>
</div>
JS:
var app = angular.module('MyApp', []);
app.directive('myDirective', function() {
return {
restrict: 'E',
scope: { someCtrlFn: '&callbackFn' },
link: function(scope, element, attrs) {
scope.someCtrlFn({arg1: 22});
}
}
});
app.controller('Ctrl', function ($scope) {
$scope.ctrlFn = function(test) {
console.log(test);
}
});
I would like know how to get the behavior of Method 2 in Method 1 i.e., to be able to call the directive's controller's function from directive's attribute.
Any help is greatly appreciated, Thank you!
In Method 1, you are creating an isolated scope and defining a scope value someCtrlFn that takes in a function from the parent scope that is using your directive. The function to use is specified by the attribute callbackFn.
The way directives work with these scope items is that they are expected to be assigned from things that are on the parent scope that is active when the directive is used. So, if you have a controller Ctrl as in your Method 2, then use the directive within that scope, your directive is trying to match the what you defined in the attribute to what is available on Ctrl's scope.
So, in your first example, it's looking for a function called ctrlFn on the parent scope, but there isn't one. It will not try to look for it on the directive's controller. This is why Method 2 works, because there is a parent scope where ctrlFn is defined, and the directive is able to properly invoke that expression.
The purpose of these scope attributes is to allow directives to bind to values or functions on a parent scope to facilitate communication. For example, to give the directive data that it will display or modify, or allow the parent to define a function the directive can invoke for a callback during an event or what have you. The parent scope cannot move into the directive's scope and force the directive's scope to use its own defined items (unless you set it up so your directive uses a default value or function if the attribute is omitted or whatever).
They are not used so a directive can define things on its scope that it uses internally. If these things are internal to the directive, you can simply add them to the scope during link or whatever is suitable.
Did you mean something like this?
var app = angular.module('MyApp', []);
app.directive('myDirective', function() {
return {
restrict: 'E',
scope: { },
link: function(scope, element, attrs) {
// defines ctrlFn that can be used later by this directive's template or controller
scope.ctrlFn = function(test) {
console.log(test);
}
// immediately invokes ctrlFn to log a message, just here to illustrate
scope.ctrlFn('Hello World!');
}
}
});
Achieved it using $rootScope instead of $scope within the directive's controller
HTML:
<div ng-app="MyApp">
<my-directive callback-fn="ctrlFn(arg1)"></my-directive>
</div>
JS:
<script>
var app = angular.module('MyApp', []);
app.directive('myDirective', function($rootScope) {
return {
restrict: 'E',
scope: { someCtrlFn: '&callbackFn' },
link: function(scope, element, attrs) {
scope.someCtrlFn({arg1: 22});
},
controller: function ($scope) {
$rootScope.ctrlFn = function(test) {
console.log(test);
}
}
}
});
</script>

How to call a method defined in an AngularJS directive?

I have a directive, here is the code :
.directive('map', function() {
return {
restrict: 'E',
replace: true,
template: '<div></div>',
link: function($scope, element, attrs) {
var center = new google.maps.LatLng(50.1, 14.4);
$scope.map_options = {
zoom: 14,
center: center,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
// create map
var map = new google.maps.Map(document.getElementById(attrs.id), $scope.map_options);
var dirService= new google.maps.DirectionsService();
var dirRenderer= new google.maps.DirectionsRenderer()
var showDirections = function(dirResult, dirStatus) {
if (dirStatus != google.maps.DirectionsStatus.OK) {
alert('Directions failed: ' + dirStatus);
return;
}
// Show directions
dirRenderer.setMap(map);
//$scope.dirRenderer.setPanel(Demo.dirContainer);
dirRenderer.setDirections(dirResult);
};
// Watch
var updateMap = function(){
dirService.route($scope.dirRequest, showDirections);
};
$scope.$watch('dirRequest.origin', updateMap);
google.maps.event.addListener(map, 'zoom_changed', function() {
$scope.map_options.zoom = map.getZoom();
});
dirService.route($scope.dirRequest, showDirections);
}
}
})
I would like to call updateMap() on a user action. The action button is not on the directive.
What is the best way to call updateMap() from a controller?
If you want to use isolated scopes you can pass a control object using bi-directional binding = of a variable from the controller scope. You can also control also several instances of the same directive on a page with the same control object.
angular.module('directiveControlDemo', [])
.controller('MainCtrl', function($scope) {
$scope.focusinControl = {};
})
.directive('focusin', function factory() {
return {
restrict: 'E',
replace: true,
template: '<div>A:{{internalControl}}</div>',
scope: {
control: '='
},
link: function(scope, element, attrs) {
scope.internalControl = scope.control || {};
scope.internalControl.takenTablets = 0;
scope.internalControl.takeTablet = function() {
scope.internalControl.takenTablets += 1;
}
}
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="directiveControlDemo">
<div ng-controller="MainCtrl">
<button ng-click="focusinControl.takeTablet()">Call directive function</button>
<p>
<b>In controller scope:</b>
{{focusinControl}}
</p>
<p>
<b>In directive scope:</b>
<focusin control="focusinControl"></focusin>
</p>
<p>
<b>Without control object:</b>
<focusin></focusin>
</p>
</div>
</div>
Assuming that the action button uses the same controller $scope as the directive, just define function updateMap on $scope inside the link function. Your controller can then call that function when the action button is clicked.
<div ng-controller="MyCtrl">
<map></map>
<button ng-click="updateMap()">call updateMap()</button>
</div>
app.directive('map', function() {
return {
restrict: 'E',
replace: true,
template: '<div></div>',
link: function($scope, element, attrs) {
$scope.updateMap = function() {
alert('inside updateMap()');
}
}
}
});
fiddle
As per #FlorianF's comment, if the directive uses an isolated scope, things are more complicated. Here's one way to make it work: add a set-fn attribute to the map directive which will register the directive function with the controller:
<map set-fn="setDirectiveFn(theDirFn)"></map>
<button ng-click="directiveFn()">call directive function</button>
scope: { setFn: '&' },
link: function(scope, element, attrs) {
scope.updateMap = function() {
alert('inside updateMap()');
}
scope.setFn({theDirFn: scope.updateMap});
}
function MyCtrl($scope) {
$scope.setDirectiveFn = function(directiveFn) {
$scope.directiveFn = directiveFn;
};
}
fiddle
Although it might be tempting to expose an object on the isolated scope of a directive to facilitate communicating with it, doing can lead to confusing "spaghetti" code, especially if you need to chain this communication through a couple levels (controller, to directive, to nested directive, etc.)
We originally went down this path but after some more research found that it made more sense and resulted in both more maintainable and readable code to expose events and properties that a directive will use for communication via a service then using $watch on that service's properties in the directive or any other controls that would need to react to those changes for communication.
This abstraction works very nicely with AngularJS's dependency injection framework as you can inject the service into any items that need to react to those events. If you look at the Angular.js file, you'll see that the directives in there also use services and $watch in this manner, they don't expose events over the isolated scope.
Lastly, in the case that you need to communicate between directives that are dependent on one another, I would recommend sharing a controller between those directives as the means of communication.
AngularJS's Wiki for Best Practices also mentions this:
Only use .$broadcast(), .$emit() and .$on() for atomic events
Events that are relevant globally across the entire app (such as a user authenticating or the app closing). If you want events specific to modules, services or widgets you should consider Services, Directive Controllers, or 3rd Party Libs
$scope.$watch() should replace the need for events
Injecting services and calling methods directly is also useful for direct communication
Directives are able to directly communicate with each other through directive-controllers
Building on Oliver's answer - you might not always need to access a directive's inner methods, and in those cases you probably don't want to have to create a blank object and add a control attr to the directive just to prevent it from throwing an error (cannot set property 'takeTablet' of undefined).
You also might want to use the method in other places within the directive.
I would add a check to make sure scope.control exists, and set methods to it in a similar fashion to the revealing module pattern
app.directive('focusin', function factory() {
return {
restrict: 'E',
replace: true,
template: '<div>A:{{control}}</div>',
scope: {
control: '='
},
link : function (scope, element, attrs) {
var takenTablets = 0;
var takeTablet = function() {
takenTablets += 1;
}
if (scope.control) {
scope.control = {
takeTablet: takeTablet
};
}
}
};
});
To be honest, I was not really convinced with any of the answers in this thread. So, here's are my solutions:
Directive Handler(Manager) Approach
This method is agnostic to whether the directive's $scope is a shared one or isolated one
A factory to register the directive instances
angular.module('myModule').factory('MyDirectiveHandler', function() {
var instance_map = {};
var service = {
registerDirective: registerDirective,
getDirective: getDirective,
deregisterDirective: deregisterDirective
};
return service;
function registerDirective(name, ctrl) {
instance_map[name] = ctrl;
}
function getDirective(name) {
return instance_map[name];
}
function deregisterDirective(name) {
instance_map[name] = null;
}
});
The directive code, I usually put all the logic that doesn't deal with DOM inside directive controller. And registering the controller instance inside our handler
angular.module('myModule').directive('myDirective', function(MyDirectiveHandler) {
var directive = {
link: link,
controller: controller
};
return directive;
function link() {
//link fn code
}
function controller($scope, $attrs) {
var name = $attrs.name;
this.updateMap = function() {
//some code
};
MyDirectiveHandler.registerDirective(name, this);
$scope.$on('destroy', function() {
MyDirectiveHandler.deregisterDirective(name);
});
}
})
template code
<div my-directive name="foo"></div>
Access the controller instance using the factory & run the publicly exposed methods
angular.module('myModule').controller('MyController', function(MyDirectiveHandler, $scope) {
$scope.someFn = function() {
MyDirectiveHandler.get('foo').updateMap();
};
});
Angular's approach
Taking a leaf out of angular's book on how they deal with
<form name="my_form"></form>
using $parse and registering controller on $parent scope. This technique doesn't work on isolated $scope directives.
angular.module('myModule').directive('myDirective', function($parse) {
var directive = {
link: link,
controller: controller,
scope: true
};
return directive;
function link() {
//link fn code
}
function controller($scope, $attrs) {
$parse($attrs.name).assign($scope.$parent, this);
this.updateMap = function() {
//some code
};
}
})
Access it inside controller using $scope.foo
angular.module('myModule').controller('MyController', function($scope) {
$scope.someFn = function() {
$scope.foo.updateMap();
};
});
A bit late, but this is a solution with the isolated scope and "events" to call a function in the directive. This solution is inspired by this SO post by satchmorun and adds a module and an API.
//Create module
var MapModule = angular.module('MapModule', []);
//Load dependency dynamically
angular.module('app').requires.push('MapModule');
Create an API to communicate with the directive. The addUpdateEvent adds an event to the event array and updateMap calls every event function.
MapModule.factory('MapApi', function () {
return {
events: [],
addUpdateEvent: function (func) {
this.events.push(func);
},
updateMap: function () {
this.events.forEach(function (func) {
func.call();
});
}
}
});
(Maybe you have to add functionality to remove event.)
In the directive set a reference to the MapAPI and add $scope.updateMap as an event when MapApi.updateMap is called.
app.directive('map', function () {
return {
restrict: 'E',
scope: {},
templateUrl: '....',
controller: function ($scope, $http, $attrs, MapApi) {
$scope.api = MapApi;
$scope.updateMap = function () {
//Update the map
};
//Add event
$scope.api.addUpdateEvent($scope.updateMap);
}
}
});
In the "main" controller add a reference to the MapApi and just call MapApi.updateMap() to update the map.
app.controller('mainController', function ($scope, MapApi) {
$scope.updateMapButtonClick = function() {
MapApi.updateMap();
};
}
You can specify a DOM attribute that can be used to allow the directive to define a function on the parent scope. The parent scope can then call this method like any other. Here's a plunker. And below is the relevant code.
clearfn is an attribute on the directive element into which the parent scope can pass a scope property which the directive can then set to a function that accomplish's the desired behavior.
<!DOCTYPE html>
<html ng-app="myapp">
<head>
<script data-require="angular.js#*" data-semver="1.3.0-beta.5" src="https://code.angularjs.org/1.3.0-beta.5/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<style>
my-box{
display:block;
border:solid 1px #aaa;
min-width:50px;
min-height:50px;
padding:.5em;
margin:1em;
outline:0px;
box-shadow:inset 0px 0px .4em #aaa;
}
</style>
</head>
<body ng-controller="mycontroller">
<h1>Call method on directive</h1>
<button ng-click="clear()">Clear</button>
<my-box clearfn="clear" contentEditable=true></my-box>
<script>
var app = angular.module('myapp', []);
app.controller('mycontroller', function($scope){
});
app.directive('myBox', function(){
return {
restrict: 'E',
scope: {
clearFn: '=clearfn'
},
template: '',
link: function(scope, element, attrs){
element.html('Hello World!');
scope.clearFn = function(){
element.html('');
};
}
}
});
</script>
</body>
</html>
Just use scope.$parent to associate function called to directive function
angular.module('myApp', [])
.controller('MyCtrl',['$scope',function($scope) {
}])
.directive('mydirective',function(){
function link(scope, el, attr){
//use scope.$parent to associate the function called to directive function
scope.$parent.myfunction = function directivefunction(parameter){
//do something
}
}
return {
link: link,
restrict: 'E'
};
});
in HTML
<div ng-controller="MyCtrl">
<mydirective></mydirective>
<button ng-click="myfunction(parameter)">call()</button>
</div>
You can tell the method name to directive to define which you want to call from controller but without isolate scope,
angular.module("app", [])
.directive("palyer", [
function() {
return {
restrict: "A",
template:'<div class="player"><span ng-bind="text"></span></div>',
link: function($scope, element, attr) {
if (attr.toPlay) {
$scope[attr.toPlay] = function(name) {
$scope.text = name + " playing...";
}
}
}
};
}
])
.controller("playerController", ["$scope",
function($scope) {
$scope.clickPlay = function() {
$scope.play('AR Song');
};
}
]);
.player{
border:1px solid;
padding: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app">
<div ng-controller="playerController">
<p>Click play button to play
<p>
<p palyer="" to-play="play"></p>
<button ng-click="clickPlay()">Play</button>
</div>
</div>
TESTED
Hope this helps someone.
My simple approach (Think tags as your original code)
<html>
<div ng-click="myfuncion">
<my-dir callfunction="myfunction">
</html>
<directive "my-dir">
callfunction:"=callfunction"
link : function(scope,element,attr) {
scope.callfunction = function() {
/// your code
}
}
</directive>
Maybe this is not the best choice, but you can do angular.element("#element").isolateScope() or $("#element").isolateScope() to access the scope and/or the controller of your directive.
How to get a directive's controller in a page controller:
write a custom directive to get the reference to the directive controller from the DOM element:
angular.module('myApp')
.directive('controller', controller);
controller.$inject = ['$parse'];
function controller($parse) {
var directive = {
restrict: 'A',
link: linkFunction
};
return directive;
function linkFunction(scope, el, attrs) {
var directiveName = attrs.$normalize(el.prop("tagName").toLowerCase());
var directiveController = el.controller(directiveName);
var model = $parse(attrs.controller);
model.assign(scope, directiveController);
}
}
use it in the page controller's html:
<my-directive controller="vm.myDirectiveController"></my-directive>
Use the directive controller in the page controller:
vm.myDirectiveController.callSomeMethod();
Note: the given solution works only for element directives' controllers (tag name is used to get the name of the wanted directive).
Below solution will be useful when, you are having controllers (both parent and directive (isolated)) in 'controller As' format
someone might find this useful,
directive :
var directive = {
link: link,
restrict: 'E',
replace: true,
scope: {
clearFilters: '='
},
templateUrl: "/temp.html",
bindToController: true,
controller: ProjectCustomAttributesController,
controllerAs: 'vmd'
};
return directive;
function link(scope, element, attrs) {
scope.vmd.clearFilters = scope.vmd.SetFitlersToDefaultValue;
}
}
directive Controller :
function DirectiveController($location, dbConnection, uiUtility) {
vmd.SetFitlersToDefaultValue = SetFitlersToDefaultValue;
function SetFitlersToDefaultValue() {
//your logic
}
}
html code :
<Test-directive clear-filters="vm.ClearFilters"></Test-directive>
<a class="pull-right" style="cursor: pointer" ng-click="vm.ClearFilters()"><u>Clear</u></a>
//this button is from parent controller which will call directive controller function

Resources