How to access $rootScope variable from directive templateUrl - javascript

I've managed to get cross-domain HTML templates working by applying a url to the rootScope which I can access from controllers and other HTML files, but the problem arises when it comes to accessing the template from a directive. Here's my directive code:
angular.module("bertha")
.directive("bthToggleHeader", function() {
var controller = [
"$scope", "$rootScope", "_", function ($scope, $rootScope, _) {
if ($scope.tglOpen)
$scope.tglShowSection = true;
$scope.toggleShowSection = function() {
$scope.tglShowSection = !$scope.tglShowSection;
};
}
];
return {
restrict: "E",
scope: {
tglHeading: "#",
tglShowSection: "=",
tglOpen: "=?"
},
transclude: true,
controller: controller,
templateUrl: $rootScope.cdnUrl + "/html/directives/bthToggleHeader.html"
};
});
When attempting this I get: ReferenceError: $rootScope is not defined. Is there something blatantly obvious that I'm doing wrong here?
In a work project we tried using the link function but that didn't play nicely with being minified at all, hence the controller approach.
Any help would be greatly appreciated! Thanks.

You can use angular's dependency injection at directive level - so just place $rootScope there. See my example below:
angular
.module('bertha')
.directive('bthToggleHeader', ['$rootScope', function($rootScope) {
var controller = [
'$scope', '_',
function($scope, _) {
if ($scope.tglOpen)
$scope.tglShowSection = true;
$scope.toggleShowSection = function() {
$scope.tglShowSection = !$scope.tglShowSection;
};
}
];
return {
restrict: 'E',
scope: {
tglHeading: '#',
tglShowSection: '=',
tglOpen: '=?'
},
transclude: true,
controller: controller,
templateUrl: $rootScope.cdnUrl + '/html/directives/bthToggleHeader.html'
};
}]);
As Joe Clay said, $rootScope exists only in the controller function - that's why it's undefined outside of it.

$rootScope has fallen out of scope by the time you try to access it in templateUrl - you can't use a function parameter outside of the function (or at least, not without saving a reference somewhere)!
var controller = [
"$scope", "$rootScope", "_", function ($scope, $rootScope, _) {
if ($scope.tglOpen)
$scope.tglShowSection = true;
$scope.toggleShowSection = function() {
$scope.tglShowSection = !$scope.tglShowSection;
};
} // FUNCTION ENDS HERE - past this point $rootScope is undefined!
];
EDIT: While this answer gives some context on why your current code doesn't work, I wasn't 100% sure of the best way to solve the problem - Cosmin Ababei's answer seems like an effective solution, and I'd recommend you follow his advice!

Related

Angularjs Directive Sharing the same scope

I am trying to identify why the scope is being shared from the same directive. I have this:
<locator-service
component="redirect"
url="/api/getAllLas1"
redirect="true"></locator-service>
<locator-service
component="signup"
url="/api/getAllLas2"
redirect="false"></locator-service>
Which is basically a directive to handle locations (it works perfectly)
here is the directive for this class
var app = angular.module("frog").directive('locatorService', LocatorService);
function LocatorService() {
return {
restrict: 'E',
templateUrl: '/scripts/frog/location-service.html',
controller: 'locatorController',
controllerAs: 'locatorController',
bindToController: true,
scope: {
component: "#",
url: "#",
redirect: "#"
}
}
}
I have the "scope" declared, I read on a few threads that this would create an isolated scope.
here is the controller class (minimised)
angular.module("frog").controller('locatorController', LocatorController);
function LocatorController($scope, $rootScope, $http, $interval, $document) {
var self = this;
$scope.$watch('url', function () {
if (self.url === undefined) return;
console.log(self.url);
})
}
I attached the debugger to 'console.log(self.url);' and it printed out the following:
/api/getAllLas2
/api/getAllLas2
Could someone help me identify why this is happening?
I have an example on my Github that works perfectly fine for directives not sharing the same scope, but it uses $scope (which I have tried to implement but never even worked slightly)
https://github.com/zackdavidson/tappers-web/tree/master/public/scripts/tappers/app/components/transaction

$scope not being injected/inherited from controller to directive with dot notation

