Passing variables from one page to another

2,301 views
Skip to first unread message

Yonatan Kra

unread,
Feb 10, 2014, 10:40:31 AM2/10/14
to ang...@googlegroups.com
I have a SPA (single page app). I am trying to keep data of one route, when moving to another, so if a person returns to the route, he'll see it in exactly the same state he left it.

In example, I have an inbox page (e.g. #/inbox).
On this page, a person can see his inbox, and gets new messages to the inbox when he stays on this page.  Unread emails are in bold, read emails are normal and they are sorted and filtered in a certain manner the user has chosen. 
The app gets one "push" of emails from the server, and then updates when it runs.
It works fine.

The problem arises when this person "navigates" away from the inbox "page".
The navigation is from #/inbox to #/other-page - each has its own controller and view.

After the person navigates away, and then comes back to the page, the page resets - e.g. the inbox starts from scratch, there are no unread emails in bold, filters are reset etc.
I followed instructions given me here, to keep the data in a service, but when I get back to #/inbox, the service resets and I lost the data.

How do I keep such data, so the person would see the same inbox page after navigating the rest of the SPA? 
Or is there a smarter way than just rebuilding the page with saved data?

Thanks



Luke Kende

unread,
Feb 12, 2014, 1:53:53 AM2/12/14
to ang...@googlegroups.com
Yes.  Use a service.  If it's not working for you then you are coding it wrong.  My guess is not understanding object inheritance and losing a reference to primitive values

My examples are not full blown, but hopefully the details will help you see what you need:

function EmailService(){
  var emailObject = {
    emails: [],
    currentView: 'inbox',
    folders: []
  }

  return {
     emails: emailObject.emails,
     setCurrentView: function(view){
       emailObject.currentView = view;
    },
    folders: emailObject.folders
  }
  
}

function InboxCtrl($scope, MyService){ 
   $scope.emails = MyService.emails; 
   
   //now whatever you do on the scope will reflect in the service emails and will remain there when reloaded
   $scope.emails.push({'from': 'bob', message: 'Hi Larry!'});

   $scope.folders = MyService.folders; 

  $scope.userClickedFolder(folder){
    MyService.setCurrentView(folder)
  }
}

function OtherCtrl(){

Yonatan Kra

unread,
Feb 12, 2014, 2:53:54 AM2/12/14
to ang...@googlegroups.com
Hi,
Thanks. That's roughly what I did. I have 2 questions.
The simple one: Are you handling the views from the controller itself (by changing the view in the service)?

The "more complex" one:

Let's say I have this route defintions:
  1. $routeProvider.when('/inbox', {
  2. templateUrl: 'inbox.html',
  3. controller: InboxCtrl
  4. }
  5. });
  6. $routeProvider.when('/otherPage', {
  7. templateUrl: 'otherPage.html',
  8. controller: OtherPageCtrl
  9. }
  10. });

In InboxCtrl I call function (InboxCtrl($scope, MyService)) and do all kind of manipulations. Let's assume a very simple service here like:
function EmailService(){
var myStatus = "OK";
return {myStatus: myStatus};
}
function InboxCtrl($scope, MyService){
$scope.myStaus = MyService.myStatus;
}
And it is bound to an input in the view, so user can change myStatus.

Now a person clicks on a link that leads him to "otherPage" (with function OtherPageCtrl($scope, MyService)).

When I call MyService.MyStatus in the "otherPage" controller, I get "OK" no matter what was its value in the "Inbox" page.

Is that a glitch in my code? Or is that how it's supposed to be?

Thanks

--
You received this message because you are subscribed to a topic in the Google Groups "AngularJS" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/angular/jL-IAlCZEaU/unsubscribe.
To unsubscribe from this group and all its topics, send an email to angular+u...@googlegroups.com.
To post to this group, send email to ang...@googlegroups.com.
Visit this group at http://groups.google.com/group/angular.
For more options, visit https://groups.google.com/groups/opt_out.

Luke Kende

unread,
Feb 12, 2014, 8:55:03 AM2/12/14
to ang...@googlegroups.com
I wrote my example terribly (the service name should obviously be consistent).... but yes, the problem is due to primitive reference being passed by value.  A rule to follow is always pass an object as javascript will maintain reference to the object which is only created once in your service (when initializing the app) and exists for the duration of the page.  So change your example to:

function EmailService(){
var myObject = {
  myStatus: "ok"
}

return myObject;
}

function InboxCtrl($scope, EmailService){

  //this will get lost if changing myStatus on scope because copies the primitive value
  $scope.myStatus = EmailService. myStatus;

  //this will not get lost as $scope.email now points to the same object as defined in the service
  $scope.email = EmailService;
  $scope.email.status = "sweet";  //any change to the object locally reflects back to the service because they are the same object, even though the property itself is a primitive.  This will be true no matter the view changes
}

Somewhere is written in an angular doc the "dot" rule.  This kind of thing trips a lot of Javascript developers up.  By the way, no, the service doesn't handle the view at all, it is just injected into the controller that is loaded with the view.  It will make sense more as you use it and explore it.  


Yonatan Kra

unread,
Feb 12, 2014, 9:28:49 AM2/12/14
to ang...@googlegroups.com
Thanks. It makes much more sense now (as usual with things that work :)).
How about "value"? Would it work the same?
e.g.
myApp.value('myValue',{
myStatus: "ok"
});
or only with a service (
myApp.service('myService',function(){
var myStatusObj = {myStatus: "ok"}; 
return myStatusObj;
});)

Thanks again

(I'm sorry for the newbie questions, but I'm still trying to get the differences between factory/value/service/provider/constant - that's my mission on my 3rd week in Angular...)

Luke Kende

unread,
Feb 12, 2014, 9:46:33 AM2/12/14
to ang...@googlegroups.com
I won't claim to be a guru with Angular, I just have worked through these sorts of issues.  What I have found is that Angular gives you a general framework for structuring your app, but not necessarily a guideline on how to do it.  It's fairly versatile in letting you determine how to do it.

That said, I use value() very rarely, as i don't see the benefit of it over a service since it only holds one reference.  With a service I can setup all sorts of data structures.

I only use a factory when I need something new'ed such that each time I inject it in a controller, I'm going to call new.  I use it with a timer object I created that can count down and up and bind to the UI (like showing you have 10,9,8,7... seconds to confirm, or, you have been active for so many seconds).  When injecting it into a controller, I will call $scope.timer = new TimerService();  In this way, I don't have a shared "state" with the timer, only a way to create a new instance of something I will use in several places to perform some functionality.

I have not used providers, but understand they offer more abstracted functionality.

I went through a three month experimental phase and learned a lot about Javascript that I did not before.  I still don't always understand it all, but enough to get things working well.

Good luck!

Yonatan Kra

unread,
Feb 15, 2014, 2:46:49 AM2/15/14
to ang...@googlegroups.com

Thanks a lot! You saved a lot of hours of trial and error.  My spa works seemlessly now.

Luke Kende

unread,
Feb 15, 2014, 3:46:47 AM2/15/14
to ang...@googlegroups.com
Awesome!  Glad I could help.
Reply all
Reply to author
Forward
0 new messages