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.
Related
I've looked at the documentation for angular.mock.module and a couple of examples of others using it but I seem to be running into an issue in my use-case that I don't understand.
I'm running Jasmine (2.4.1) tests with angular (1.4.9) and I have my angular app separated into multiple modules. When I attempt to mock out certain parts of my app for unit testing I want to mock out entire modules (or providers) so that I only expose the pieces I use.
Here is a very simple app that has a main module plunker which depends on plunker.service. plunker.service depends on plunker.constant.
var app = angular.module('plunker', ['plunker.service']);
app.controller('MainCtrl', function($scope, valueService, appService) {
$scope.init = function() {
$scope.appValue = valueService.getValue();
$scope.appIsRunning = appService.getStatus();
};
});
angular.module('plunker.service', ['plunker.constant'])
.service('appService', function(appSettings) {
var vm = this;
vm.getStatus = function () {
if (appSettings.isRunning) {
return true;
} else {
return false;
}
};
})
.service('valueService', function(valueSettings) {
var vm = this;
vm.getValue = function () {
return valueSettings.value;
}
});
angular.module('plunker.constant', [])
.constant('appSettings', { isRunning: true })
.constant('valueSettings', { value: 10 });
In my Jasmine tests I have a beforeEach() that registers my modules using module (aka angular.mock.module).
I have seen 3 ways of using module
string
function with $provide
object
You can see below that I use the module('plunker') (string) to register my main module and I have 3 ways of mocking out my appSettings constant (A, B, C). You will notice that the function with $provide.constant works fine but function with $provide.value does not and object does not.
beforeEach(function() {
module('plunker');
function useFunction(typeofProvider) {
module(function($provide) {
$provide[typeofProvider]('appSettings', { isRunning: false });
});
}
function useObject() {
module({
appSettings: { isRunning: false }
});
}
// A. THIS WORKS! //
useFunction('constant');
// B. THIS DOES NOT //
// useFunction('value');
// C. THIS ALSO DOES NOT!! //
// useObject();
inject(function($rootScope, $controller) {
$scope = $rootScope.$new();
ctrl = $controller('MainCtrl', {
$scope: $scope
});
});
});
I have also seen people use the following syntax...
beforeEach(function() {
var mockService = function () {
var mockValue = 10;
this.value = mockValue;
};
// D.
module('a.module.name', function newProviders($provide){
$provide.service('realService', mockService);
});
});
My questions
In my test code, why does A. work but B. and C. do not?
Is D. equivalent to calling module('a.module.name'); followed by module(function newProviders($provide) { ... });? Does placing both in the same module() call have any special effects on how things are registered or is it just a shorthand? (based on the documentation it should be a shorthand)
Related to Jasmine, specifically, do all beforeEach() calls run in the same top-to-bottom order with every execution?
Here is my plunker for the above app and jasmine code
Thanks
This happens because of how Angular injector works. In fact, there are two different injectors in Angular. The one (available as $injector in config blocks) deals with service providers. Another one (available as $injector anywhere else) deals with service instances. Providers and instances are cached and stored internally.
$provide.constant('service') creates both provider and instance of name 'service' at call time.
All other types of services are lazily instantiated. They create 'serviceProvider' provider at call time, but 'service' instance is created on the first injection.
Since Angular service instance is a singleton, it refers to instance cache before the instantiation. If the instance is in the cache, it is reused and not instantiated. constant service instance is eagerly instantiated, so only another constant can override the instance.
Object properties in angular.mock.module are shortcuts for $provide.value, and useObject() equals to useFunction('value') in this example.
As long as module order stays the same,
module('a.module.name', function ($provide) { ... });
is indeed a shortcut for
module('a.module.name');
module(function ($provide) { ... });
Due to the fact that appSettings object isn't used in config blocks (the primary use of constant service), it is more convenient to make it value.
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 defined a service like this :
angular.module('myApp').service('myService', [
'$rootScope',
...
...
I want my service to be instantiated only for new user (i.e. when user.createdAt > today).
So is there a way to conditionally inject my service or at least destroy my service without any side effect if the user is an old one.
You can use the $injector service to get inject-ibles dynamically if you need to:
app.controller('DemoCtrl', function($injector) {
this.clicky = function() {
myFact = $injector.get('myFactory');
myFact();
};
});
app.factory('myFactory', function() {
return function() {
alert('foobar!');
};
});
Here's a full running demo: http://jsbin.com/bemakemaja/1/edit
And the $injector docs: https://docs.angularjs.org/api/auto/service/$injector
As a general guideline though I'd recommend not designing your services such that simply injecting them has side effects.
use factory instead of service, so that you can have some checking logic in your service
angular.module('myApp').factory('myService', function () {
if (newUser) {
return { // your service implementation here
}
}
return undefined;
};
So is there a way to conditionally inject my service or at least destroy my service without any side effect if the user is an old one.
Best to capture this logic inside the service:
class Service{
static $inject = ['$rootScope'];
constructor($rootScope){
if (user.createdAt > today){ /*something*/ }
else {/*other thing*/}
}
}
NOTE: services are singletons so conditional injection doesn't see like a desirable solution.
Also to learn about $inject : https://www.youtube.com/watch?v=Yis8m3BdnEM
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
I am working on an angularJS app and I am trying to stick with the most efficient and widely accepted styles of development in AngularJs.
Currently, I am using this way of declaring my services like so:
app.factory('MyService', function() {
/* ... */
function doSomething(){
console.log('I just did something');
}
function iAmNotVisible(){
console.log('I am not accessible from the outside');
}
/* ... */
return{
doSomething: doSomething
};
});
However, there are numerous examples out there and I am not quite sure which design style to follow. Can someone with extensive knowledge about services explain the reason why a certain style is more relevant than another?
Is what I am doing useful in any way other than restricting the access to certain functions in my service?
I would suggest you layout your factory or service as they do in the angular-seed app, except that app annoyingly only uses value in the services.js boilerplate. However you can adapt the layout they use for controllers, directives and filters.
'use strict';
/* Filters */
angular.module('myApp.filters', []).
filter('interpolate', ['version', function(version) {
return function(text) {
return String(text).replace(/\%VERSION\%/mg, version);
}
}]);
Which means for a service you would do:
'use strict';
/* Services */
angular.module('myApp.filters', []).
service('myservice', ['provider1', 'provider2', function(provider1, provider2) {
this.foo = function() {
return 'foo';
};
}]).
factory('myfactoryprovider', ['myservice', function(myservice) {
return "whatever";
}]);
This has more boilerplate than you absolutely need, but it is minification safe and keeps your namespaces clean.
Than all you have to do is decide which of const, value, factory or service is most appropriate. Use service if you want to create a single object: it gets called as a constructor so you just assign any methods to this and everything will share the same object. Use factory if you want full control over whether or not an object is created though it is still only called once. Use value to return a simple value, use const for values you can use within config.
I don't believe there's an accepted standard but I myself follow this convention:
var myServiceFn = function (rootScope, http) { ... };
...
app.factory('MyService', ['$rootScope', '$http', myServiceFn]);
I feel like this is cleaner than defining the function inline and also allows for proper injection if I ever decide to minify my files. (see http://docs.angularjs.org/tutorial/step_05).
As an example, I've been defining services within modules like so:
angular.module('userModule', [])
.service('userService', function() {
this.user = null;
this.getUser = function() {
return this.user;
};
this.userIsNull = function() {
return (this.user === null);
};
this.signout = function() {
this.user = null;
};
})
I think it is more clear to use the .service rather than the .factory?