Scope not updating inside a function in a different state - javascript

I have two states using the same controller, when I trigger a function from State 1, I want it to go to state 2 and changes its title as well, here's my code:
$scope.page2Title = "Testing";
$scope.aFunction = function(){
$scope.page2Title = "Hello";
$state.go('eventmenu.page2');
};
But page2's title is not updated to "Hello", it is still "Testing". I also tried $scope.apply() but doesn't work either.

Your $scope.page2Title is a local scope to the controller. It would not be available when the new view is loaded.
Using $rootScope or a service would allow the data to be persistent between views. I think for what you are doing $rootScope would be appropriate.
If you were looking to store business logic or data I would recommend using a service.
About $rootScope

Related

How to pass Data from One Controller to Service and access that data on Other Controller on same event?

I'm facing some issue while trying to pass the data from one controller to the other using my service.
I've been implementing the prototype inheritance using the $rootScope in my controller and broadcasting that object, so that I can access that data in the other controllers.
As I'm using the $rootScope, I'm polluting the global namespace, I'd like to pass the data from this controller to the other.
I'm just displaying minimal data inside a table, when the user clicks on specific record of the table, I want to display entire data inside the object.
This is how I'm handling.
<tr ng-repeat="contact in filterContacts = (contacts.contactsData | filter:sensitiveSearch | orderBy:selectBox) " ng-style="{'background-color': contact.email == selectedContact.email ? 'lightgrey' : ''}" ng-click="selectContact(contact)">
In Controller A: I'm calling this function from view and injecting the specific row contact detail.
$scope.selectContact = function(contact) {
contactService.selectedContactInfo(contact);
};
In the Service I'm just returning that data -
var selectedContactInfo = function(contact) {
return contact;
};
How can I access this data in the Controller B during the same event and display it on my view.
Here is the reference Link: http://plnkr.co/edit/beOmiv?p=info
I don't want to use the $rootScope, but I'd like to access the data onto the other controller.
You could use an angular service for this.
The angular services are singletons and you inject them in controllers so you could inject the same service in two different controllers and you are effectively passing state between them.
Imagine you have a property in your service which could be called selectedUserID. You update this property when you click on the specific row. Then in the next controller, you inject the same service and use this property to determine which details you will load.
so you could have a method inside your service which could look like this :
updateSelectedUser = function(userID) {
this.selectedUserID = userID;
}
Your controller then calls this method from the service when the click action happens :
myService.updateSelectedUser($scope.selectedUserID);
This is just an example of course, put your own values in there.
One thing to keep in mind : services can hold state, they are objects after all and singletons so you always inject the same instance.
It makes sense to make sure that the state stored inside the service is not modified by outside actions which do not go through this service. In other words make sure that nothing else changes this selectedUserID so your service state data never gets out of sync. If you can do this, then you are golden.

Angular nested-scopes and nested-views retaining model data

