Dynamically specify the controller to use in an Angular directive - javascript

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

Related

Angular JS - The directive is accessible between controllers

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?

How to inject a controller to a service?

Normally the service is injected in a controller.
controller.js
angular
.module('myApp')
.factory('myService', function($http){
var myService = {
get: get
};
return myService;
function get(){
// Get function.
}
});
service.js
angular
.module('myApp')
.controller('myController', function(myService) {
myService.get();
});
But it is possible to reverse the way? To Inject a controller to a service?
The straight answer would be No. You don't inject a controller to a service or anywhere else for that matter. The real question is why would you want to do that?
// Update
Assuming you are referring to https://material.angularjs.org/latest/api/service/$mdDialog.
The mdDialog service expects a controller function, you could probably pass it a reference to a function.
Example:
angular.module('app',[])
.factory('myService', function(){
return {
myMdDialogCtrl: function($scope, $mdDialog, items){
// Controller for MD Dialog
}
}
})
.controller('ctrl1', function($scope, $mdDialog, myService){
$mdDialog.show({
...
controller: myService.myMdDialogCtrl
})
})
.controller('ctrl2', function($scope, $mdDialog, myService){
$mdDialog.show({
...
controller: myService.myMdDialogCtrl
})
})

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.

Categories

Resources