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!
Related
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 = '';
});
I'm trying to update a variable in my controller every time a variable is updated in my service. I'm using the $scope.$watch(), but unfortunately only the last change is being effected. Here is the code that I used. Does anyone know what is wrong with this?
Service:
rApp.factory('pService', ['$http', '$rootScope', '$sanitize',
function ($http, $rootScope, $sanitize) {
var pService = {};
//Some other code
pService.Update=function(status)
{
if(status.LastItemId!=undefined)
{
pService.disItemId = status.LastItemId;
}
}
//Some other code
return pService;
});
Controller:
rApp.controller('dController', ['$scope','$rootScope' 'pService' ,dispenseController]);
function dController($scope,$rootScope, pService) {
$scope.$watch(function () { return pService.disItemId }, function (newVal, oldVal) {
if (newVal != oldVal) {
$scope.lastItemId = pService.disItemId;
}
})
});
In your specific case :
You don't need to use $watch. Actually you don't have any use of $watch in standard angular application.
Just do this in your controller :
$scope.lastItem = pService;
And then use the var like this :
$scope.lastItem.disItemId;
This will always point to the updated disItemId.
Problem
If you bind your var like this :
Service
[...]
service.serviceVar = 1;
return service
[...]
This will create a "1" var with a reference.
Controller
[...]
$scope.myvar = Service.serviceVar;
[...]
This will bind $scope.myvar to the "1" reference.
If you do this in your service or in an other controller :
service.serviceVar = 2;
You will create a new var "2" with a new reference and you will assign this reference to service.serviceVar. Badly all your old references to the old 1 var will not update.
Solution
To avoid that do it like this :
Service
[...]
service.servicevar = {};
service.servicevar.value = 1;
return service
[...]
You create an object with a new reference and assign it to servicevar. You create a var "1" and assign it to servicevar.value.
Controller
[...]
$scope.myvar = Service.servicevar;
[...]
You assign the servicevar reference to your scope var.
view
{{myvar.value}}
You can use the value by using the property of your var.
Updating the var doing this :
service.servicevar.value = 2;
You will create a new var "2" with a new reference and replace the old reference by this one.
BUT this time you keep all your references to servicevar in your controllers.
I hope i was clear and it solve you issue.
EDIT
My answer was only a partial answer on this question.
Here is the update plunker
First you had a type on the controller definition. You're closing the braces too early. Here is the good definition
app.controller('MainCtrl',['$scope','pService',
function MainCtrl($scope,pService) {
$scope.serviceVar=pService.myVar;
$scope.val = $scope.serviceVar;
}]);
Then you were using windows.interval in angular you need to use the $interval service instead
Example :
app.factory('pService',function($interval){
var pService={};
pService.myVar={};
pService.myVar.count=1;
$interval(function(){
pService.myVar.count++;
},1000);
return pService;
});
Then my problem was occuring. You were binding the value instead of the object in your controller. It looks like this now :
$scope.val = $scope.serviceVar;
Hope it helped you
I have the following controllers:
HeaderCtrl, NewsFeedCtrl, MainCtrl
MainCtrl contains both the other two controllers, which are in the same level.
I'm defining an object in authenticationService and update its value in MainCtrl and I update it frequently in NewsFeedCtrl and I want to display its value in the HTML page controlled by HeaderCtrl.
when I use this line in my HeaderCtrl:
$scope.unreadNum=authenticationService.notificationList.length;
and then I use data binding in my HTML page to display its value:
{{unreadNum}}
I only get the initial value I inserted in authenticationService, not the one after the update in the other controllers.
it seems that my HeaderCtrl is defining all his scope objects only one time and then there's no more use for the Ctrl, but I still want his HTML page to be updated after the update in object values in other controllers.
to sum it up: the value of the object I want is stored in one of my services, and I am unable to display it in my HTML page because I can't seem bind it correctly.
You can send messages between the controllers using a service. The service looks something like this...
aModule.factory('messageService', function ($rootScope) {
var sharedService = {};
sharedService.message = {};
sharedService.prepForBroadcast = function(msg) {
this.message = msg;
this.broadcastItem();
};
sharedService.broadcastItem = function () {
$rootScope.$broadcast('handleBroadcast');
};
return sharedService;
});
In the controller that is sending the message, inject this service...
aModule.controller("sendingController", function ($scope, messageService) {
Then add a method that will broadcast the change to any controller that is listening...
$scope.sendMessage = function (someObject) {
messageService.prepForBroadcast(someObject);
},
In any controller that wants to receive the message, inject the service, and add a handler like this to do something with the message...
$scope.$on('handleBroadcast', function() {
//update what you will..
$scope.something = messageService.message;
});
I am trying to create a service when I can set my formSubmit.
For example. In controller A I call "service.setFormSubmit(doThis(obj))" and in controller B I call "service.getFormSubmit()". Where it will execute the function doThis(obj) in controller B.
UPDATE - Re-formulated question.
I have 1 view where I want to edit or create a category. This means I need a dynamic ng-submit. I want to to this in the controller. So like this:
$scope.editCategory = function(obj) {
$scope.formSubmit = 'editCategory'
}
And on the create I want to change the formSubmit var to createCategory of course.
So I can make a difference between creating and editing the category.
Is this possible? Would be really nice if someone has a way to do this..!
Thanks in advance!
Instead of passing around strings which need to be eval'ed, use the service to share functionality directly between controllers.
The service can be dirt-simple:
.factory('MyService', function(){
var service = {};
return service;
});
Once injected and assigned to scope variables in both controllers you have an intermediary unit which can act as a modifiable channel for cross-controller collaboration.
.controller('FirstController', function($scope, MyService){
$scope.service = MyService;
})
.controller('SecondController', function($scope, MyService){
$scope.service = MyService;
$scope.service.create = function(obj){
console.log('Creating');
}
$scope.service.edit = function(obj){
console.log('Editing');
}
})
From the scope of FirstController, you can now call the function also available on the scope of SecondController:
<div ng-controller="FirstController">
<input type="checkbox" ng-model="button.type"> Toggle create/edit<br/>
<button ng-if="button.type" ng-click="service.create(obj)">Create</button>
<button ng-if="!button.type" ng-click="service.edit(obj)">Edit</button>
</div>
Demo
If you aren't reloading the page you can create an encapsulated variable in your service. Your set call would assign the value passed to that variable and your get call would return that variable to the caller.
One way I have achieved passing the data is to submit the form using the service and return a Json result to the service. Store the Json object in the encapsulated variable on the return and then pass a success or failure to the controller. When successful, let the controller redirect the view which will redirect using angular routing and ng-view. Once the new view, along with the new controller is loaded into the page, you can call the variable in your service to retrieve the data on the next controller.
Example Code:
app.factory('service', function ($q, $http) {
var savedData;
return {
loadData: function() {
return data;
},
search: function (parameters) {
var searchURL = '/MVCController/Search?parameter1=' + parameters.one +
'¶meter2=' + parameters.two;
var deferred = $q.defer();
$http.get(searchURL).success(function (data) {
savedData = data;
deferred.resolve(true);
}).error(function(data) {
data = 'An error occurred while searching: ' + data;
savedData = data //(if you want to save the error)
deferred.reject(data);
});
return deferred.promise;
}
}
});
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>