I'm looking for a pattern in order to have globals constant in my application. But not with a controller or a factory. (so not with app.constant() too)
I just want to set a variable but I didn't find something good.
I wanted to set this var in my rootScoop but without success.
with something like
myApp.run(function($rootScoop){
$rootScoop.global = {};
});
When I use that code, an arror occurs for nothing (transtateFilterProvider). When I delete this code, the translateService works,
I MUST have access in all html view, I don't want to always use a controller (useless in this case), I just want to set a global variable in rootScoop.
Thank you.
You are getting an error because it is :
$rootScope
And not
$rootScoop
Anyway, correct way to do this is to add a constant module to your app like :
angular.module('yourapp', []).constant('Constants', {
foo: 'bar'
});
But you'll have to call Constants in controllers.
If you use $rootScope, remember you will need to call $root.global in templates.
Related
I created a directive, I´m using in my template:
<data-input> </data-input>
In my directive (dataInput) I have the template (...data-input.html).
In that template the html-code says:
<input ng-change="dataChanged()" ... > ...
In the JavaScript it says:
scope.dataChanged= function(){ //change the data }
Works perfectly, but know I need to safe the changed data to a variable in my controller (that has a different scope of course).
The template, where I put the <data-input> </data-input> belongs to the final scope, I´m trying to reach.
I tried it via parameter, but it didnt work.
Thanks
Here are your options:
You can nest controllers if possible. This will create scope inheritance and you will be able to share variables of the parent.
You can use $rootscope from both the controllers to share data. Your dataChanged function can save anything to the $rootscope
You can use angular.element to get the scope of any element. angular.element('data-input').scope() returns it's cope.
This is not recommended. But there are circumstances in which people use global space to communicate between angular and non-angular code. But I don't think this is your case.
You can use Angular Watches to see changes is some variable, like this
eg:
$scope.$watch('age + name', function () {
//called when variables 'name' or 'age' changed
//Or you can use just 'age'
});
Does anyone know the best way to create and access a global constant is AngularJS?
I want to be able to doe something like:
app.constant('baseURL','http://www.baseurl.com');
and access it from any factory or controller.
Going for constant would be one of the better way as it has ability that we can access it inside the config level or in provider, so that we can take use of them to carry the config level settings.
For usage you could inject the same way as we do inject for service/factory or consider any dependency.
For making it more better you could use Object inside the your app.constant, so that it can carry multiple settings in it.
Constant
app.constant('configSettings', {
'baseUrl': 'http://www.baseurl.com',
'someElseSetting': 'settingValue'
//other setting will also be there.
});
Controller
app.controller('myCtrl', function($scope, configSettings){
//console.log(configSettings.baseUrl);
//you could use base URL here
});
When creating an Angular Module one could essentially add global arrays or objects to the module. Like so..
var myApp = angular.module('myApp', ['myModule']);
myApp.run(function()
{
});
angular.module('myModule', [])
.run(function()
{
// global to module
var thisModule = angular.module('myModule');
thisModule.globalArray = [];
thisModule.globalObject = {};
});
So here's the question(s). Would it be a bad idea to do something like this? Is there anything in the documentation that recommends not doing this? And if so, why would or wouldn't you recommend not doing this?
Demo:
http://jsfiddle.net/hrpvkmaj/8/
In general, Angular goes to great lengths to avoid global state. You can observe this in the dependency injection system that the framework is based on. To use a component, you must inject it as a parameter that is wired up behind the scenes. The framework also has a powerful scoping system that allows for nice and easy encapsulation. Relying on global variables works against these systems.
In particular, it would be a bad idea to do something exactly like your code example because it isn't how Angular was designed to be used. You shouldn't be adding your own properties to Angular's module object. At the very least, you should be injecting the $rootScope object and adding your global variables to that.
app.run(function($rootScope)
{
$rootScope.globalArray = [];
$rootScope.globalObject = {};
});
From the Angular documentation:
Every application has a single root scope. All other scopes are descendant scopes of the root scope.
If you went this route, you could inject $rootScope wherever you need to use those global values.
However, the Angular team discourages using $rootScope.
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.
There is another way of defining global values in Angular that is even preferable over using $rootScope: defining a value provider.
A value provider is the simplest kind of provider. It defines a single value that can be injected throughout your app.
You can define one like this:
app.value("myValue", {
someProp: 'Hello World'
});
Then you can inject the value wherever you need it like this:
app.controller("myController", function ($scope, myValue) {
$scope.someValue = myValue.someProp;
});
Here's a fiddle using a value provider to inject a global value.
In the end, any answer you get, including this one, will include some level of subjectivity. There are many ways to handle global values, but Angular provides some really convenient ways of using the framework to accomplish this.
With standard controller syntax in AngularJS, you can watch a variable like:
$scope.$watch(somethingToWatch, function() { alert('It changed!'); });
Using the controllerAs syntax, I want to react to this change in an active controller. What's the easiest way to do this?
More detail, if it helps. I have one controller in a side pane that controls the context of the application (user selection, start time, end time, etc.). So, if the user changes to a different context, the main view should react and update. I'm storing the context values in a factory and each controller is injecting that factory.
You can always use a watcher evaluator function, especially helpful to watch something on the controller instance or any object. You can actually return any variable for that matter.
var vm = this;
//Where vm is the cached controller instance.
$scope.$watch(function(){
return vm.propToWatch;
}, function() {
//Do something
}, true);//<-- turn on this if needed for deep watch
And there are also ways to use bound function to bind the this context.
$scope.$watch(angular.bind(this, function(){
return this.propToWatch;
//For a variable just return the variable here
}), listenerFn);
or even ES5 function.bind:
$scope.$watch((function(){
return this.propToWatch;
}).bind(this), listenerFn);
If you are in typescript world it gets more shorter.
$scope.$watch(()=> this.propToWatch, listenerFn);
Eventhough you can watch on the controller alias inside the controller ($scope.watch('ctrlAs.someProp'), it opens up couple of problems:
It predicts (or in other words pre-determines) the alias used for the controller in the view/route/directive/modal or anywhere the controller is used. It destroys the purpose of using controllerAs:'anyVMAlias' which is an important factor in readability too. It is easy to make typo and mistakes and maintenance headache too since using the controller you would need to know what name is defined inside the implementation.
When you unit test the controller (just the controller), you need to again test with the exact same alias defined inside the controller (Which can probably arguably an extra step if you are writing TDD), ideally should not need to when you test a controller.
Using a watcher providing watcher function against string always reduced some steps the angular $parse (which watch uses to create expression) internally takes to convert the string expression to watch function. It can be seen in the switch-case of the $parse implementation
How to set ng-controller as an expression from the $scope?
According to the documentation:
ngController – {expression} – Name of a globally accessible
constructor function or an expression that on the current scope
evaluates to a constructor function.
But how to evaluate scope expression as a controller for controllers that have been registered with module .controller?
For example:
Layout:
<div ng-controller="myExpr"></div>
JavaScript (define controller):
app.controller('myCtrl', ['$scope', '$timeout', function () { ... }];
JavaScript (parent scope):
$scope.myExpr = ...;
What should be in myExpr to use myCtrl as a controller via expression?
I've tried $controller('myCtrl')... not working...
P.S. If controller has been defined via globally accessible function.. it's possible to provide it as myExpr. But what to do if it has been defined so?
The expressions that ng-controller accept are a bit wierd. So you can do this by writing your controller slightly differently (but read below as for why you probably don't want to).
function myCtrl($scope) {
$scope.value = 'Stuff';
}
This is a controller and will work like normal for this case. Like in this example: http://jsbin.com/ubevel/2/edit
So why not do it?
First of all this is not a good way to define things from a testing perspective. Secondly, this allows you to set the controller dynamically on load but it won't allow you to change things after that. If you change the value of myExpr after the page has loaded the controller will not change.
So what to do?
I would highly suggest looking at using a service instead. Swap out your actions by supplying your outer controller with a service that you then change in the same manner you are now trying to change the inner controller. So something like: http://jsbin.com/ubevel/5/edit
This service can be swapped out on the fly, changing it will change the actions that are available in the scope.
You could also use an include, but this would result in duplicate html. I personalty am fine with this since I am against reusing html for two different types objects (sooner or later you want to change one but not the other and it becomes a mess). But a lot of people would object to that.
An extra note: There are probably nicer ways to do it with controllers, I probably haven't looked at all angles, but I just don't think controllers are the right tool for this case.