I just went through a code where the programmer has created an array of controllers and then added the array to a module.
Following is the type of code :-
// the array of controllers
var controllers = {};
//adding a controller to array
controllers.ExampleController1 = function(){};
controllers.ExampleController2 = function(){};
//adding the controllers to module
var ABCmodule=angular.module('ABCmodule',[]);
ABCmodule.controller(controllers);
I realized that the controller array is made in global scope. Isnt that dangerous? Moreover is this good style of coding. I prefer only making one variable for module and adding all controllers inside that module. What is the best way to do this?
This particlar setup can be solved by:
for (var ctrl in controllers) {
if(controllers.hasOwnProperty(ctrl)) {
ABCmodule.controller(ctrl);
}
}
That being said, why not just add controllers directly to module which would be declared first? Also loop can be simplified using lodash's _.forOwn()
angular
.module('abc', [])
.controller('ctrl1', ctrl1)
.controller('ctrl2', ctrl2); // ...
function ctrl1() {
}
function ctrl2() {
}
Angular does this internally all the time, but you don't have to accept its source code style as paragon. It is ok while you keep the variables inside IIFE.
If you want to keep the style in ng vein, you can do like this:
ctrlModule.config(function ($controllerProvider, controllers) {
angular.forEach(controllers, function (constructor, name) {
$controllerProvider.register(name, constructor);
});
});
ctrlModule.constant('controllers', { ... });
Related
I am trying to inject a few helpers (lodash and a few of my own) in Angular controllers so I have:
angular.module('app').factory("_", Lodash);
Lodash.$inject = ['$window'];
function Lodash($window) {
if ($window._)
return $window._;
}
angular.module('app').factory("_", Helper);
Helper.$inject = ['$window'];
function Helper($window) {
return {
test: function () {
return "test";
}
}
}
So I would like all helpers to be accessible under _ and I would define them in a few JS files ... Can this be done?
With my code only one of them work: lodash methods or test().
For services that don't require dependencies ($window isn't crucial here because it serves for no purpose and can be replaced with window or _ global) config block is a good place to define them, because constant services are already available there.
app.config(function ($injector, $provide) {
var lodash = $injector.has('_') ? $injector.get('_') : {};
angular.extend(lodash, window._);
$provide.constant('_', lodash);
});
app.config(function ($injector, $provide) {
var lodash = $injector.has('_') ? $injector.get('_') : {};
angular.extend(lodash, {
test: ...
});
$provide.constant('_', lodash);
});
When the service is defined this way, its value can be extended several times, the order of config blocks doesn't matter.
Alternatively, service value can be changed with decorator, in order to do that the service should be already defined.
You can not redeclare a factory. As you already noticed, this will overwrite the previous.
You could prepare the helper in separate files and register them in one factory call elsewhere in your app.
I am using angular and grafana in my project.
I have a service -> dashboardViewStateSrv
My Service Code :
define([
'angular',
'lodash',
'jquery',
],
function (angular, _, $) {
'use strict';
var module = angular.module('grafana.services');
module.factory('dashboardViewStateSrv', function($location, $timeout) {
function DashboardViewState($scope) {
var self = this;
self.state = {};
self.panelScopes = [];
self.$scope = $scope;
// something
}
return {
create: function($scope) {
return new DashboardViewState($scope);
}
};
});
});
In My side menu controller :
$scope.dashboardViewState = dashboardViewStateSrv.create($scope);
if ($scope.dashboardViewState) {
if($scope.dashboardViewState.state.citreedepth){
depth = +$scope.dashboardViewState.state.citreedepth;
}
}
In My Dashboard controller :
$scope.dashboardViewState = dashboardViewStateSrv.create($scope);
DashboardViewState object is being created twice (Dashboard Ctrl and Side Menu ctrl).
I am creating DashboardViewState object twice, I want to avoid that. If I can avoid creating DashboardViewState object in Side Menu ctrl?
There should be only one view state. As per my understanding all the services are singleton in angular.
Please guide me what I can do?
Services are singletons, they are essentially a constructure function allowing you to use the this keyword inside them. They are instantiated once when first created then that instance is shared throughout your app.
Factories are, well, factories. Somewhere in Angular it will call Object.create() on the object your return from a factory. Meaning each call will return a new instance of it.
So in your use case your creating a new object twice. First by using a factory, then second by returning a new object from that factory.
This may help http://blog.thoughtram.io/angular/2015/07/07/service-vs-factory-once-and-for-all.html
So if you want a single instance of an object through your application you should use .service() not .factory();
If you want to instantiate a new Object only once you could use a service. Have the object as a property and a get method. The service could check if the object is already created and if not make it.
something like this (example code, not tested):
module.service('dashboardViewStateSrv', function($location, $timeout) {
this.object;
this.get = function (){
if(this.object === undefined) {
return this.object = Object.create({}); //Create your object
} else {
return this.object;
}
}
});
however i did notice some booboo's (Sorry always reminds me of Hook when i say that). First you do not need to alias the this keyword, were not working in an jQuery callback, even if we were you can bind your function etc.
Second and this is important. Your passing a $scope object into your service, this is very very bad. Not just for this reason but how can controllers share a single service object if it has a reference to a $scope? Your services should be a collection of single simple methods that have their input and output data. They work on that and continue. You can then chain them and pass them the specific data each method needs. They shouldn't be a monolithic object that has everything in hidden properties, think functional, a pipeline if you will.
I'm building a small two-language app with the use of angular-translate. I want to have a language switcher in every view (controller). I'm trying to figure out how to put the code responsible for language switching into every controller. The code looks like this:
var langSwitch = $Scope.setLang = function (langKey) {
$translate.use(langKey);
};
So far I've figured that I can create a factory that looks like this:
app.factory('langSwitch', function ($rootScope, $translate) {
var langSwitch = $rootScope.setLang = function (langKey) {
$translate.use(langKey);
};
return langSwitch;
});
and inject it into controllers in this maner:
app.controller('HomeCtrl', function (langSwitch) {
// normal controller code here
});
This works but 1) I'm using $rootScope and I have a feeling this is bad practice & 2) jsHint screams that "langSwitch" is not defined. Maybe there is a simpler way to make the function global without putting it into every controller?
I'm still pretty new to Angular so don't scream at me :) Thanks.
edit
My view:
<button ng-click="setLang('en_GB')">English</button>
<button ng-click="setLang('pl_PL')">Polish</button>
Although you got the idea, you overcomplicated things a bit. You could declare the service as follows:
app.service('langSwitch', function ($translate) {
this.setLang = function (langKey) {
$translate.use(langKey);
};
});
And then inject langSwitch in the controller responsible for lang switching, as you already did. No need to inject $rootScope in the service.
You don't need $rootScope indeed unless you need to process some global events in your application. All services and factories in angular are singletons by default. That means once it created, it will be passed as the same instance in every place it is declared as a dependency. So if you want to share data and functionality between different controllers - the services will suit fine. You can change your factory code to:
app.factory('langSwitch', function($translate) {
return {
setLang: function(langKey) {
$trasnlate.use(langKey);
};
};
});
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;
I have a function that I will attach to my scope like this. It's attached to the scope as I use this function in my HTML pages Presently I am doing this in more than one controller. Note that my controllers are all top level controllers so I cannot really put this in a higher up controller and have it inherited.
$scope.isNotString = function (str) {
return (typeof str !== "string");
}
I asked how I could share this functionality and was given the following
example:
app.service('myService',function(){
this.sharedFunction = function() {
//do some stuff
};
});
myCntrl1($scope,myService) {
$scope.doSomething = function() {
myService.sharedFunction();
}
}
myCntrl2($scope,myService) {
$scope.doSomething = function() {
myService.sharedFunction();
}
}
Is there a way that I could more directly share it by passing
in $scope to the service and in that way eliminating the need for:
$scope.doSomething = function() {
myService.sharedFunction();
}
In each controller.
You can attach the function to the parent (root) scope, but using a service is the preferred way of sharing code between controllers.
You could call myService.init($scope) in the controller and that function could append properties to the scope but more likely you would want to use a parent controller from which you inherit.
You either declare a shared function in a top level "controller" or in a "service" like you mentioned in your example. There's no other better way so far.
You can assign the scope to a global variable, that will expose that scope globally, but will behave the same way like "service", except you don't have to inject it like service, rather can call by globalVar.dosomething().