If I have two directives, and need to have one of them use the controller of the other one, what is the best way to accomplish something like that in angular? I'm trying to use the require: otherDirective stuff I see in the docs, but I'm getting an
Error: No controller: dirOne
from the following example. I was under the impression that by specifying dirOne in the require statement, dirTwo would have access to the controller of dirOne. What am I doing wrong here?
Here's a plunkr as well
var app = angular.module('myApp', []);
app.directive('dirOne', function() {
return {
controller: function($scope) {
$scope.options = {
"opt1" : true,
"opt2" : false,
"opt3" : false,
}
this.getOptions = function() {
return $scope.options;
};
}
};
});
app.directive('dirTwo',function() {
return {
require: 'dirOne',
link: function(scope, element, attrs, dirOneCtrl) {
$scope.options = dirOneCtrl.getOptions();
alert($scope.options);
}
};
});
http://plnkr.co/edit/vq7vvz
There were two problems with your plunkr:
In order for a directive to require the controller of another directive, the two directives have to be on the same element, or if you use the ^ notation, the required directive can be on a parent element.
<div dir-one dir-two></div>
Also, in the second directive you called your parameter "scope" but then tried to use it as "$scope".
link: function(scope, element, attrs, dirOneCtrl) {
scope.options = dirOneCtrl.getOptions();
alert(scope.options);
}
Related
HTML :
<div id="idOfDiv" ng-show="ngShowName">
Hello
</div>
I would like to call the function which is declared in my controller from my directive.
How can I do this? I don't receive an error when I call the function but nothing appears.
This is my directive and controller :
var d3DemoApp = angular.module('d3DemoApp', []);
d3DemoApp.controller('mainController', function AppCtrl ($scope,$http, dataService,userService,meanService,multipartForm) {
$scope.testFunc = function(){
$scope.ngShowName = true;
}
});
d3DemoApp.directive('directiveName', [function($scope) {
return {
restrict: 'EA',
transclude: true,
scope: {
testFunc : '&'
},
link: function(scope) {
node.on("click", click);
function click(d) {
scope.$apply(function () {
scope.testFunc();
});
}
};
}]);
You shouldn't really be using controllers and directives. Angularjs is meant to be used as more of a component(directive) based structure and controllers are more page centric. However if you are going to be doing it this way, there are two ways you can go about it.
First Accessing $parent:
If your directive is inside the controllers scope you can access it using scope.$parent.mainController.testFunc();
Second (Preferred Way):
Create a service factory and store your function in there.
d3DemoApp.factory('clickFactory', [..., function(...) {
var service = {}
service.testFunc = function(...) {
//do something
}
return service;
}]);
d3DemoApp.directive('directiveName', ['clickFactory', function(clickFactory) {
return {
restrict: 'EA',
transclude: true,
link: function(scope, elem) {
elem.on("click", click);
function click(d) {
scope.$apply(function () {
clickFactory.testFunc();
});
}
};
}]);
Just a tip, any time you are using a directive you don't need to add $scope to the top of it. scope and scope.$parent is all you really need, you will always have the scope context. Also if you declare scope :{} in your directive you isolate the scope from the rest of the scope, which is fine but if your just starting out could make things quite a bit more difficult for you.
In your link function you are using node, which doesn't exist. Instead you must use element which is the second parameter to link.
link: function(scope, element) {
element.on("click", click);
function click(d) {
scope.$apply(function() {
scope.testFunc();
});
}
I have created two directives and inserted the first directive into the second one. The content of template attribute works fine but the scope variable of the controller is not recognized. Please provide me solution on this
sample link: http://jsbin.com/zugeginihe/2/
You didn't provide the attribute for the second directive.
HTML
<div second-dir first-dir-scope="content">
<div first-dir first-dir-scope="content"></div>
</div>
Link demo: http://jsbin.com/jotagiwolu/2/edit
The best option would using parent directive, We could take use of require option of directive like require: '?secondDir' in firstDir
Code
var myApp = angular.module("myApp", []);
myApp.controller("myController", function($scope) {
$scope.content = "test1";
});
myApp.directive("firstDir", function() {
return {
restrict: "AE",
require: '?secondDir',
scope: {
firstDirScope: "="
},
template: "<div>first content</div>",
link: function(scope, element, attrs, secondDir) {
console.log(scope.firstDirScope, 'first');
}
};
});
myApp.directive("secondDir", function() {
return {
restrict: "AE",
scope: {
firstDirScope: "="
},
controller: function($scope) {
console.log($scope.firstDirScope, 'second');
}
};
});
Working JSBin
Below is the only way i could figure out how to get a directive to pull out an attribute from its origin element, get a new value by hitting a service, and then adding that new service method return as a class in the directive template. i'm wondering if there is an alternative pattern that might be cleaner then this pattern that might use ng-class or possibly ng-transclude:
html:
<my-directive data-condition="{{hour.condition}}"></my-directive>
js:
angular.module('myApp')
.directive('myDirective', function (myService) {
return {
transclude: true,
replace: true,
scope: true,
template: '<i class="{{wiIconClass}}"></i>',
restrict: 'E',
link: function($scope, $elm, attrs){
$scope.wiIconClass=myService.getValue(attrs.condition);
}
}
});
If your function myService.getValue is synchronous, you could simply do:
<div ng-class="getClass(hour.condition)">
And in your controller:
$scope.getClass = function(condition) {
return myService.getValue(condition);
}
Alternatively, you can directly put your service within your scope:
$scope.myService = myService;
So the HTML becomes
<div ng-class="myService.getValue(hour.condition)">
In both cases, you will need to inject your service into your controller:
myModule.controller('myController', function($scope, myService) {
// this controller has access to myService
})
I would use the Directives scope parameter instead of using the Directives Attribute values. This is because when using the attributes you will need to setup a $watch to see when that value updates, with using $scope you get the benefit of the binding aspect.
As far as to respond to the best way, its hard to say without knowing your actual task. You can have Angular update the elements css class value in several different ways.
Here's a working Plunker with some small updates to your existing code.
http://plnkr.co/edit/W0SOiBEDE03MgostqemT?p=preview
angular.module('myApp', [])
.controller('myController', function($scope) {
$scope.hour = {
condition: 'good'
};
})
.factory('myService', function() {
var condValues = {
good: 'good-class',
bad: 'bad-class'
};
return {
getValue: function(key) {
return condValues[key];
}
};
})
.directive('myDirective', function(myService) {
return {
transclude: true,
replace: true,
scope: {
condition: '='
},
template: '<i class="{{myService.getValue(condition)}}"></i>',
restrict: 'E',
link: function(scope, elm, attrs) {
scope.myService = myService;
}
};
});
html :
<div ng-app="appMod">
<div task-info>{ { data.name } }</div>
</div>
script :
var appmod = angular.module('appMod', []);
appmod.directive("taskInfo", function () {
return {
restrict: 'A',
scope: {},
link: function ($scope, $element, attr) {
$scope.taskdat = '{"name":"Task name","status":"Completed"}';
$scope.data = JSON.parse($scope.taskdat);
scope = $scope; //scope data
},
};
});
is it possible to bind directive scope without having controller scope in Angular Js? If yes, please give me some solution examples.
You don't need a controller scope for writing a directive , see this fiddle.
Here, there is no controller scope, and the value hero is bound within the directive as:
myApp.directive('myDirective', function() {
return {
restrict: 'EAC',
link: function($scope, element, attrs, controller) {
var controllerOptions, options;
$scope.hero='superhero'
}
};
});
Works fine :)
Also the example you provided is similar, but you just need to remove scope from returned JSON object(from directive), as it is being defined as $scope inside the link fucntion.
see : http://jsfiddle.net/bg0L80Lx/
controller option ?
.directive('mydirective', function() {
return {
restrict: 'A', // always required
//controller: 'SomeController'
template:'<b>{{status}}</b>',
controller:'YourCtrl'
}
})
I made an AngularJS directive which works in AngularJS 1.2 but after updating my application to AngularJS 1.3 the fourth parameter of my link function does not contain an array of controller instances anymore but an array of $get.Constructor {} items.
jtApp.directive("jtWizard", ["$q", "$timeout", function ($q, $timeout) {
return {
require: ["^ngController", "jtWizard"],
restrict: "E",
replace: false,
transclude: true,
templateUrl: "App/Components/wizard.htm",
scope: {
mainHeader: "#"
},
controller: "jtWizardController",
link: function postLink(scope, elm, attrs, controllers, transcludeFn) {
...
var userWizardController = controllers[0];
var jtWizardController = controllers[1];
...
}
);
Any ideas about that?
1.3 changes how directive controllers are created, controllers are now instances of Constructor, I believe it is a side effect of commit 5f3f25a1. This also means you cannot return an object to define a controller, rather must modify the controllers members directly.
controller: function() { this.foo = "bar"; }
rather than
controller: function() { return { foo: "bar" }; }
Functionality should otherwise remain backwards compatible.