Related
I have a variable that I declared in rootscope to share among other controllers.
I also need to place more than one variable in the rootscope.
Syntactically, how would you do that.
This is my definition for declaring just one variable in rootscope.
(function (module)
{
angular.module("sample", []).run(["$rootScope", function($rootScope){$rootScope.TaxPercent = 30;}]);
.
.
.
}
$rootScope is an object, so just assign properties:
$rootScope.TaxPercent1 = 30;
$rootScope.TaxPercent2 = 40;
Or
$rootScope.TaxPercent = {percent1: 30, percent2: 40}; // SET
console.log($rootScope.TaxPercent.percent1); // GET
But NOT:
$rootScope = {percent1: 30, percent2: 40};
Because you'll overwrite all the previously data set to the object
In general $rootScope shouldn't be used to share variables for different and many reasons that have been explained before in articles like:
here
What will happen if you forget about your $rootScope.TaxPercent = 30; global variable and then you create a variable $scope.TaxPercent inside your controller? That would cause problems for sure.
Just providing an option here and suggesting the use of Angular Services
Here is a sample example of how Angular Service work
From the Angular FAQ page:
Of course, global state sucks and you should use $rootScope sparingly, like you would (hopefully) use with global variables in any language. In particular, don't use it for code, only data. If you're tempted to put a function on $rootScope, it's almost always better to put it in a service that can be injected where it's needed, and more easily tested.
I have some static config-like values that are used throughout the web app. Currently I am creating directives to put them into the $scope, but that seems awfully wasteful.
Is there a way for angular expressions to directly access some kind of "static" values defined either in some module or in the Javascript global scope?
First, consider if globals/statics are the way to go...
If so, define your statics as values in angular:
angular.module('globals', [])
.value('serverConfig', 'xyz')
.value('foo', { x:4, y: 5 })
// etc
You can use those globals from code with normal dependency injection; use a run function to place them in the $rootScope, so that they are also available in expressions:
angular.module(...)
.run(['$rootScope', 'serverConfig', 'foo', function($rootScope, serverConfig, foo) {
$rootScope.serverConfig = serverConfig;
$rootScope.foo = foo;
// etc
}]);
If you only need them available in expressions, just use the run() function.
You can use the constant in angularjs,it will be helpful.
module.constant(name, object);
or
module.value(name, object);
or
you can use localStorage in html5(It cannot store the object),for storing object use localStorageModule third party js
There seems to be no way to provide data to an Angular controller other than through attributes in the DOM handled by directives (of which ngInit is a handy example).
I'd like to provide other "constructor" data, e.g. objects with functions to my
$scope.
Background: We have an existing dashboard-style single page application,
where each widget manages a <div>, and widget-instance-specific data
is provided as an object along with support functions, etc.. This object data
doesn't fit nicely into DOM attributes or ngInit calls.
I can't really come up with a better way to it than to have a global hash, and use an instance-specific unique key. Before calling angular.bootstrap(domElement, ['myApp']), we set up all "constructor" parameters in this global hash under the key and then use
<div ng-init='readInitialValuesFromHash("uniqueKey")'>...</div>
where readInitialValuesFromHash gets all its data from
globalHash["uniqueKey"] and stores what it needs it in $scope (possibly
just the "uniqueKey").
(What seems like an alternative is to use a directive and jQuery.data(), but jQuery.data uses a global hash behind the scenes)
Of course I can hide the global data in a function, but fundamentally still use
a singleton/global variable. This "global hash and pass key as param to ng init
trick" just seems like such a hack...
Am I missing something? Is there a better way, given that the
widget-instance-specific data is actually more complicated than suitable for
inserting in the DOM via directives/attributes due to the legacy dashboard
framework?
Are there dangers when putting complicated objects in the $scope as long as they aren't referenced by directives, {{}} or $scope.$watch() calls?
Angular's front page says:
Add as much or as little of AngularJS to an existing page as you like
So in light of that, how should I proceed?
EDIT: Comments have asked to make my question more clear. As an example of a non-trivial constructor parameter, assume I want to give this myObj to the controller, prototypical inheritance, function and all:
var proto = {
p1: "p1",
pf: function() {
return "proto"
}
};
function MyObj(ost) {
this.ost = ost;
}
MyObj.prototype=proto;
var myObj = new MyObj("OST");
So I have myObj, and I have a string:
<div ng-app='myApp' ng-controller="MyCtrl">....</div>
I put the string in the DOM, and call angular.bootstrap().
How to I get the real myObj object into MyCtrl's $scope for this <div>, not a serialized/deserialized version/copy of it?
Services is what you are looking for.
You can create your own services and then specify them as dependencies to your components (controllers, directives, filters, services), so Angular's dependency injection will take care of the rest.
Points to keep in mind:
Services are application singletons. This means that there is only one instance of a given service per injector. Since Angular is lethally allergic to global state, it is possible to create multiple injectors, each with its own instance of a given service, but that is rarely needed, except in tests where this property is crucially important.
Services are instantiated lazily. This means that a service will be created only when it is needed for instantiation of a service or an application component that depends on it. In other words, Angular won't instantiate services unless they are requested directly or indirectly by the application.
Services (which are injectable through DI) are strongly preferred to global state (what isn't), because they are much more testable (e.g. easily mocked etc) and "safer" (e.g. against accidental conflicts).
Relevant links:
Understanding Angular Services
Managing Service Dependencies
Creating Angular Services
Injecting Services into Controllers
Testing Angular Services
About Angular Dependency Injection
Example:
Depending on your exact requirements, it might be better to create one service to hold all configuration data or create one service per widget. In the latter case, it would probably be a good idea to include all services in a module of their own and specify it as a dependency of your main module.
var services = angular.module('myApp.services', []);
services.factory('widget01Srv', function () {
var service = {};
service.config = {...};
/* Other widget01-specific data could go here,
* e.g. functionality (but not presentation-related stuff) */
service.doSomeSuperCoolStuff = function (someValue) {
/* Send `someValue` to the server, receive data, process data */
return somePrettyInterestingStuff;
}
...
return service;
}
services.factory('widget02Srv', function () {...}
...
var app = angular.module('myApp', ['myApp.services']);
app.directive('widget01', function ('widget01Srv') {
return function postLink(scope, elem, attrs) {
attrs.$set(someKey, widget01Srv.config.someKey);
elem.bind('click', function () {
widget01Srv.doSomeSuperCoolStuff(elem.val());
});
...
};
});
ExpertSystem's answer gave me the hint that I needed. A separate controller instance for each widget. Note how the constructorParameter (==myObj) gets inserted into the controller.
function creatWidgetInstance(name) {
....
var controllerName = name + 'Ctrl';
// myObj comes from the original question
var constructorParameter = myObj;
widgetApp.controller(controllerName, function($scope) {
$scope.string = constructorParameter;
});
....
newWidget = jQuery(...);
newWidget.attr('ng-controller', controllerName);
angular.bootstrap(newWidget[0], ['widgetApp']);
....
}
See it working in a plunker
Perhaps a more beautiful solution is with a separate service too, as in:
function creatWidgetInstance(name) {
....
var controllerName = name + 'Ctrl';
var serviceName = name + 'Service';
// myObj comes from the original question
var constructorParameter = myObj;
widgetApp.factory(serviceName, function () {
return { savedConstructorParameter: constructorParameter };
});
widgetApp.controller(controllerName,
[ '$scope', serviceName, function($scope, service) {
$scope.string = service.savedConstructorParameter;
}
]
);
....
newWidget = jQuery(...);
newWidget.attr('ng-controller', controllerName);
angular.bootstrap(newWidget[0], ['widgetApp']);
....
}
See this in a working Plunker
The answer to the question requires backtracking a few assumptions. I thought that the only way to setup $scopewas to do it on a controller. And so the question revolves around how to "provide data to an Angular controller other than through attributes in the DOM handled by directives".
That was misguided.
Instead, one can do:
var scope = $rootScope.$new();
// Actually setting scope.string=scope makes for a horrible example,
// but it follows the terminology from the rest of the post.
scope.string = myObj;
var element = $compile(jQuery('#widgetTemplate').html())(scope);
jQuery('#widgets').append(element);
See this Plunker for a working example.
I have a problem where i'm initialising a variable on the scope in a controller. Then it gets changed in another controller when a user logs in. This variable is used to control things such as the navigation bar and restricts access to parts of the site depending on the type of user, so its important that it holds its value. The problem with it is that the controller that initialises it, gets called again by angular some how and then resets the variable back to its initial value.
I assume this is not the correct way of declaring and initialising global variables, well its not really global, so my question is what is the correct way and is there any good examples around that work with the current version of angular?
You've got basically 2 options for "global" variables:
use a $rootScope http://docs.angularjs.org/api/ng.$rootScope
use a service http://docs.angularjs.org/guide/services
$rootScope is a parent of all scopes so values exposed there will be visible in all templates and controllers. Using the $rootScope is very easy as you can simply inject it into any controller and change values in this scope. It might be convenient but has all the problems of global variables.
Services are singletons that you can inject to any controller and expose their values in a controller's scope. Services, being singletons are still 'global' but you've got far better control over where those are used and exposed.
Using services is a bit more complex, but not that much, here is an example:
var myApp = angular.module('myApp',[]);
myApp.factory('UserService', function() {
return {
name : 'anonymous'
};
});
and then in a controller:
function MyCtrl($scope, UserService) {
$scope.name = UserService.name;
}
Here is the working jsFiddle: http://jsfiddle.net/pkozlowski_opensource/BRWPM/2/
If you just want to store a value, according to the Angular documentation on Providers, you should use the Value recipe:
var myApp = angular.module('myApp', []);
myApp.value('clientId', 'a12345654321x');
Then use it in a controller like this:
myApp.controller('DemoController', ['clientId', function DemoController(clientId) {
this.clientId = clientId;
}]);
The same thing can be achieved using a Provider, Factory, or Service since they are "just syntactic sugar on top of a provider recipe" but using Value will achieve what you want with minimal syntax.
The other option is to use $rootScope, but it's not really an option because you shouldn't use it for the same reasons you shouldn't use global variables in other languages. It's advised to be used sparingly.
Since all scopes inherit from $rootScope, if you have a variable $rootScope.data and someone forgets that data is already defined and creates $scope.data in a local scope you will run into problems.
If you want to modify this value and have it persist across all your controllers, use an object and modify the properties keeping in mind Javascript is pass by "copy of a reference":
myApp.value('clientId', { value: 'a12345654321x' });
myApp.controller('DemoController', ['clientId', function DemoController(clientId) {
this.clientId = clientId;
this.change = function(value) {
clientId.value = 'something else';
}
}];
JSFiddle example
Example of AngularJS "global variables" using $rootScope:
Controller 1 sets the global variable:
function MyCtrl1($scope, $rootScope) {
$rootScope.name = 'anonymous';
}
Controller 2 reads the global variable:
function MyCtrl2($scope, $rootScope) {
$scope.name2 = $rootScope.name;
}
Here is a working jsFiddle: http://jsfiddle.net/natefriedman/3XT3F/1/
In the interest of adding another idea to the wiki pool, but what about AngularJS' value and constant modules? I'm only just starting to use them myself, but it sounds to me like these are probably the best options here.
Note: as of the time of writing, Angular 1.3.7 is the latest stable, I believe these were added in 1.2.0, haven't confirmed this with the changelog though.
Depending on how many you need to define, you might want to create a separate file for them. But I generally define these just before my app's .config() block for easy access. Because these are still effectively modules, you'll need to rely on dependency injection to use them, but they are considered "global" to your app module.
For example:
angular.module('myApp', [])
.value('debug', true)
.constant('ENVIRONMENT', 'development')
.config({...})
Then inside any controller:
angular.module('myApp')
.controller('MainCtrl', function(debug, ENVIRONMENT), {
// here you can access `debug` and `ENVIRONMENT` as straight variables
})
From the initial question is actually sounds like static properties are required here anyway, either as mutable (value) or final (constant). It's more my personal opinion than anything else, but I find placing runtime configuration items on the $rootScope gets too messy, too quickly.
// app.js or break it up into seperate files
// whatever structure is your flavor
angular.module('myApp', [])
.constant('CONFIG', {
'APP_NAME' : 'My Awesome App',
'APP_VERSION' : '0.0.0',
'GOOGLE_ANALYTICS_ID' : '',
'BASE_URL' : '',
'SYSTEM_LANGUAGE' : ''
})
.controller('GlobalVarController', ['$scope', 'CONFIG', function($scope, CONFIG) {
// If you wish to show the CONFIG vars in the console:
console.log(CONFIG);
// And your CONFIG vars in .constant will be passed to the HTML doc with this:
$scope.config = CONFIG;
}]);
In your HTML:
<span ng-controller="GlobalVarController">{{config.APP_NAME}} | v{{config.APP_VERSION}}</span>
localStorage.username = 'blah'
If you're guaranteed to be on a modern browser. Though know your values will all be turned into strings.
Also has the handy benefit of being cached between reloads.
Please correct me if I'm wrong, but when Angular 2.0 is released I do not believe$rootScope will be around. My conjecture is based on the fact that $scope is being removed as well. Obviously controllers, will still exist, just not in the ng-controller fashion.Think of injecting controllers into directives instead. As the release comes imminent, it will be best to use services as global variables if you want an easier time to switch from verison 1.X to 2.0.
You can also use the environment variable $window so that a global variable declare outside a controller can be checked inside a $watch
var initWatch = function($scope,$window){
$scope.$watch(function(scope) { return $window.globalVar },
function(newValue) {
$scope.updateDisplayedVar(newValue);
});
}
Becareful, the digest cycle is longer with these global values, so it is not always real-timed updated. I need to investigate on that digest time with this configuration.
I just found another method by mistake :
What I did was to declare a var db = null above app declaration and then modified it in the app.js then when I accessed it in the controller.js
I was able to access it without any problem.There might be some issues with this method which I'm not aware of but It's a good solution I guess.
Try this, you will not force to inject $rootScope in controller.
app.run(function($rootScope) {
$rootScope.Currency = 'USD';
});
You can only use it in run block because config block will not provide you to use service of $rootScope.
It's actually pretty easy. (If you're using Angular 2+ anyway.)
Simply add
declare var myGlobalVarName;
Somewhere in the top of your component file (such as after the "import" statements), and you'll be able to access "myGlobalVarName" anywhere inside your component.
You can also do something like this ..
function MyCtrl1($scope) {
$rootScope.$root.name = 'anonymous';
}
function MyCtrl2($scope) {
var name = $rootScope.$root.name;
}
I have a problem where i'm initialising a variable on the scope in a controller. Then it gets changed in another controller when a user logs in. This variable is used to control things such as the navigation bar and restricts access to parts of the site depending on the type of user, so its important that it holds its value. The problem with it is that the controller that initialises it, gets called again by angular some how and then resets the variable back to its initial value.
I assume this is not the correct way of declaring and initialising global variables, well its not really global, so my question is what is the correct way and is there any good examples around that work with the current version of angular?
You've got basically 2 options for "global" variables:
use a $rootScope http://docs.angularjs.org/api/ng.$rootScope
use a service http://docs.angularjs.org/guide/services
$rootScope is a parent of all scopes so values exposed there will be visible in all templates and controllers. Using the $rootScope is very easy as you can simply inject it into any controller and change values in this scope. It might be convenient but has all the problems of global variables.
Services are singletons that you can inject to any controller and expose their values in a controller's scope. Services, being singletons are still 'global' but you've got far better control over where those are used and exposed.
Using services is a bit more complex, but not that much, here is an example:
var myApp = angular.module('myApp',[]);
myApp.factory('UserService', function() {
return {
name : 'anonymous'
};
});
and then in a controller:
function MyCtrl($scope, UserService) {
$scope.name = UserService.name;
}
Here is the working jsFiddle: http://jsfiddle.net/pkozlowski_opensource/BRWPM/2/
If you just want to store a value, according to the Angular documentation on Providers, you should use the Value recipe:
var myApp = angular.module('myApp', []);
myApp.value('clientId', 'a12345654321x');
Then use it in a controller like this:
myApp.controller('DemoController', ['clientId', function DemoController(clientId) {
this.clientId = clientId;
}]);
The same thing can be achieved using a Provider, Factory, or Service since they are "just syntactic sugar on top of a provider recipe" but using Value will achieve what you want with minimal syntax.
The other option is to use $rootScope, but it's not really an option because you shouldn't use it for the same reasons you shouldn't use global variables in other languages. It's advised to be used sparingly.
Since all scopes inherit from $rootScope, if you have a variable $rootScope.data and someone forgets that data is already defined and creates $scope.data in a local scope you will run into problems.
If you want to modify this value and have it persist across all your controllers, use an object and modify the properties keeping in mind Javascript is pass by "copy of a reference":
myApp.value('clientId', { value: 'a12345654321x' });
myApp.controller('DemoController', ['clientId', function DemoController(clientId) {
this.clientId = clientId;
this.change = function(value) {
clientId.value = 'something else';
}
}];
JSFiddle example
Example of AngularJS "global variables" using $rootScope:
Controller 1 sets the global variable:
function MyCtrl1($scope, $rootScope) {
$rootScope.name = 'anonymous';
}
Controller 2 reads the global variable:
function MyCtrl2($scope, $rootScope) {
$scope.name2 = $rootScope.name;
}
Here is a working jsFiddle: http://jsfiddle.net/natefriedman/3XT3F/1/
In the interest of adding another idea to the wiki pool, but what about AngularJS' value and constant modules? I'm only just starting to use them myself, but it sounds to me like these are probably the best options here.
Note: as of the time of writing, Angular 1.3.7 is the latest stable, I believe these were added in 1.2.0, haven't confirmed this with the changelog though.
Depending on how many you need to define, you might want to create a separate file for them. But I generally define these just before my app's .config() block for easy access. Because these are still effectively modules, you'll need to rely on dependency injection to use them, but they are considered "global" to your app module.
For example:
angular.module('myApp', [])
.value('debug', true)
.constant('ENVIRONMENT', 'development')
.config({...})
Then inside any controller:
angular.module('myApp')
.controller('MainCtrl', function(debug, ENVIRONMENT), {
// here you can access `debug` and `ENVIRONMENT` as straight variables
})
From the initial question is actually sounds like static properties are required here anyway, either as mutable (value) or final (constant). It's more my personal opinion than anything else, but I find placing runtime configuration items on the $rootScope gets too messy, too quickly.
// app.js or break it up into seperate files
// whatever structure is your flavor
angular.module('myApp', [])
.constant('CONFIG', {
'APP_NAME' : 'My Awesome App',
'APP_VERSION' : '0.0.0',
'GOOGLE_ANALYTICS_ID' : '',
'BASE_URL' : '',
'SYSTEM_LANGUAGE' : ''
})
.controller('GlobalVarController', ['$scope', 'CONFIG', function($scope, CONFIG) {
// If you wish to show the CONFIG vars in the console:
console.log(CONFIG);
// And your CONFIG vars in .constant will be passed to the HTML doc with this:
$scope.config = CONFIG;
}]);
In your HTML:
<span ng-controller="GlobalVarController">{{config.APP_NAME}} | v{{config.APP_VERSION}}</span>
localStorage.username = 'blah'
If you're guaranteed to be on a modern browser. Though know your values will all be turned into strings.
Also has the handy benefit of being cached between reloads.
Please correct me if I'm wrong, but when Angular 2.0 is released I do not believe$rootScope will be around. My conjecture is based on the fact that $scope is being removed as well. Obviously controllers, will still exist, just not in the ng-controller fashion.Think of injecting controllers into directives instead. As the release comes imminent, it will be best to use services as global variables if you want an easier time to switch from verison 1.X to 2.0.
You can also use the environment variable $window so that a global variable declare outside a controller can be checked inside a $watch
var initWatch = function($scope,$window){
$scope.$watch(function(scope) { return $window.globalVar },
function(newValue) {
$scope.updateDisplayedVar(newValue);
});
}
Becareful, the digest cycle is longer with these global values, so it is not always real-timed updated. I need to investigate on that digest time with this configuration.
I just found another method by mistake :
What I did was to declare a var db = null above app declaration and then modified it in the app.js then when I accessed it in the controller.js
I was able to access it without any problem.There might be some issues with this method which I'm not aware of but It's a good solution I guess.
Try this, you will not force to inject $rootScope in controller.
app.run(function($rootScope) {
$rootScope.Currency = 'USD';
});
You can only use it in run block because config block will not provide you to use service of $rootScope.
It's actually pretty easy. (If you're using Angular 2+ anyway.)
Simply add
declare var myGlobalVarName;
Somewhere in the top of your component file (such as after the "import" statements), and you'll be able to access "myGlobalVarName" anywhere inside your component.
You can also do something like this ..
function MyCtrl1($scope) {
$rootScope.$root.name = 'anonymous';
}
function MyCtrl2($scope) {
var name = $rootScope.$root.name;
}