I'm having an issue with injecting/inheriting a scope from a controller to a directive. Importantly, the module is not separated into its own var.
angular.module('articles').controller('ArticlesController', ['$scope, ...
]).directive('comments', ['$scope' ... //does not work!
You don't inject the scope into a directive as a dependency. Should be like this:
.directive([function() {
return {
"link": function($scope, $element, $attrs) {
//code here
}
}
}]);
The best way is to think about directives as black boxes. You provide it with some data and it updates/displays it. You can read all required info about directives here and declare directive's inputs and outputs like this:
.directive('myDirective', function() {
return {
restrict: 'E',
scope: {
myParameter: '=' //this declares directive's interface (2 way binded)
},
link: function (scope, element) {
console.log(scope.myParameter);
}
};
});
and then you can use this directive as:
<my-directive my-parameter='variable_declared_in_controller'></my-directive>
I just bypassed $scope entirely with a couple of other scopes:
.directive('comments', ['$stateParams', '$location', 'Articles',
function ( $stateParams, $location, Articles) {
if ($stateParams.articleId != null){
var article = Articles.get({
articleId: $stateParams.articleId
});
var comments = {articleId: article.articleId, created: Date.now, comment:"Hello, World!", user: "admin" };
return{
restrict: "A",
scope: true,
template: "<div></div>"
};
}
}
]);

Angular custom directive: a strange error occurs when set controller attribute to `function(scope){}`

When I wrote a custom directive, a strange error blocks me.
angular.module('app.directives', [])
.directive('cyMenu', ['RecursionHelper', function(RecursionHelper) {
function postLink(){};
return {
restrict: 'E',
templateUrl: 'views/component/cy-menu.html',
replace: true,
transclude: false,
require: '?^cyMenu',
controller: function ($scope) { // when set this argument($scope) to scope, error occurs.
this.getList = function() {
return $scope.list;
}
},
scope: {
list: '=',
isSubmenu: '#'
},
compile: function(tElement) {
return RecursionHelper.compile(tElement, postLink);
}
}
}
As I pointed out(see comment), when I set controller attribute to controller: function (scope) {}, the error occurs:
Error: [$injector:unpr] Unknown provider: scopeProvider <- scope
http://errors.angularjs.org/1.3.6/$injector/unpr?p0=scopeProvider%20%3C-%20scope
...
I don't know why. Any help will be appreciated.
update
Here is angular's official demo, it looks similar to my directivehttps://code.angularjs.org/1.3.6/docs/guide/directive:
angular.module('docsTabsExample', [])
.directive('myTabs', function() {
return {
restrict: 'E',
transclude: true,
scope: {},
controller: function($scope) {
...
},
templateUrl: 'my-tabs.html'
};
})
A very good practice is to separate every component in a separate file, so I would start by putting your controller in a file like that :
//File recursionHelperController.js
(function() {
'use strict';
angular
.module('app.controllers')
.controller('RecursionHelperController', RecursionHelperController);
RecursionHelperController.$inject = ['$scope'];
function RecursionHelperController($scope) {
//do your stuff
}
})();
Note that I gave you the most correct format I know for a controller or any angular element in general, but you can do something simpler like:
angular.module('app.controllers')
.controller('RecursionHelperController', ['$scope', function($scope) {
//do your stuff
}])
You can then call this controller in your main file :
controller: 'RecursionHelperController'
Hope it helps
Update :
Sometimes the automatic injection has some troubles that's why I recommend doing it this way with an explicit injection. The angular doc only shows the simplest way for clarity and tutorial purposes
Update 2
If you don't want to separate the controller, try using the injection safe notation
controller : ['$scope', function($scope) {
//do your stuff
}])

What's the best way to call function within directive from controller

Just wondering what's the best way to communication from controller to directive function, i have got an ng-click on one of the button, but the function sit in the directive, is there a way i can call the function within the controller (which sits in directive). i understand u can apply double binding with scope, is there any better way of doing so?
Cheers
app.controller('leadsListing', ['$scope', function($scope){
$scope.filterresultcount = 0;
$scope.records = [];
$scope.filtertotal = '';
$scope.$watch('filtertotal', function(){
$scope.filterresultcount = parseInt($scope.filtertotal / 20);
});
$scope.moreFilterResult = function(){
if($scope.filterresultcount > 0){
$scope.filterresultcount--;
}
$scope.heyJoe(); // It's in diretive
};
}]);
app.directive('recordfilter', ['$http', 'filterService', function($http, filterService){
return {
scope: {
names : '#names',
model : '#model',
records : '=records',
filtertotal : '=filtertotal',
filterresultcount : '=filterresultcount'
},
restrict: 'A',
replace: true,
link: function($scope, iElm, iAttrs, controller) {
$scope.heyJoe()
}
}
}
I believe the best way to implement this kind of controller --> directive communication is to use $scope.$broadcast from the controller, and $scope.$on in the directive's controller/ linking function.
Controller:
app.controller('leadsListing', ['$scope', function($scope){
// ...
$scope.moreFilterResult = function(){
if($scope.filterresultcount > 0){
$scope.filterresultcount--;
}
$scope.$broadcast('joeCalled');
};
}]);
Directive:
app.directive('recordfilter', ['$http', 'filterService', function($http, filterService){
return {
scope: {
names : '#names',
model : '#model',
records : '=records',
filtertotal : '=filtertotal',
filterresultcount : '=filterresultcount'
},
restrict: 'A',
replace: true,
link: function(scope, iElm, iAttrs, controller) {
scope.$on('joeCalled', function(){
// Do something...
});
});
};
}
Edit:
Created a working example of this technique:
http://jsfiddle.net/9p3eyy5h/2/
Calling a function directly in the directive from the controller could be done by placing an empty object on the controller scope, binding it to the directive's scope with '=', and attaching a function to it in the directive's linking function/ controller, which could later be called by the wrapping controller.
Controller:
app.controller('leadsListing', ['$scope', function($scope){
// ...
$scope.directiveFuncs = {};
$scope.moreFilterResult = function(){
if($scope.filterresultcount > 0){
$scope.filterresultcount--;
}
$scope.directiveFuncs.heyJoe();
};
}]);
Directive:
app.directive('recordfilter', ['$http', 'filterService', function($http, filterService){
return {
scope: {
names : '#names',
model : '#model',
records : '=records',
filtertotal : '=filtertotal',
filterresultcount : '=filterresultcount',
// Binding to the controller's func obj
funcs: '='
},
restrict: 'A',
replace: true,
link: function(scope, iElm, iAttrs, controller) {
scope.funcs.heyJoe = function(){
// Do something...
}
});
};
}
HTML:
<div ng-controller="leadsListing">
<div recordfilter funcs="directiveFuncs"></div>
</div>
I would however advise to use my other approach, as it prevents direct dependency between the controller and the directive, and therefor, more robust, so it won't throw an error if the directive is missing or changes.
Working example:
http://jsfiddle.net/9pm3zg5s/1

Creating an angular directive that binds a service?

Not sure if I am misunderstanding how directives are created here. Say for example I have a controller such as:
angular.module('myApp.controllers').controller('MyController', ['$scope', 'MyService', function($scope, MyService) {
$scope.restangularService = MyService;
}
I then have a directive such as:
angular.module('myApp.directives').directive('myGrid', function() {
return {
restrict: 'A',
templateUrl: 'some/path/here.html',
scope: {
restangularService: '&'
},
controller: ['$scope', function($scope) {
//access $scope.restangularService to run some queries
}
};
});
I then use my directive as such:
<div data-my-grid data-restangular-service='restangularService'></div>
I would expect that in my directive I could access $scope.restangularService and make calls however it's not being populated correctly. Am I doing this totally wrong? Any input? I have a feeling I need to be using the ngModel directive somehow.
The "&" prefix of an isolate scope value in a directive provides "one-way binding" which makes available a getter function in the directive's scope.
Any changes you make to the object will not make their way back up to the parent controller of the directive (it is "read-only"). So you can't access your 'restangularService' variable as you would in the controller's scope, without calling the getter function:
angular.module('myApp.directives', []).directive('myGrid', function() {
return {
restrict: 'A',
templateUrl: 'some/path/here.html',
scope: {
restangularService: '&'
},
controller: ['$scope', function($scope) {
console.log($scope.restangularService()); // outputs service value
}]
};
})
Alternatively, you could use "=", which would allow you directly access the scope object you pass in:
angular.module('myApp.directives', []).directive('myGrid', function() {
return {
restrict: 'A',
templateUrl: 'some/path/here.html',
scope: {
restangularService: '='
},
controller: ['$scope', function($scope) {
console.log($scope.restangularService); //outputs service value
}]
};
})
Plunk demonstrating both types

Categories