Angular JS - The directive is accessible between controllers - javascript

I am new to Angular and just wondering how come my directive is accessible between controller as I only register it into sub controller, not main app controller. Below is my code.
app.module.js
angular.module('adminPage', ['ngRoute', 'ui.bootstrap', 'ngAnimate', 'login', 'registerEmployee', 'listEmployee'])
.controller('adminPageCtrl', ['$scope', '$location' , funcAdminPageCtrl]);
angular.module('login',[]);
angular.module('registerEmployee', []);
angular.module('listEmployee', []);
So as stated, I have adminPage acts as a main controller and others as sub controllers.
registerEmployeeController.js
angular.module('registerEmployee')
.controller('registerEmployeeCtrl', ['$scope', funcRegisterEmployeeCtrl
])
.directive("compareTo", ['$timeout', funcCompareTo]);
As seen above, I register the compareTo directive to registerEmployee sub controller.
I would like to use the same directive in listEmployeeController.js's ModalInstanceCtrl (popup), so I added the same directive to that. When I tested it, I got error which is Error: [$compile:multidir] Multiple directives [compareTo, compareTo] asking for new/isolated scope on:. The code is below.
angular.module('listEmployee')
.controller('listEmployeeCtrl', ['$scope', '$uibModal', '$log', '$document', funcListEmployeeCtrl ])
.controller('ModalInstanceCtrl', funcModalInstanceCtrl)
.directive("compareTo", ['$timeout', funcCompareTo]);
After that I tried removing the compareTo directive from listEmployeeController.js and the error is gone. It is, then, working as expected (the compareTo directive can be used in listEmployeeView.html's modal) even though it is not registered to its controller.
My funcCompareTo is as below.
function funcCompareTo($timeout) {
return {
require: "?ngModel",
scope: {
otherModelValue: "=compareTo"
},
link: function(scope, element, attributes, ngModel) {
$timeout(function(){
if (!ngModel) {
console.log("can't get ngModel.");
return;
}
ngModel.$validators.compareTo = function(modelValue) {
console.log(modelValue+"; "+scope.otherModelValue);
if (modelValue == '') return true;
return modelValue == scope.otherModelValue;
};
scope.$watch("otherModelValue", function() {
ngModel.$validate();
});
}, 0);
}
};
}
As I only registered the directive in registerEmployeeCtrl, how come that directive is existed in ModalInstanceCtrl?

Related

Resolve in directives AngularJS

how i can use something like resolve in config in directives?
I have such code:
angular.module('config', ['ngRoute', 'resources.params'])
.config(['$routeProvider', function($routeProvider) {
'use strict';
$routeProvider
.when('/config',{
templateUrl: 'templates/config/config.tpl.html',
controller: 'ConfigCtrl',
resolve: {
params: ['Params', '$route',
function(Params, $route) {
if ($route.current.params.skey)
return Params.get($route.current.params.skey);
else
return null;
}
]
},
reloadOnSearch: true
});
}
])
.controller('ConfigCtrl', ['$scope','$route','$routeParams', 'params','Params',
function($scope,$route,$routeParams,params,Params){
'use strict';
I can use "params" in my controller because i wrote "params: [..." in my .config
But now i want to use this "params" in my directive:
.directive('mapsysitem', ['$location', '$routeParams', '$freshmark',
function($location, $routeParams, $freshmark) {
'use strict';
return {
restrict: 'E',
require: '^mapsyslist',
scope: {
zoomlist: '#',
item: '=',
skey: '=',
select: '&'
},
replace: true,
templateUrl: 'templates/map/mapsysitem.tpl.html',
controller: ['$element', '$scope', 'System','$filter','Params',
function($element, $scope, System, $filter, Params) {
.....
}]
};
}]);
If i will add "params" to controller options i will have "Unknown provider: paramsProvider <- params". How i can solve this problem? Thaks you.
Due to your use of an isolated scope within your directive, you will need pass the parameter as an attribute in the directive node
Set the scope in the app controller
.controller('ConfigCtrl', ['$scope','$route','$routeParams','params','Params',
function($scope,$route,$routeParams,params,Params){
$scope.myParams=Params
then pass the $scope.myParams to the directive
<mapsysitem param='myParams'></mapsysitem>
Then create the two way binding in your scope
scope: {
param:'='
},
Then you will be able to use this in the directive controller
controller: ['$element', '$scope', 'System','$filter',
function($element, $scope, System, $filter ) {
var myParams= scope.param
I don't think you can get the scope or attributes values of your directive's controller so if you can't you have to use a service (or $rootScope maybe) to share those values (and watch for changes).
Otherwise you may use the link function. You can access to the scope and attributes here. Here is a quote from angular site about controller/link function :
Best Practice: use controller when you want to expose an API to other directives. Otherwise use link.
This mean "use link function unless you're really sure you need controller".
See https://docs.angularjs.org/guide/directive

Access current state name within a directive?

I am using UI-Router and trying to access my web app's current state from within a directive, using the following:
footer.directive.js
(function () {
'use strict';
angular
.module('app')
.directive('myFooter', myFooter);
myFooter.$inject = ['$cookies', 'userFactory', '$state', '$log', '$rootScope'];
function myFooter($cookies, userFactory, $state, $log, $rootScope) {
var directive = {
restrict: 'E',
templateUrl: 'app/components/footer/footer.html',
controller: FooterController,
controllerAs: 'vm',
bindToController: true
};
return directive;
function FooterController($state) {
var vm = this;
vm.currentState = $state;
}
}
})();
footer.html
<div class="footer">
<p>{{ vm.currentState.current.name }}</p>
</div>
When I run $log.log($state) it posts an object in my console that has a current object with a name attribute that is equal to the state name that I need, but when I try to reference the $state.current.name, either on my view or by logging it to the console, it displays as an empty string.
I'm a bit new to Angular, so if someone could explain to me what is going on here or at the least how to fix this so that I can display what I want properly, that would be a huge help. Thanks!
Edit: Two other questions that I looked at before posting this one are:
This one which seems to deal more with changing a class name based on state name, and this one, which doesn't quite address my problem either (and doesn't look like it could possibly be the right way to do this.)
First of all, there is a naming bug in your posted code! (amaiFooter)
The second thing is, if you log an object, it's bound by a call by reference
So the moment you log it, you log the reference. That means when you look at it, it can contain other values than when you have logged it
But when you logged the state name and it was undefined, that was the right output!
You can try to wrap it in a $timeout function with 0 delay, just to add your code to the end of the current digest cycle, that should solve your problem
You need inject the state service on the controller as follows
(function () {
'use strict';
angular
.module('app')
.directive('myFooter', myFooter);
myFooter.$inject = ['$cookies', 'userFactory', '$state', '$log', '$rootScope'];
function myFooter($cookies, userFactory, $state, $log, $rootScope) {
var directive = {
restrict: 'E',
templateUrl: 'app/components/footer/footer.html',
controller: FooterController,
controllerAs: 'vm',
bindToController: true
};
return directive;
}
FooterController.$inject = ['$state'];
function FooterController($state) {
var vm = this;
vm.currentState = $state;
}
})();

AngularJS - State name passed into controller doesn't work

I'm new to angular and I can't figure out why this isn't working.
When I call {{$state.current.name}} in my view it works perfectly fine, but as soon as I try to pass it to my controller, the state gets "lost".
Here is my setup:
Module:
angular.module('app', ['ui.router'])
.run(function ($rootScope, $state, $stateParams) {
$rootScope.$state = $state;
$rootScope.$stateParams = $stateParams;
})
//followed by boilerplate ui router setup
View:
<home-nav-bar></home-nav-bar>
Directive:
angular.module('app')
.directive('homeNavBar', function () {
return {
restrict: 'E',
templateUrl: 'homeNavBarView.html',
controller: 'navCtrl'
};
})
})();
Controller:
angular.module('app')
.controller('navCtrl', function ($state, $scope) {
alert($state.current.name); //returns empty
});
})();
At this point I am clueless as to why I can get the state name in the view but not in my controller..
Well, ui-router docs say you can access current state's config object using the current property of $state, so there is absolutely no need to attach anything to $rootScope. I have just tested something along the lines of (simplified a bit for readability):
angular.module('myApp')
.controller('SomeCtrl', function ($scope, $state) {
console.log($state.current);
});
The result in Chrome console is:
Object {url: "/some", templateUrl: "app/some.html", controller: "SomeCtrl", name: "some"}
So as you can see all information should be available along with the name.

Dynamically specify the controller to use in an Angular directive

I am looking to specify the controller that a directive should use via an attribute on the element - i.e. dynamically:
HTML
<div data-mydirective data-ctrl="DynController"></div>
Angular
angular.module('app', [])
.controller('MainController', [function() { ... }])
.controller('DynController', [function() { ... }])
.directive('mydirective', [function() {
return {
controller: 'DynController', // <- make this dynamic
...
}
}]);
You can do the following:
.directive('mydirective', [function() {
return {
controller: function($scope, $element, $attrs){
//Make decision based on attributes or $scope members
if($scope.$attrs.caseA == 'true'){
return new ControllerA($scope, $element, $attrs);
} else {
return new ControllerDefault($scope, $element, $attrs);
}
},
...
}
}]);
Taking it a step farther, you can use $controller
.directive('mydirective', ['$controller',function($controller) {
return {
controller: function($scope, $element, $attrs){
return $controller($attrs.dynamicCtrlName, {$scope: $scope,$element:$element,$attrs:$attrs});
},
...
}
}
In both cases, make sure you provide all the specific injectable dependencies (particular $scope, $attrs, transclude function etc...) your controllers expect. More details on controller injectables here $compile under the controller section - ideally, outer controller function should receive them all pass as locals.
Nothing in the docs about this, but you can do the following. From my understanding this stuff is used under the hood to make ng-controller work.
.directive('mydirective', [function() {
return {
controller: '#',
name: 'ctrl', // <- attribute that specifies the controller to use
...
}
}]);
I think a better approach is just use scope since scope is already bound to controller, i.e.
put what you want to call on controller scope and simple handle it in directive link

Accessing parent controller in an Angular directive

I have directive that dynamically sets the header bar content for a given application state.
I would like to be able to access the functions and variables in the Controller of the current view, but I am only ever able to access my RootCtrl.
The directive looks like this.
return {
restrict: 'EA',
template: "<div ng-include='getState()'></div>",
transclude: true,
scope: false,
controller: ['$scope', '$state', function($scope, $state) {
//some logic to retrieve and return the correct header template html
}],
link: function(scope, element, attrs){
console.log(scope.test);
console.log(scope.test2);
}
}
And the controllers.
.controller('RootCtrl', function($scope, $state, $location, $rootScope) {
$scope.test = 'hello';
//...
})
.controller('ContactsCtrl', function($scope, $state, CustomerService) {
console.log('Contacts init');
$scope.test2 = 'hello 2';
//...
})
And when I navigate to the contacts state, the output looks like this.
hello
undefined
Contacts init
What should I do if I want to be able to access the test2 variable?
You will need to use the require property inside your directive.
This will make the scope of the defined controllers available inside the link function as 4th argument. You can access the scopes as an array inside the link function then.
Your code may look like:
return {
restrict: 'EA',
template: "<div ng-include='getState()'></div>",
transclude: true,
scope: false,
require:['^RootCtrl', '^ContactsCtrl'],
controller: ['$scope', '$state', function($scope, $state) {
//some logic to retrieve and return the correct header template html
}],
link: function(scope, element, attrs, requiredControllers){
console.log(requiredControllers[0].test);
console.log(requiredControllers[1].test2);
}
}
See the Angular documentation for Directives for some more examples (under the title Creating Directives that Communicate) and the explanation of the ^controller syntax.

Categories

Resources