I'm looking for a way to access both controllers inside the directive.
Currently it's not working and I don't know why or if it's even possible.
If I use both require and controller option, inside the link function ctrl property refers to whatever I requested via the require option.
I can't think of a way to access the controller inside the link function when the require option is present.
It seems that these two properties are mutually exclusive ?
angular.module('moduleName').directive('directiveName', [function () {
return {
controller: 'MediaController',
require:'ngController',
link: function (scope, element, attributes, ctrl) {
// I need access to both controllers here
}
}
}]);
If you want both controllers, then require both controllers:
angular.module('moduleName').directive('directiveName', [function () {
return {
controller: MediaController,
require:['directiveName', 'ngController'],
In this case ctrl is an array containing the two controllers.
Without really knowing why you need to access both controllers, I can only offer minimal advice here. My suggestion would be to create a service to handle cross controller needs. Services are singletons and they support data binding. Services are my preference for cross controller work every day. For example:
App.controller('Ctrl1', function Ctrl1 ($scope, TestService) {
$scope.someValue = TestService.getValue();
});
App.controller('Ctrl2', function Ctrl2 ($scope, TestService) {
$scope.someValue = TestService.getValue();
});
App.factory('TestService', function() {
var myVal = "I Bound";
return {
getValue: function() {
return myVal;
}
}
});
This method allows you to abstract a controllers need to directly access another controller. Your services can be pulled into these directives or other services too. I hope this helps a bit.
Thanks,
Jordan
Related
I can access $controllerProvider but can not access $controller in the following method
angular.module(MODULE_NAME, ['common'])
.config(['$routeProvider','$controllerProvider',
function($routeProvider, $controllerProvider) {
console.log($controllerProvider);//defined
console.log($controller);//undefined
}]);
If I use $controller as dependency injection, it is giving
Unknown provider: $controller
But I need to access it, How do I do that
EDIT
I need this because I want to check my controller exists not not. Here is the post where from I am using this code
try {
$controller(controllerName);
listControlerName = MODULE_NAME+'ListController';
} catch (error) {
listControlerName = 'CommonListController';
}
CONTEXT
I am creating architecture of a project. My project structure as follows.
I have one COMMON module. with ListController, EditController, ViewController
I have some other modules like MOD1, MOD2 etc with MOD1ListController, MOD1EditController, MOD1ViewController so on.
Those module specific controller extend corresponding controller from Common Module.
Now my plan is while a new module (MODX) need to be developed, then if there is some extra functionality, then only developer will create a new MODXListController for that module by inheriting common ListController. Otherwise they need not to create any thing.
So system will check if that module contains MODXListController or not. If not then system will use Common ListController.
I do not want to create a MODXListController which inherits common ListController but does not do any extra change. Because I have lots of module nearly 25 and all of then are sharing same functionality mostly.
Without explicitly inheriting all controllers (which is acceptable but verbose solution), a good alternative is to wrap controller switching functionality into mod-controller directive (similar to ng-controller), something like that:
angular.module('common', [])
.constant('MODULE_NAME', 'Common')
.constant('modControllers', [])
.controller('CommonEditController', ...);
.controller('CommonListController', ...);
.directive('modController', function (MODULE_NAME, modControllers) {
return {
restrict: 'A',
scope: true,
priority: 500,
controller: function ($controller, $attrs, $scope) {
var ctrlName = (modControllers.indexOf($attrs.modController) >=0 ? MODULE_NAME : 'Common') + $attrs.modController;
angular.extend(this, $controller(ctrlName, { $scope: $scope }));
});
};
});
angular.module('mod1', ['common'])
.constant('MODULE_NAME', 'Mod1')
.constant('modControllers', ['ListController']);
.controller('Mod1ListController', function ($controller) {
angular.extend(this, $controller('CommonListController');
...
});
When the controller should be specified in application code rather than view (i.e. route and directive controllers), the similar thing can be done with controller factory.
angular.module('common')
.provider('ctrlFactory', function (MODULE_NAME, modControllers) {
function factoryFn(ctrlName) {
return (modControllers.indexOf(ctrlName) >=0 ? MODULE_NAME : 'Common') + ctrlName;
};
this.get = this.$get = factoryFn;
});
It can be injected into directives and used as
...
controller: ctrlFactory('ListController')
Due to the fact that it returns controller name instead of $controller instance and depends only on constant services (MODULE_NAME, modControllers), service provider can also be used in the same way as service instance to declare route controllers in config block:
...
controller: ctrlFactoryProvider.get('ListController')
Because $controller doesn't expose the list of registered controllers, try-catching it comes to mind as automatic solution, and it is the last thing the one may want to do: besides it is a hack, it just suppresses possible exceptions and damages testability.
In .config you can only use providers (e.g. $routeProvider).
In .run you can only use instances of services (e.g. $route).
$controller is of type service which can not be used in .config.
For more detail on this read here.
The detailed discussion on Stack Overflow at this thread shows what can be injected into others.
You can not inject $controller into config
Although you can use the following service to check if your controller is defined.
angular.service('ControllerChecker', ['$controller', function($controller) {
return {
exists: function(controllerName) {
if(typeof window[controllerName] == 'function') {
return true;
}
try {
$controller(controllerName);
return true;
} catch (error) {
return !(error instanceof TypeError);
}
}
};
}]);
Now you can inject ControllerChecker and call its exists(CtrlName) function to check whether your controller is defined or not.
This is something I discovered when trying to make my controllers more reusable.
app.factory('BaseController', function(myService) {
return function(variable) {
this.variable = variable;
this.method = function() {
myService.doSomething(variable);
};
};
})
.controller("ChildController1", function($scope, BaseController) {
BaseController.call($scope, "variable1");
})
.controller("ChildController2", function($scope, BaseController) {
BaseController.call($scope, "variable2");
});
And now I can do something like this in my HTML (e.g inside ng-controller="ChildController1"): ng-click="method()"
The code simply works. But I don't know how it really works (what kind of pattern is it?) and would it be a good practice to do so?
Your solution works more or less like this:
app.factory('mixinBaseController', function(myService) {
return function (scope, variable) {
angular.extend(scope, {
variable: variable;
method: function() {
myService.doSomething(variable);
}
});
};
})
.controller("ChildController1", function($scope, mixinBaseController) {
mixinBaseController($scope, "variable1");
})
.controller("ChildController2", function($scope, mixinBaseController) {
mixinBaseController($scope, "variable2");
});
Can you see? By your .call($scope, ...) is just setting the context (this) to the real $scope and this way the BaseController just extends your scope with properties and functions.
This is only to demonstrate how your code works.
To achieve a more JavaScript like inheritance, please see:
Can an AngularJS controller inherit from another controller in the same module?
AngularJS controller inheritance
AngularJS Inheritance Patterns
Two Approaches to AngularJS Controller Inheritance
You should as well have a look at the "controllerAs" syntax introduced in AngularJS 1.2. I highly recommend to use controllerAs. This also should help to implement inheritance for your controllers.
I have a directive which is associated with one controller and the functions in my controller defined as
MyFormController.prototype.addNewRow = function addNewRow() {
//Adding row code
};
I want to call this method from another controller, possible ways?
I ve user the service and moved the code into that service which is shared across the controllers, however the service code does the DOM manipulation, and then i guess the next question would be that can we use $compile in a service test case
service or factory is used to share data between controller.so it would be best to define function in service and factory.
demo:
(function() {
angular.module('app', [])
.service('svc', function() {
var svc = {};
svc.method = function() {
alert(1);
}
return svc;
})
.controller('ctrl', [
'$scope', 'svc', function($scope, svc) {
svc.method();
}
]);
})();
You should not!!!
That defeats the whole purpose of modularity.
If possible try to make the function generic and create a service/factory. Now both the places where you need, use the same generic function defined in service and do their stuff.
Otherwise you can also look at events to make changes accordingly.
Look at this blog post:
http://ilikekillnerds.com/2014/11/angularjs-call-controller-another-controller/
Last but the worst solution is (avoid using this, this is literally an aweful way) is catching the element inside directive and getting its scope and taking the function from it.
Example,
var otherControllerFunc = $(".inside-directive").scope().yourfunc;
Is there a correct way to pass a non-angular callback (preferably an anonymous function) name to a directive and then have it activated from the directive. My motivation is to interface from the directive to legacy non-angular code.
Here is what I want:
<my-Directive callback="function (param) {some-legacy-function(param)}">
and then have it activated:
app.directive('myDirective', function($scope) {
return {
scope: {
callback: '&'
}
controller: function($scope) {
$scope.someMethod = function() {
$scope.callback($scope.param);
}
}
});
Note that callback function is not defined on any scope and should preferably anonymous (but I can manage with a non-anonymous function as well).
I found one of doing this by simply passing a string and then doing an eval to call the method but after reading around I understand this approach is problematic (security wise).
Any suggestions would be appreciated.
you can wrap your legacy code inside a injectable service, so you can inject it anywhere (and replace/mock it for tests)
// here set on global for example
var legacyFunction=function(){};
//angular code (has to be loaded after your "old" script
myApp.factory('legacyFunction',function(){
return legacyFunction;
})
//now it is available anywhere in you application (directives, controllers, etc) as an injectable -here in a controller so you can bind it to a $scope for example
myApp.controller('myController',['$scope', 'legacyFunction', function($scope, func){
$scope.myFunc = func
}])
I have a service that watches something on the rootscope and does something in response. Nobody else requires that service as a dependency. How do I tell the service to start doing its thing?
This is how I do it for now.
angular.module('app')
.service('myService', function ($rootScope) {
return function () {
$rootScope.$on("...", function () {
//do stuff
});
};
})
.run(function (myService) {
myService();
});
Is there a cleaner way to do this?
I've implemented similar things, but using a directive, and not a service:
app.directive('myDirective', function() {
return {
controller: function($scope) {
$scope.$on('myEvent', function () {
// Do stuff
});
}
}
});
And then used this in the root element of my application, which can, for example, be body:
<body ng-app="app" my-directive>
The benefit of using directives in this way over services is that it makes it a touch less global / final. For example, if you end up wanting some scopes to not be able to $emit events to this handler, then you could move the myDirective to another element. Or you could maybe down the road you'll want a part of your app to respond slightly differently to myEvent, so you can add another instance of myDirective, maybe passing it some options via attributes. On the whole it leaves it a lot more open to adding complex behaviour.