I am having a js file like below.
(function(){
angular.module("MyModule",[])
.service('testService',testService)
.directive("testDirective",testDirective);;
function testDirective(testService){
var directive = {
template: '<input type="text">',
restrict: 'EA'
};
return directive;
}
function testService($scope){
$scope.name = "testName";
}
}())
When I load the page I am getting an error like below.
Error: [$injector:unpr] Unknown provider: $scopeProvider <- $scope <- inputResourceService <- inputResourceDirective
Please help me with the issue.
$scope dependency will never be available inside service function
You should create an variable inside your service that will be easily shareable among-st your all angular component
(function(){
angular.module("MyModule",[])
.service('testService',testService)
.directive("testDirective",testDirective);;
function testDirective(testService){
var directive = {
template: '<input type="text" ng-model="something">',
restrict: 'EA',
link: function(scope, element, attrs){
scope.name = testService.name; //assigned service variable value to scope
}
};
return directive;
}
function testService(){
var testService = {}
testService.name = "testName";
return testService;
}
}())
Working Plunkr
I don't think you can use the scope like this in a service.
I'd say that a service would behave kind of like a class in Java, so you can call its methods from the controller.
I don't know if your doubt is more complex than what you have shown, but you could rewrite this code like this:
(function(){
angular.module("MyModule",[])
.directive("testDirective",testDirective);
function testDirective(testService){
var directive = {
template: '<input type="text">',
restrict: 'EA',
controller: function ($scope) {
$scope.name = 'testName';
}
};
return directive;
}
}())
Using a controller for the directive instead of a service.
$scope service isn't available in the service. If you really need the $scope, you can use $rootScope instead:
(function(){
angular.module("MyModule",[])
.service('testService',testService)
.directive("testDirective",testDirective);;
function testDirective(testService){
var directive = {
template: '<input type="text">',
restrict: 'EA'
};
return directive;
}
function testService($rootScope){
$scope.name = "testName";
}
}())
However, it doesn't make any sense to inject the $scope or $rootScope in the service.
Related
The goal here is to have two different directives that are technically siblings share functionality. I will either use one or the other, never one inside the other.
However, the second directive will have all the capability of the first with some small additions. Because of this, I would like the functionality to inherit from the "Parent" directive to the "Child".
I'm achieving this by re-using the same directive definition object from the Parent on the Child, with the exception of the controller/template fields being changed.
This was all working well up until I hit the watchers from my ParentDirCtrl. For some reason the watcher seems to be set up correctly watching mydir.obj1 and yet somehow inside the watcher callback function mydir.obj1 becomes undefined.
I'm assuming something about _.extend/$controller is changing how the $scopes work so mydir.obj1 isn't defined in the ParentDirCtrl, but I'm not sure why that would be the case.
Plunk
angular.module('plunker', [])
// lodash
.constant('_', _)
.controller('MainCtrl', function($scope, $timeout) {
$scope.obj = {
name: 'John',
age: 30,
};
})
.controller('ParentDirCtrl', function($scope) {
var mydir = this;
mydir.doStuffInParent = function() {
alert('executed from the parent directive');
}
$scope.$watch('mydir.obj1', function() {
// ====================================
// ERROR
// Why is 'mydir.obj1' undefined when
// occupation is set?
// ====================================
mydir.obj1.occupation = 'Meteorologist';
});
})
.directive('parentDirective', parentDirective)
.directive('childDirective', function() {
// borrow the directive definition object from the parent directive
var parentDDO = parentDirective();
// uodate the template and controller for our new directive
parentDDO.template = [
'<div>',
'<p ng-click="mydir.doStuffInParent()">{{mydir.obj1.name}}</p>',
'<p ng-click="mydir.doStuffInChild()">{{mydir.obj1.age}}</p>',
'</div>'
].join('');
parentDDO.controller = function($scope, $controller, _) {
// extend 'this' with the Parent's controller
var mydir = _.extend(this, $controller('ParentDirCtrl', { $scope: $scope }));
mydir.doStuffInChild = function() {
alert("executed from the child directive");
};
};
return parentDDO;
});
// this will be moved to the top during declaration hoisting
function parentDirective() {
return {
restrict:'E',
scope: {},
bindToController: {
obj1: '=',
},
template: '<div>{{mydir.obj1}}</div>',
controller: 'ParentDirCtrl',
controllerAs: 'mydir',
};
}
obj1 is populated on the child controller instance - that's why mydir.obj1 is undefined in the parent watcher. You can access obj1 directly via scope or by using the reference passed into the watcher:
$scope.$watch('mydir.obj1', function(val) {
$scope.mydir.obj1.occupation = 'Meteorologist';
// or
val.occupation = 'Meteorologis';
});
There is no scope inheritance here - both controllers operate on the same scope. Controller-AS syntax is what confuses you - I'd get rid of it to make things clearer.
I have this directive which sets the test variable to abc in the init function of the controller. However in my link function the test variable is undefined.
angular.module('module')
.directive('directive', myDirective);
function myDirective() {
var directive = {
link: link,
bindToController: true,
controllerAs: 'dt',
controller: Controller,
...
};
return directive;
function link(scope, element, attrs) {
...
scope.dt.initialize = function() {
initializeDirective(scope, element, attrs);
}
}
function initializeDirective(scope, element, attrs) {
// not able to get scope.dt.test, it's undefined
console.log(scope.dt.test); // undefined
}
}
function Controller() {
var vm = this;
vm.test = '123';
vm.create = function(data, config) {
vm.test = 'abc';
vm.initialize();
}
...
}
And in another JavaScript file code that creates the directive. It is calling the create function
// JavaScript in another file that inits the directive
var directive = vm.get(...);
vm.create(vm.data, config);
From my understanding, scope.dt should be automatically injected to the link function because of the controllerAs, which it is but scope.dt.test does not exist.
EDIT
I put the test variable outside the create function and set it to 123. The console.log now logs 123, which is not what I want (I want abc).
That is because your link function executes when the directive is compiled. If you call create after that you are to late and the variable 'test' is stil undefined at the moment of compilation.
What you probably want is specify the value as an attribute on your directive:
angular
.module('module')
.directive('directive', myDirective);
function myDirective(){
return {
link: link,
bindToController: true,
controllerAs: 'dt',
controller: controller,
scope: {
test: '#'
}
};
function controller(){
var vm = this;
// at this point vm.test is set to the value specified.
}
function link(scope, elem, attrs){
// U can use scope.dt.test, scope.test or attrs.test here
}
}
Use this directive as:
<my-directive test="{{someValue}}"></my-directive>
I created a d3 barchart directive using scope in the plunker http://plnkr.co/edit/yF8H9i8tyu1o2xJCN9bV
with controller having chartData in the scope.
.controller('d3Controller', ['$scope', function($scope) {
$scope.chartData = [10,20,30,40,50];
}])
and I have a bi-directional association of chartData with directive's isolated scope
scope: {
chartData: '='
},
restrict: 'EA',
replace: false,
link: function (scope, elem, attrs) {
var data = attrs.chartData.split(',');
var d3 = $window.d3;
var chart = d3.select(elem[0]);
chart
.append("div")
.attr("class", "chart")
//returns an array of all <div>...</div> elements in div
.selectAll("div")
.data(scope.chartData)
.enter()
.append("div")
.transition().ease("elastic")
.style("width", function (d) {
return d + "%"
})
.text(function (d) {
return d + "%"
});
and the associated directive is as follows
<bar-chart chart-data="chartData"></bar-chart>
This is working fine. But, I am trying to do the same using "controllerAs"
I tried making some changes, but, it is not working.
http://plnkr.co/edit/eIRkAtfJx9rlWN5LtllC
I changed the controller's scoped chartData to this
.controller('d3Ctrl', ['$scope', function($scope) {
var self = this;
self.chartData = [10,20,30,40,50];
}])
using controllerAs and bindToDirective options for directive
scope: { },
controllerAs: 'barCtrl',
controller: function() { },
bindToDirective: {
chartData: '='
},
when trying to get chartData, it is saying barCtrl is not defined.
.selectAll("div")
.data(barCtrl.chartData)
What am I doing wrong?
You are incorrectly doing some stuff in your controller As code.
1) You need to specify 2 way binding via the scope property of the settings not bindToDirective, there is no recognized property like that in the directive settings.
2) Need to use bindToController flag to specify any scope bound 2 way bound properties to be added to the controller instance and not directly on the scope. Though it is possible that you can do bindToController:{chartData:"="} it is not documented in the official doc and hence i would not recommend doing that way since it could be removed as well in the upcoming versions.
3) You can use the 4th argument to the link function as the controller instance and refer to it inside your linking function.
So it would look like
.directive('barChart', ['$window', function($window) {
var myDirective = {
controllerAs: 'barCtrl',
controller: angular.noop,
bindToController:true, //<-- Need to specify bound values to be added to the controller instance
scope: { //Need to use scope not bindToDirective
chartData: '='
},
restrict: 'EA',
replace: false,//if it is false you don't need it
//use the 4th argument as the controller instance
link: function (scope, elem, attrs, barCtrl) {
}
Demo
I'm trying to create a directive that will be used in multiple places in the app and I want it to opt into a controller.
When using the controller method
return {
...
controller: 'BlogDashCourseCtrl',
...
}
it gets the controller fine. But when I require it
return {
...
require: '^BlogDashCourseCtrl',
...
link: function($scope, iElm, iAttrs) {
$scope.title = iAttrs.title; // Do not share me with other directives
if($scope.title === $scope.step) { // $scope.step comes from a shared scope
...
}
}
}
it can't find the controller.
I don't want the controller to be called multiple times. I just want the directives to share a scope, have a private scope, too (so $scope in the directive doesn't bubble up) and do some stuff with a service.
A directive is attached to a DOM node. A DOM node can't have two scopes. Either you share the parent scope or you create an isolated one and explicitly inherit stuff from it.
BlogDashCourseCtrl:
this.step = 'whatever'; //maybe $scope.step
SomeDirective:
return {
...
require: '^BlogDashCourseCtrl',
...
link: function($scope, iElm, iAttrs, blogDashCourseCtrl) {
$scope.title = iAttrs.title; // Do not share me with other directives
if($scope.title === blogDashCourseCtrl.step) { // $scope.step comes from a shared scope
...
}
}
}
Notice that blogDashCourseCtrl does not reference the $scope of that directive/controller, but the reference itself.
There really is extensive documentation on this, with examples.
I've just upgraded to angularjs 1.2.1, and some code which has been working before, is no longer working.
I have the following directive:
var fooFactory = function($timeout)
{
var foo =
{
scope:
{
bar: '=',
baz: '=',
onBar: '&',
onBaz: '&',
},
controller: ['$scope', '$element', '$attrs',
function($scope, el, attrs)
{
var that = this;
that.state = null;
that.main = function()
{
that.state = "init";
};
that.getState = function()
{
return that.state;
}
that.main();
$scope.foo = that;
console.debug( "foo main called", $scope.foo );
}],
link: function(scope, el, attrs)
{
},
};
return foo;
};
MyAngularApp.directive('foo', ['$timeout', fooFactory]);
Here's how I'm including this directive in the view:
<div data-foo="true">
Foo: {{foo}} <br />
Foo state: {{foo.getState()}}
</div>
The problem is, when I run this code, I do get the console.debug statement for foo main called(), and $scope.foo is correctly set to the controller of the foo directive. But in the view itself, I see no output at all for either {{foo}} or {{foo.getState()}}, as if these have not been set on the scope at all.
Is it because of the isolate scope, that the line $scope.foo = that; is not having any effect? Please advise.
Works if you update (I would rather say pollute) the one already in the parent scope:
$scope.$parent.foo = that;
console.debug( "foo main called", $scope.$parent.foo );
Weird! It seems instead of creating a model in the directive's scope it goes up the chain looking for the model in the parent scope which I believe is not even there.
Totally weird!