Angular Variable/Function Global Scope - angularjs

I have a variable that i use almost everywhere in my app, in many different controllers.
I'm looking for the best possible way of setting that variable knowing that :
-The variable will need to get updated from a controller and resulting in an update on the whole app ( other controllers mainly).
-Some of those function are instance of an object with function in itself that have a callback , does that cause any issue?
So far there seems to be 2 way of doing that :
rootScope but that rarely advised apparently:
myApp.run(function ($rootScope) {
$rootScope.var = "string";
});
And building a custom directive ( but what about setting it's variable ?)
angular.module('myApp').factory('test', function() {
return {
test : 'string'
};
});
Can anyone point me in the right direction and help me choose betweem the two?

I would recommend using a service or factory save that value in someService.variable and the broadcast n event that this value has been changed. I am attaching sample code. It may contain some syntactical error but I want to give you an idea
angular.module("myapp")
.controller('myCtrl', function($scope, $rootScope, myService) {
$scope.change = function() {
myService.var = 'abc';
$rootScope.broadcast('varChanged');
};
})
.controller('myOtherCtrl', function($scope, $rootScope, myService) {
$rootScope.on('varChanged', function(e) {
use(myService.var);
});
})
.service('mySerice', function() {
this.var = '';
});

Related

How do I use services in Angular to store variables?

How do I use services to store variables so that I can access it in previous controller on pressing back button?
For example:
.controller('myCtrl', function($scope, myservice) {
console.log(myservice.myvar.myval)
// output: undefined pressing back button
});
.controller('myCtrl2', function($scope, myservice) {
myservice.myvar.myval = "This I want in controller myCtrl on pressing back button"
console.log(myservice.myvar.myval)
// outputs the value
});
If services are not the best approach and I should use $rootScopethen will it do the job.
P.S angular.module and myservice are defined.
You need to use getter-setter for this.
For Example:
angular.module('MyModule', [])
.service('myservice', function () {
this.myval = "value";
this.getValue = function () { return this.myval }; //getter
this.setValue = function (val) { this.myval = val }; //setter
})
.controller('myCtrl', function($scope, myservice) {
console.log(myservice.getValue());
// get the value here
});
.controller('myCtrl2', function($scope, myservice) {
myservice.setValue("This I want in controller myCtrl on pressing back button");
// Set the value here
});
This is for the reference only. Please don't copy-paste and run code.
Angular services are singletons and can be freely shared among components, which have access to dependency injection. You can define a closure with a module pattern of getters and setters or directly attach properties to the object you intend to return.
I see 4 options, you can store it in a service, in $rootScope, in localStorage for persistent storage, and in sessionStorage to store datas only during the application lifetime. There's btw a nice module that wrap localStorage and sessionStorage, and make its use simpler :
http://ngmodules.org/modules/ngStorage

How to get data from scope by rootscope

I got an app.run where i get my data from an webintent. In the app.run i'm not allowed to use $scope so i use $rootscope.Now my $scope got $scope.sendURL(Object) and I want to make this call inside my app.run.
I searched a lot on stackoverflow and google and I came acros $broadcast and $emit. But i can't figure out if that is right thing to do. Does somebody know what i should try?
edit:
I need to call the $scope.sendURL inside app.run and i have no idea how to do it. I searched on stackoverflow and came acros $broadcast and $emitbut i'm not use if i need to use them.
Move the logic into a factory and use the factory in run and in your controller:
var app = angular.module('myApp', []);
app.run(function (myFactory) {
var data = {};
myFactory.sendURL(data);
});
app.controller('MyController', function ($scope, myFactory) {
$scope.sendURL = myFactory.sendURL;
});
app.factory('myFactory', function ($http) {
return { sendURL : sendURL };
function sendURL(data) {
// Put the logic here
console.log(data);
}
});

angular set a variable accessible to any scope

I am creating an app with Angular. There is a form in the app for a user to enter a string. When a user enters a string this calls a function using ng-submit:
ng-model="input1" ng-submit="update()"
Within the update function I can then access the value of input1 using
$scope.input1
What I would like to do is allow that value to be accessible outside of just the scope of the update function, so that it can be called by anything else within the angular app.
Ideally, I would like to be able to update thing1:
var app = angular.module('app1, []);
var thing1 = "value from input"
I've been through the documentation and am thinking maybe what I need to do is set an app.value, so:
var app = angular.module('app1, []);
app.value('thing1', "value from input")
Then in the submit function set the value of thing1 so that it updates the app.value, but that isn't working.
I'm not sure how I can do this. Any thoughts?
Define a service or value (as you surmised). This is the angular-ish way to do what you desire.
.service( 'appService', function(){
var srvc = {};
srvc.input1 = 'foo'
return srvc
}
.value( 'input1', { data: 'foo' })
If you use a value, then you need to use an object as strings and numbers are immutable. You might consider a values value where you store multiple values if you go that route.
Then you inject that where you need access to it:
.controller( 'SomeController', function( $scope, appService ){
$scope.doSomething = function(){ appService.input1 = 'bar' }
})
.controller( 'AnotherController', function ($scope, $q, appService ){
$scope.myInput = appService.input1
})
If anyone is interested, I created a service as follows:
app.service('thing', function(){
var string1 = '123'
return{
getString: function(){
return string1;
}
setString: function(value){
string1 = value;
}
Then within the controller I add the thing service:
app.controller('myController', function(thing){
controller code
});
I can put a value into the 'thing' service from any scope by using:
thing.setString($scope.valueIwantToSet)
Or get a value from any scope by using:
$scope.variableIwantToUse = thing.getString();
I far as I undestant you need to create something like a global variable. Try to use $rootScope. This is a parent scope for all the other scopes. You can inject and access it in any controller, so you will be able to read/write your value. Here is the example.
function Controller1($scope, $rootScope) {
$rootScope.someVar = "Some value";
}
function Controller2($scope, $rootScope) {
$rootScope.someVar2 = $rootScope.someVar;
$rootScope.someVar = "Some new value";
}
You just inject the $rootScope into some controller, set the value and then inject this into the other and just read the value.
Hope this helps!

AngularJS push of undefined

Cannot call method 'push' of undefined
I receive that error when my AngularJS runs the following:
$scope.ok = function () {
$modalInstance.close();
$scope.key.push({ title: '', gps:'', desc:''});
};
I declared my $scope.key = []; right after my .controller as I need to be able to use the $scope.key in other parts of the application. Could someone please point out where I should be declaring this?
$scope.ok is my Save Button which pulls the data from my input fields and $scope.plotmarkers is what I am using to pull the data from the inputs that have been pushed.
app.controller('MenuSideController', ['$scope','$modal','$log', function($scope, $modal, $log) {
$scope.key = [];
$scope.createmarker = function () {
var modalInstance = $modal.open({
templateUrl: 'template/modal-add-marker.html',
controller: ModalInstanceCtrl,
resolve: {}
});
modalInstance.result.then(function (selectedItem) {
}, function () {
$log.info('Modal dismissed at: ' + new Date());
});
};
var ModalInstanceCtrl = function ($scope, $modalInstance) {
$scope.ok = function () {
$modalInstance.close();
$scope.key.push({ title: '', gps:'', desc:''});
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
};
$scope.plotmarkers = function() {
console.log($scope.key);
};
}]);
Don't forget to pass a scope to $modal.open! If you don't, it will default to the root scope, which is not a child of the controller's scope, so key is not defined on it or its parents. You can use { scope: $scope.$new(), ... } as a parameter of $model.open to pass it a child of the controller's scope. See the docs for details. Good luck!
I think you are using $modal a bit improperly.
So you have two controller here - one for the application logic and one for the modal window itself. According to best practice you shouldn't interact between different controllers directly (the case with parent-child directive is exclusion but honestly speaking it not direct interaction - rather your linker function used the controller from parent directive). Instead of interaction between controller in general we should use services. It is just additional note.
What is related to your question - you have two things here to keep in mind:
if you want to pass the information from the controller to the modal window you should use resolve property which actually specifies multiple functions which are called to get the data and then injected to the modal windows controller as a function parameter. This way you can pass some data from the main controller.
if you need to pass the result back you should use result property of the modal instance which is the promise (by using your $modalInstance.result.then(function(result){ ... }); ) To pass this object from the modal you can close it with the result as a parameter like this: $modalInstance.close(result);
Hope this helps. For further details you can look at the documentation for the $modal: Angular Bootstrap

Update scope value when service data is changed

I have the following service in my app:
uaInProgressApp.factory('uaProgressService',
function(uaApiInterface, $timeout, $rootScope){
var factory = {};
factory.taskResource = uaApiInterface.taskResource()
factory.taskList = [];
factory.cron = undefined;
factory.updateTaskList = function() {
factory.taskResource.query(function(data){
factory.taskList = data;
$rootScope.$digest
console.log(factory.taskList);
});
factory.cron = $timeout(factory.updateTaskList, 5000);
}
factory.startCron = function () {
factory.cron = $timeout(factory.updateTaskList, 5000);
}
factory.stopCron = function (){
$timeout.cancel(factory.cron);
}
return factory;
});
Then I use it in a controller like this:
uaInProgressApp.controller('ua.InProgressController',
function ($scope, $rootScope, $routeParams, uaContext, uaProgressService) {
uaContext.getSession().then(function(){
uaContext.appName.set('Testing house');
uaContext.subAppName.set('In progress');
uaProgressService.startCron();
$scope.taskList = uaProgressService.taskList;
});
}
);
So basically my service update factory.taskList every 5 seconds and I linked this factory.taskList to $scope.taskList. I then tried different methods like $apply, $digest but changes on factory.taskList are not reflected in my controller and view $scope.taskList.
It remains empty in my template. Do you know how I can propagate these changes ?
While using $watch may solve the problem, it is not the most efficient solution. You might want to change the way you are storing the data in the service.
The problem is that you are replacing the memory location that your taskList is associated to every time you assign it a new value while the scope is stuck pointing to the old location. You can see this happening in this plunk.
Take a heap snapshots with Chrome when you first load the plunk and, after you click the button, you will see that the memory location the scope points to is never updated while the list points to a different memory location.
You can easily fix this by having your service hold an object that contains the variable that may change (something like data:{task:[], x:[], z:[]}). In this case "data" should never be changed but any of its members may be changed whenever you need to. You then pass this data variable to the scope and, as long as you don't override it by trying to assign "data" to something else, whenever a field inside data changes the scope will know about it and will update correctly.
This plunk shows the same example running using the fix suggested above. No need to use any watchers in this situation and if it ever happens that something is not updated on the view you know that all you need to do is run a scope $apply to update the view.
This way you eliminate the need for watchers that frequently compare variables for changes and the ugly setup involved in cases when you need to watch many variables. The only issue with this approach is that on your view (html) you will have "data." prefixing everything where you used to just have the variable name.
Angular (unlike Ember and some other frameworks), does not provide special wrapped objects which semi-magically stay in sync. The objects you are manipulating are plain javascript objects and just like saying var a = b; does not link the variables a and b, saying $scope.taskList = uaProgressService.taskList does not link those two values.
For this kind of link-ing, angular provides $watch on $scope. You can watch the value of the uaProgressService.taskList and update the value on $scope when it changes:
$scope.$watch(function () { return uaProgressService.taskList }, function (newVal, oldVal) {
if (typeof newVal !== 'undefined') {
$scope.taskList = uaProgressService.taskList;
}
});
The first expression passed to the $watch function is executed on every $digest loop and the second argument is the function which is invoked with the new and the old value.
I'm not sure if thats help but what I am doing is bind the function to $scope.value. For example
angular
.module("testApp", [])
.service("myDataService", function(){
this.dataContainer = {
valA : "car",
valB : "bike"
}
})
.controller("testCtrl", [
"$scope",
"myDataService",
function($scope, myDataService){
$scope.data = function(){
return myDataService.dataContainer;
};
}]);
Then I just bind it in DOM as
<li ng-repeat="(key,value) in data() "></li>
This way you can avoid to using $watch in your code.
No $watch or etc. is required. You can simply define the following
uaInProgressApp.controller('ua.InProgressController',
function ($scope, $rootScope, $routeParams, uaContext, uaProgressService) {
uaContext.getSession().then(function(){
uaContext.appName.set('Testing house');
uaContext.subAppName.set('In progress');
uaProgressService.startCron();
});
$scope.getTaskList = function() {
return uaProgressService.taskList;
};
});
Because the function getTaskList belongs to $scope its return value will be evaluated (and updated) on every change of uaProgressService.taskList
Lightweight alternative is that during controller initialization you subscribe to a notifier pattern set up in the service.
Something like:
app.controller('YourCtrl'['yourSvc', function(yourSvc){
yourSvc.awaitUpdate('YourCtrl',function(){
$scope.someValue = yourSvc.someValue;
});
}]);
And the service has something like:
app.service('yourSvc', ['$http',function($http){
var self = this;
self.notificationSubscribers={};
self.awaitUpdate=function(key,callback){
self.notificationSubscribers[key]=callback;
};
self.notifySubscribers=function(){
angular.forEach(self.notificationSubscribers,
function(callback,key){
callback();
});
};
$http.get('someUrl').then(
function(response){
self.importantData=response.data;
self.notifySubscribers();
}
);
}]);
This can let you fine tune more carefully when your controllers refresh from a service.
Like Gabriel Piacenti said, no watches are needed if you wrap the changing data into an object.
BUT for updating the changed service data in the scope correctly, it is important that the scope value of the controller that uses the service data does not point directly to the changing data (field). Instead the scope value must point to the object that wraps the changing data.
The following code should explain this more clear. In my example i use an NLS Service for translating. The NLS Tokens are getting updated via http.
The Service:
app.factory('nlsService', ['$http', function($http) {
var data = {
get: {
ressources : "gdc.ressources",
maintenance : "gdc.mm.maintenance",
prewarning : "gdc.mobMaint.prewarning",
}
};
// ... asynchron change the data.get = ajaxResult.data...
return data;
}]);
Controller and scope expression
app.controller('MenuCtrl', function($scope, nlsService)
{
$scope.NLS = nlsService;
}
);
<div ng-controller="MenuCtrl">
<span class="navPanelLiItemText">{{NLS.get.maintenance}}</span>
</div>
The above code works, but first i wanted to access my NLS Tokens directly (see the following snippet) and here the values did not become updated.
app.controller('MenuCtrl', function($scope, nlsService)
{
$scope.NLS = nlsService.get;
}
);
<div ng-controller="MenuCtrl">
<span class="navPanelLiItemText">{{NLS.maintenance}}</span>
</div>

Resources