Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Persisting data between routes without going to the server?

Tags:

backbone.js

I am working on a log in for my backbone application and came on an issue I am not sure how to solve without making a call to the server. This brought up a discussion in my team about what the way other folks are handing this kind of thing in backbone because we think we will be running into a similar thing moving forward.

It's Friday and I'm probably just brain dead, but here goes...

We have a User Model. The login method of the View creates a new user Model and call's it's login method passing in the user's credentials and a callback function which has an object that contains the users information.

Here is the login method for our View:

login: function(event){
    event.preventDefault();

    var user = new App.User;
    user.login($('#username').val(), $('#password').val(), 
        (function(msg) {
            // success callback
            if (msg.loggedIn) {
                console.log("Authenticate successful: " + JSON.stringify(msg));
                var data = { user : msg, bob : "bob", trigger:true };
                console.log("Prepared data: " + JSON.stringify(data));
                App.router.navigate('home',data);
            } else {
                console.log("Authenticate unsuccessful: " + JSON.stringify(msg));
            }
        }).bind(this), 
        function(msg) { 
            // failure callback
            console.log("Authenticate communication failure: " + JSON.stringify(msg));
            alert("Communication fail!"); 
            App.router.navigate('login',{trigger:true}); 
        });
}

What we are trying to figure out is how to best make this Model data available to another route (home) so we can use it in the View.

So I have this router:

routes: {
'': 'home',
'home': 'home',
'login': 'login'
},
home: function(data){
   console.log(data);
}

Once we have logged the user in we need to update the route and have access to that users data, but don't want to have to make a trip back to the server to fetch it.

I am concerned because we are building a kind of "wizard" where the user may need to move forward and backward through some steps and I don't want to have to hit the server every time they navigate through the application, but it's seeming like we are going to either need to save stuff to a global variable (don't want to do this) or make a trip back to the server every time. I'm sure others have had to deal with similar issues. Just looking for some insight.

Thanks!

like image 781
Charles Avatar asked Jan 04 '13 20:01

Charles


2 Answers

"it's seeming like we are going to either need to save stuff to a global variable (don't want to do this)"

To state the obvious: You're going to need to preserve state. Your options are either to transfer the state to the server and back, or hold state on the client. Since you've already identified that you don't want to pass the state via a server, you're left with preserving state between different pages (routes) on the client.

And that's what global variables are for. It sounds icky, I know, but it's also one of the main benefits that Single-Page Applications bring to the table. Statefulness. And that state will always be held by some global object.

There are better and worse ways of managing state. Having a global variable called data that you keep assigning and reassigning is obviously the worst way. You should figure out a pattern that makes sense for your requirement.

If I understood your code sample correctly, what you want to store is the information about the current user. It also seems that you already have a global variable App. It occurs to me that it would be a good idea to keep some kind of session info:

login: function(data){
    doLogin({
        success: function(userData) {
            App.session.currentUser = userData;
            App.router.navigate('home', { trigger:true });
        }
    });
},

home: function(data){
    if(!App.session || !App.session.currentUser) {
        App.router.navigate('login', { trigger:true });
        return;
    }

    var user = App.session.currentUser;
    //do something with user
}

State is not necessarily evil. What's evil is depending on global state throughout the application, which leads easily to untestable spaghetti code. But if you resolve the state dependency as "high up" in the chain as possible (e.g. in the Router), and pass the values down using constructors and method arguments, you can still keep the testability and side-effectlessness in the rest of the codebase.

Sorry that I don't have a silver bullet for you. There are some libraries, Backbone.StateManager among them, which can help in managing state, transitions and such, but essentially they don't do anything you can't do for yourself.

like image 198
jevakallio Avatar answered Sep 27 '22 00:09

jevakallio


Use localStorage!

Modify your code to do the following:

       // success callback
        if (msg.loggedIn) {
            console.log("Authenticate successful: " + JSON.stringify(msg));
            var data = { user : msg, bob : "bob", trigger:true };
            var dataString = JSON.stringify(data);
            console.log("Prepared data: " + dataString;
            window.localStorage.setItem("userdata",dataString);
            App.router.navigate('home',data);

Now whenever you need to check if the user is logged in, do the following:

try {
    var userData = window.localStorage.getItem ("userdata");
} catch (e) {
    // Do something
}

The try-catch is necessary to make sure that your code doesn't barf if the authentication has never been successful.

like image 45
Sudipta Chatterjee Avatar answered Sep 27 '22 00:09

Sudipta Chatterjee