pass data between controllers (service) angular - angularjs

I'm creating a quiz and I want to add the answer the user gives to an array (myAnswers), when the quiz is finished, I redirect my user to the summary page, where he can see the correct answer and the answer he has given. Those are both different controllers. I tried experementing with a service, but this doesn't work out...
Can someone help me with this one please?
service
var lycheeServices = angular.module('lycheeControllers', [])
lycheeServices.service('myAnswerService', function () {
var myAnswers= [];
this.AddAnswer = function(number, a){
myAnswers[number-1] = a;
};
this.getAnswer = function(number){
return myAnswers[number-1];
};
});
controller quiz
lycheeControllers.controller('quizCtrl', ['$scope', '$http', 'myAnswerService',
function ($scope, $http, myAnswerService) {
$scope.checked = function (answer) {
myAnswerService.addAnswer(number, answer.answer);
}
controller summary
lycheeControllers.controller('summaryCtrl', ['$scope', '$http', 'myAnswerService', function ($scope, $http, myAnswerService) {
$scope.myAnswer = myAnswerService.getAnswer(number);
]

In your controller summary, when you retrieve a value using your myAnswerService.getAnswer(number);, it returns a new value and your $scope.myAnswer is a copy of the value.
If your answer is a value type, any changes on it will not affect the value in your service answer array.
If your answers array contains objects (reference type), then manipulating the properties of $scope.myAnswer will update the properties of the same object referenced by the answer inside the service's array. But if you replace it with another answer, your answer inside the answers array will not notice the change
I don't know what you're trying to achieve, but a sensible solution is you store the service object to the scope. Like this:
$scope.myAnswer = myAnswerService
Note: When you do redirects, ensure that you don't do full page refresh because your data will be cleared completely.

Related

AngularJS scope variable value change not reflecting across controllers

In my angularjs, i have two controllers and use the variable which is referred in UI. From UI I am calling method OpenPage of NumTwoController which updates value of variable1 and NumOneController tries to access updated value.
After I update the variable in NumTwoController, newly updated value is not reflecting in NumOneController and also in NumOne.html page.
myModule.controller('NumOneController', ['$rootScope', '$scope',
function ($rootScope, $scope) {
$scope.variable1 = 1; // initial value assigned
$scope.$on('NumOne', function (e, opt) {
var valueselected = $scope.variable1; //still 1 is assigned.trying to read the value assigned in NumTwoController
});
}
]);
myModule.controller('NumTwoController', ['$rootScope', '$scope',
function ($rootScope, $scope) {
$scope.variable1 = [];
$scope.OpenPage = function (menuItem, PageReload){
$scope.variable1 = 2;
});
}
]);
I tried adding $scope.$apply() but didnt succeed.
Guidance to reflect the updated value in the another controller is much appreciated
You cannot directly access the variable of scope of one controller to another.
You can make use broadcast and on functions or angular service for sharing the scope between two controllers.
check out this =>
Share data between AngularJS controllers
https://benohead.com/blog/2016/07/18/angularjs-sharing-data-controllers/

Utilising $scope.apply()

I have the following which works fine, drawing info from a RESTful api feed
app.controller('servicesController', ['$scope', '$location', '$http', '$interval',
function($scope, $location, $http, $interval) {
var getData = function() {
// Initialize $scope using the value of the model attribute, e.g.,
$scope.url = "https://(remote link to JSON api)";
$http.get($scope.url).success(function(data) {
$scope.listOfServices = data.runningServices; // get data from json
});
};
getData();
$interval(getData(), 10000);
}
]);
However my view is not updating every 10 seconds as expected. I have read that I need to use $scope.apply() somewhere in this above code.
I tried placing the following (in the appropriate place above)
$http.get($scope.url).success(function(data) {
$scope.listOfServices = data.runningServices; // get data from json
$scope.apply(); //I also tried $scope.runningServices.apply()
});
$scope.apply is not your problem, the scope will be digested automatically at the end of the $http request and $interval. Certain actions automatically "inform" Angular that the scope may have changed and trigger a digest; only if you're writing "non-Angular" code may you have to explicitly trigger a scope digest, since otherwise Angular wouldn't notice any changes.
No, your issue is that you're calling getData(), and then have its return value (undefined) execute every ten seconds. Which is obviously nonsense. You just want to pass the function itself to $interval:
$interval(getData, 10000);
// look ma, ^^^^^, no parentheses

passing ng repeat objects from one controller to other [duplicate]

This question already has answers here:
Passing data between controllers in Angular JS?
(18 answers)
Closed 6 years ago.
Hi i am displaying data using ng repeat.
I am displaying 3 persons in a template.. now when i click on 1 of them. I want to display in detail information of that person... so i need to make new controller and new template...but i know one way that is to pass one of the unique parameter like mobile no to the controller.. which then changes path with having mobile number to another controller and template.using location.path().search().. now in that 2nd controller i can request information again using 2nd parameter.. is this okay ??
I wanted to pass whole persons data from 1 controller to other but i don't know how can i do it...
Use a service to set the selected person's information and access the information using a routeParam for the user's unique id.
The code structure would look something like below
angular
.module('app')
.controller('ParentController', function (UserService) {
$scope.onSelect = function (user) {
UserService.setCurrentUser(user);
}
})
.controller('ChildController', function ($routeParams, UserService) {
$scope.user = UserService.getCurrentUser($routeParams.userId);
})
.factory('UserService', function () {
var currentuser = {};
return {
getCurrentUser: getCurrentUser,
setCurrentUser: setCurrentUser
};
function getCurrentUser () {
return currentuser;
}
function setCurrentUser (user) {
currentuser = user;
}
})
You can make the second controller child of the first controller.
All the data of the first controller can then be accessed in the second controller.
It also depends on the use cases, you can use the following methods to share data between controllers (if you do not want to do the above mentioned)
1) $localStorage
2) $sessionStorage
3) create a service

View updates everywhere but in current controller - angularJS

EDIT: As asked, I'll explain a bit more efficiently !
I've been sitting in front of an annoying problem recently, which is that whenever I update a value inside a directive, the controllers I'm not currently "in" are the only ones to be updated properly.
Scenario example: Profile page is made of two controllers. Navbar_controller which is just currently displaying the user name :
<div ng-if="Auth.isAuthenticated">Hello, {{Auth.getCurrentUser().name}}</div>
The second controller , Profile_controller is here to update user values. This is a simple function in the angular first controller, which updates CurrentUser:
$scope.updateUser = function (type, form) {
if (!$scope.modif)
return ;
$http.put('/api/users/' + Auth.getCurrentUser()._id + '/update', {type:type, modif:$scope.modif})
.success(function (data, status) {
$scope.user = Auth.setNewUser(data);
})
.error(function () {
console.log("error");
});
};
When I update, for example, the name. I can see that the database has been modified properly. And indeed, navbar_controller got the update because a new name is printed in the div. However, Profile_controller doesn't get the update: the name printed in the profile page didn't change.
Here are the two basic functions in Auth.service.js :
getCurrentUser: function() {
return currentUser;
},
// 'user' is the data retrieved in http put request dot success
setNewUser: function(user) {
currentUser = user;
$rootScope.$broadcast(); // Navbar_controller is updated with or without this line
return currentUser;
}
Anyway, if I look at the navbar and its controller, which is calling Auth.getCurrentUser() method, the user values are instantly modified. I'e been using an ugly method consisting in modifying the controller values manually or by refreshing the page... But this isn't the way to go, right ?
There must be something with "$rootScope.$broadcast();", but I'm really new to Angular and other questions on stackoverflow are too specific to help me understand properly.
Thank you !
Your question was a little difficult to understand, but I think the problem is that you are reference a changing object in your various controllers. Here is an example to explain:
Service:
var myObject = { ... };
return {
getObject() { return myObject; }
setObject(obj) { myObject = obj; }
};
Controller 1:
$scope.myObjA = Service.getObject();
Controller 2:
$scope.myObjB = Service.getObject();
Now on initialisation both controllers will be referencing the same object, so if you changed a property inside either controller (eg. $scope.myObjB.name = 'bob';), then the other controller would also see the name.
However if you changed the object itself in a controller (eg. Service.setObject(newObj);), then the controller will be referencing the new object, while the other controller will still be referencing the old one.
You can fix this by wrapping your service object in a container:
var cont = {
user: ...
};
function getContainer() { return cont; }
function setNewUser(user) { cont.user = user; }
Then inside your controllers, get the container (not the user):
$scope.cont = Service.getContainer();
And inside your html:
<div>{{cont.user.name}}</div>
Now when you update the user, all attached controllers will be updated.
Well I'd try to change and store the user information in $rootScope, for your scenario could be a good fit.
getCurrentUser: function() {
$rootScope.currentUser===undefined ? 'no User': $rootScope.currentUser;
},
setNewUser: function(user) {
$rootScope.currentUser = user;
//$rootScope.$broadcast(); no need to broadcast
return getCurrentUser();
}
in that way currentUser will be updated in different scopes as needed!
I'll quote AnuglarJs FAQ regarding to $rootscope:
$rootScope exists, but it can be used for evil
Occasionally there are pieces of data that you want to make global to
the whole app. For these, you can inject $rootScope and set values on
it like any other scope. Since the scopes inherit from the root scope,
these values will be available to the expressions attached to
directives like ng-show just like values on your local $scope.
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.
Conversely, don't create a service whose only purpose in life is to
store and return bits of data.

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