1

I've been working on a simple chat application that will allow users to type messages in a simple group chat and have them update/display in real time. Because of this I have decided to use Firebase, and spent some time getting used to its web API. However, I've run into some issues when I try to manually create a simple user authentication system, or even just return a list of values to another function.

I'm trying to follow an MVC pattern, with the HTML forms triggering event handlers inside global.js which then calls the appropriate method in the controller file, which in turn calls the appropriate method in the DAL. The DAL goes to the remote Firebase and performs the requested action.

My problem is that I'm not getting anything to happen after the once event handler fires. The contoller will perform the checkUserLogin() method, but nothing will be assigned to the variable loginValid. As far as I can tell from debugging, the execution reaches the end of the event handler and then drops itself. I've read something about asynchronous JS, but don't really understand it.

All I want to happen is to retrieve a list from Firebase, process it for authentication before passing the result from the DAL to the controller, and then do stuff with the result in the controller. For these purposes I don't care about the real time updates, I just sometimes want to return a current snapshot of the data in the Firebase list and perform actions on it inside another function.

Here is my file structure (as necessary). global.js contains event handlers, and is responsible for handling HTML page events and choosing the appropriate controller action (security_controller.js) which then calls the necessary DAL action (security_dal.js).

global.js

$(document).ready(function() {
    $("#btnSubmitLogin").on("click", btnSubmitLogin_click);
});

function btnSubmitLogin_click() {
     doUserLogin();
}

security_controller.js

function doUserLogin() {
    var username = $("#txtLoginUsername").val();
    var password = $("#txtLoginPassword").val();

    //Determine if user is logged in, and react accordingly
    var loginValid = checkUserLogin(username, password);

    if (loginValid) {
        navigateToPage("pageHome");
        $("#successMessageLogin").text("Success validating user");
    }
    else {
        navigateToPage("pageLogin");
        $("#errorMessageLogin").text("Error validating user");
    }
}

security_dal.js

function checkUserLogin(username, password) {
    var foundUser = null;
    var loginValid = false;

    fb_ref.once("value", function(snapshot) {
        var list = snapshot.val();

        for (var i in list) {
            if (list[i].username === username && list[i].password === password) {
                foundUser = list[i];
                break;
            }
        }           

        return loginValid;
    });

    if (foundUser !== null) {
        console.info(foundUser.username);
        console.info(foundUser.password);
        loginValid = true;
    }

    return loginValid;
}

I checked out several similar searches, but came up with nothing that answered my question. In the following question/answer, the answer mentioned something about asynchronous javascript and seems to resolve the issue by merely putting the code that needs to be executed after retrieving the values inside the callback function. However, this goes against what I'm trying to achieve with my "MVC pattern." The DAL shouldn't be touching the view, but rather passing the returned value(s) back to the controller.

Firebase Query Inside Function Returns Null

Ideally, here is what I want to happen:

  • global.js calls btnSubmitLogin_click()
  • btnSubmitLogin_click() calls doUserLogin in the controller
  • doUserLogin in the controller calls checkUserLogin in the DAL
  • checkUserLogin in the DAL retrieves a list of users from Firebase and looks for a match with entered credentials
  • checkUserLogin in the DAL returns true/false to the controller and assigns it to the variable loginValid
  • The controller decides what to do with this information, updating the view as necessary

Note: I am fully aware that Firebase has its own authentication, but I want to create my own simple version just for the experience I guess. Because of this I just need a list/array of the data to manipulate in the controller. I am also aware of the possibility of filtering/sorting, however I wish to use this method for checking for users as I want to know how to retrieve a list.

Please let me know if I'm doing this the wrong way entirely (new to Firebase), or if something else should be provided. Thanks!

Community
  • 1
  • 1
Kendall
  • 1,992
  • 7
  • 28
  • 46

1 Answers1

1

Yes, a-synchronousness is the key here. Generally you can solve that by making your own callbacks. That way you can still do the logic in the right spots.

In this case, you could make a 3rd parameter for checkUserLogin:

checkUserLogin(username, password, function(foundUser) {

     if (foundUser) {
         navigateToPage("pageHome");
         $("#successMessageLogin").text("Success validating user");
     } else {
        navigateToPage("pageLogin");
        $("#errorMessageLogin").text("Error validating user");
     }
});

And then:

function checkUserLogin(username, password, callback) {
    fb_ref.once("value", function(snapshot) {
        var list = snapshot.val();
        var foundUser;

        for (var i in list) {
            if (list[i].username === username && list[i].password === password) {
                foundUser = list[i];
                break;
            }
        }           
        callback(foundUser)
    });
}

(Obliged to point out the unencrypted passwords etc etc)

Jorg
  • 7,219
  • 3
  • 44
  • 65
  • Ah, thanks. I believe that makes a bit of sense, I'll give it a shot. I think I still don't understand callbacks rather well, or function passing (or something like that??). And yes, I am completely aware of the unencrypted passwords, this is completely non-production. But I appreciate the warning :) – Kendall Apr 11 '16 at 22:02
  • Functions can be used just like any variable. That means you can use a function as a parameter for another function, in the same sense that a function can return a function. You're using them already, see the second `fb_ref.once` input parameter. – Jorg Apr 11 '16 at 22:06
  • I get that, in theory anyway. But when it comes to wrapping my head around it I always struggle. I'll definitely look into it more in the future though. I'm finishing a second year of programming, but unfortunately they've never looked at more than basic JS, and I've been left on my own. This definitely will help me understand more I hope! – Kendall Apr 11 '16 at 22:20