I currently have an application with a rather complex wizard to create a data record. The wizard consists of 3 steps, each associated with a nested view and a controller. Only the data record itself is shared among all three scopes and each controller contributes additional data to the main record.
But they also have scope specific data, that will be used to render additional fields which are only relevant to that nested scope.
I want to be able to go back and forth between the wizard steps but currently it looks like the nested scopes get discarded as soon as I move on to another nested view. I looked up the scope lifecycle in the developer guide: https://docs.angularjs.org/guide/scope#scope-life-cycle
But I does not really tell me how the scope lifecycle applies to nested scopes and how I can prevent these scopes from being discarded. Of course I could move all the data of the nested scopes into the parent scope, but to me that would just feel like a workaround, because actually that data is only relevant to the individual scopes.
I'll try to give a short example:
angular.module('app').controller('ParentCtrl', function ($scope) {
...
$scope.dataRecord = {};
}
angular.module('app').controller('Child1Ctrl', function ($scope) {
...
$scope.dataRecord.test = 'a';
$scope.childScope1SpecificData = '123';
}
angular.module('app').controller('Child2Ctrl', function ($scope) {
...
$scope.dataRecord.test2 = 'b';
$scope.childScope2SpecificData = '456';
}
When I now switch back and forth between the two childscopes, the dataRecord will be adjusted properly, but changes to childScope1SpecificData (via an input field from the template) will be discarded as soon as I switch to Child2Ctrl and back.
Is there a way to persist this data which switching the scope or is it meant to be discarded and I am simply using it wrong?
Thanks
EDIT:
Ok I looked into the factory approach. Maybe to make it more plastic: The additional data, that belongs to each child scope, is a fileuploader with its associated upload queue. Only in a later validation step these pictures actually become part of the datarecord, but until then I don't want the uploaded images to get lost upon switching views.
So what I could do is to externalize the whole fileupload logic into a factory that returns fileuploaders associated to IDs. Whenever a child scope requests the same id the factory will return the same fileuploader. Different Ids will return different uploaders or new ones. That would pretty much solve the problem but would also mean that the data never gets discarded at all unless I really close the browser, because the factory now is absolutely independent of any scope. Since I only want to retain the data in the context of that wizard, I want the data to be discarded, as soon as I leave the wizard.
So after having looked into these other approaches, it seems like I have to go with the original idea: I have to attach the uploaders to the parent scope. So they will continue to exist when switching to other child views, but they will also be discarded as soon as I leave the wizard.
I hope that was correctly summarized
If you are using 'controller as' syntax, you can use this variant.
angular.module('app').controller('ParentCtrl', function ($scope) {
...
$scope.dataRecord = {};
}
angular.module('app').controller('Child1Ctrl', function ($scope) {
...
$scope.ParentCtrl.dataRecord.test = 'a';
$scope.ParentCtrl.childScope1SpecificData = '123';
}
angular.module('app').controller('Child2Ctrl', function ($scope) {
...
$scope.ParentCtrl.dataRecord.test2 = 'b';
$scope.ParentCtrl.childScope2SpecificData = '456';
}
So, you are changing ParentCtrl object in you parent scope, not for every instance.
Sorry, if it was no understandable

Angularjs Best Practice for Data Store

My angular app have 2 controllers. My problem is that the controllers does not keep the data when the user navigates away from the page.
How can I store the selected data on of my controllers into a data store so it can be used between other controllers?
Option 1 - custom service
You can utilize a dedicated angular service to store and share data between controllers (services are single instance objects)
service definition
app.service('commonService', function ($http) {
var info;
return {
getInfo: getInfo,
setInfo: setInfo
};
// .................
function getInfo() {
return info;
}
function setInfo(value) {
info = value;
}
});
usage in multiple controllers
app.controller("HomeController", function ($scope, commonService) {
$scope.setInfo = function(value){
commonService.setInfo(value);
};
});
app.controller("MyController", function ($scope, commonService) {
$scope.info = commonService.getInfo();
});
Option 2 - html5 localStorage
You can use the built-in browser local storage and store your data from anywhere
writing
$window.localStorage['my-data'] = 'hello world';
reading
var data = $window.localStorage['my-data']
// ...
check out this awesome project:
https://github.com/grevory/angular-local-storage
Option 3 - via web server api
If you need to persist data among different users, you should save it somewhere in the server side (db / cache)
function getProfile() {
return $http.get(commonService.baseApi + '/api/profile/');
}
function updateProfile(data) {
var json = angular.toJson(data);
return $http.post(commonService.baseApi + '/api/profile/', json);
}
EDIT See Jossef Harush's answer where he has written an in-depth response that covers other methods including this one.
I'd recommend using either localStorage or sessionStorage - http://www.w3schools.com/html/html5_webstorage.asp.
HTML local storage provides two objects for storing data on the client:
window.localStorage - stores data with no expiration date
window.sessionStorage - stores data for one session (data is lost when the browser tab is closed)
This assumes that you don't want to POST/PUT the data to your web service (windows service mention in your question).
If you data is an array or some sort, you can convert it to JSON to store as a string and then when you need it you can parse it back as follows - How do I store an array in localStorage?:
var names = [];
names[0] = prompt("New member name?");
localStorage["names"] = JSON.stringify(names);
//...
var storedNames = JSON.parse(localStorage["names"]);
There is an option not mentioned in other answers (AFAIK).
EVENTS
You can use events for communication between controllers.
It's a straightforward communication that doesn't need a mediator
(like service) and can't be wiped by the user (like HTML storage).
All the code is written in controllers that you are trying to
communicate with and thus very transparent.
A good example how to leverage events to communicate between controllers can be seen below.
The publisher is the scope that wanna publish (in other words let others know something happened). Most don't care about what has happened and are not part of this story.
The subscriber is the one that cares that certain event has been published (in other words when it gets notified hey, this happened, it reacts).
We will use $rootScope as a mediator between publisher and a subscriber. This always works because whatever scope emits an event, $rootScope is a parent of that scope or parent of a parent of a parent.. When $rootScope broadcasts (tells everyone who inherits) about an event, everyone hears (since $rootScope is just that, the root of the scope inheritance tree) so every other scope in app is a child of it or child of a child of a child..
// publisher
angular.module('test', []).controller('CtrlPublish', ['$rootScope','$scope',
function ($rootScope, $scope) {
$scope.send = function() {
$rootScope.$broadcast('eventName', 'message');
};
}]);
// subscriber
angular.module('test').controller('ctrlSubscribe', ['$scope',
function ($scope) {
$scope.$on('eventName', function (event, arg) {
$scope.receiver = 'got your ' + arg;
});
}]);
Above we see two controllers communicating a message to each other using an event. The event has a name, it has to be unique, otherwise, a subscriber doesn't differentiate between events. The event parameter holds autogenerated but sometimes useful data, the message is the payload. In this example, it's a string but it can be any object. So simply put all the data you wish to communicate inside an object and send it via event.
NOTE:
You can avoid using root scope for this purpose (and limit the number of controllers that get notified of an event) in case two scopes are in direct inheritance line of each other. Further explanation below:
$rootScope.$emit only lets other $rootScope listeners catch it. This is good when you don't want every $scope to get it. Mostly a high level communication. Think of it as adults talking to each other in a room so the kids can't hear them.
$rootScope.$broadcast is a method that lets pretty much everything hear it. This would be the equivalent of parents yelling that dinner is ready so everyone in the house hears it.
$scope.$emit is when you want that $scope and all its parents and $rootScope to hear the event. This is a child whining to their parents at home (but not at a grocery store where other kids can hear). This is a shortcut to use when you wanna communicate from the publisher that is a child or n-th child of the subscriber.
$scope.$broadcast is for the $scope itself and its children. This is a child whispering to its stuffed animals so their parents can't hear.
EDIT: I thought plunker with a more elaborate example would be enough so I decided to keep is simple here. This elaborate explanation should be better.
To share data between two controllers on the same page, you can use factories/services. Take a look at Share data between AngularJS controllers for example.
However, if this is across page reloads/refreshes, you will need to store the data in local storage and then read it upon reloading. An example of that is here: How do I store data in local storage using Angularjs?
Checkout this library https://www.npmjs.com/package/angularjs-store
This can help you manage your application state much simpler as it will force you to have a one way data flow on your application.

How to change the same values everywhere - in service , controllers, outside angular?

service implementation
myService = function()
{
this.config = {
show0: false,
show1: true,
role : -1,
id : -1;
};
};
in controller, I map the config values
$scope.config = myService.config; //I guess this by reference, isnt it???
in templates of these controllers for e.g. the $scope.config.show0 is used with for e.g. ng-model
Now outside angular in my threejs code
I get the service using injector which I have defined earlier and change some values depending on certain conditions
var service = window.my.injector.get('myService');
service.config.id = 1991;
Now this value is not immediately reflected in the HTMl template,
Source = {{config.id}} still renders as Source = -1
But when I click on some other button in the same template which is mapped to any other value in the same scope
Source = {{config.id}} still renders as 1991
How should I force this rerendering or refreshing in my non angular code soon after
var service = window.my.injector.get('myService');
service.config.id = 1991;
///do something to refresh that controller
Am I using the service wrong? How should I make this config available in angular controllers, templates and non angular code if not via a service?
Shouldnt changing the $scope.config properties values and changing the values outside angular by retrieving the service via injector change the values everywhere ?
This is because the angular digest cycle does not kick in from your threejs code. I am not sure where your three js code is, but try using $scope.$apply to kick in the digest cycle, and it should work fine.
If you can share a jsFiddle, I can have a better understanding on what you are trying to achieve, but the reason why this is not happening is, as I said, that the digest cycle does not kick in.

Private variables in the controller scope

In angular, following the demos, I can define a controller as:
function TodoCtrl($scope) {
$scope.todos = [
{text:'learn angular', done:true},
{text:'build an angular app', done:false},
{text:'empty dishwasher', done:false}];
$scope.oldtodos = [];
var oldtodos2 = [];
...
You'll note that I have two oldtodos. One on the $scope and one that is just a local var. I think the latter approach is the way to go if you want to encapsulate this variable - i.e. no-one other than the controller has any interest in it and the former is good if you want to pass the variable back to the model
Am I correct?
It looks like you just want to keep a private copy of oldTodos around in the event you have to either refer back to them or resurrect one of them or something. In that case, it probably makes sense not to put the oldTodos into the scope. As Maxim said, scope is relevant if you want to bind values to the view. If you just want to save the oldTodos so keep a reference to them, a normal variable is fine.
Then, if you want to bring one of them back, just copy it into the $scope.todos and back it comes.
Note that you can also abstract all of the todos into a service and inject the service into your controller for another level of encapsulation and better testing strategy. It will also enable you to share the todos across controllers if that's necessary.
Long story short, if you want AngularJS app to interact with the application scope, then you need to add the data in the scope. That's it.
$scope.oldtodos is the correct way to add data to the scope and it can be referred by name oldtodos in HTML template. While var oldtodos2 is private in your controller, so angular will not be able to access this data in the template since it is not in the scope.

Categories

Resources