Lennert
Lennert

Reputation: 153

How to use Firebase query in Angular factory?

I'm trying to get the new Firebase queries working in my factory, but I'm stuck. I want to return the value of 'logo' out of my opponents table, based on the opponent name. I can get the example with console.log from the Firebase docs running:

function OpponentService($firebase) {
    var factory = {};
    var _url = 'https://somefirebaseproject.firebaseio.com/opponents';
    var _ref = new Firebase(_url);

    factory.getLogo = function(opponent) {
        _ref.orderByChild('name').equalTo(opponent).on("child_added", function(snapshot){
            console.log(snapshot.val().logo);
        });
    };

    return factory;
}

When I call my factory with OpponentService.getLogo(opponentName) in my controller, I get the correct logo id in my console. But how can I return the value instead of sending it to my console? I want to store it in a variable like this: $scope.opponentLogo = OpponentService.getLogo(opponentName). I tried several variations with a return statement like this:

factory.getLogo = function(opponent) {
        _ref.orderByChild('name').equalTo(opponent).on("child_added", function(snapshot){
            return snapshot.val().logo;
        });
};

But apparently I don't fully understand how factories work in Angular, because I get an undefined. Could it be that the value isn't available yet and I should use a promise in someway? Anyone who can point me in the right direction?

Upvotes: 2

Views: 2111

Answers (1)

Niko Nyman
Niko Nyman

Reputation: 1926

You're returning the value of logo from the anonymous function inside the Firebase on() call, but you're not returning anything from getLogo().

Returning a promise would be a good way to do this. This is how you retrieve the opponent logo with AngularFire, if there is no guarantee opponentName will be unique:

// getLogo() returns a promise that you can bind to the UI.
// When loading finishes, the binding will get the opponent's logo value.
factory.getLogo = function (opponentName) {
    var opponentsArray = $firebase(_ref.orderByChild('name').equalTo(opponentName)).$asArray();

    // $loaded() returns a promise, so we can use the then() method to add
    // functionality when the array finishes loading and the promise resolves.
    var r = opponentsArray.$loaded().then(function () {
        // Now return the logo for the first matching opponent
        return opponentsArray[0].logo;
    });

    return r;
};

If opponentName is unique, I would rethink the data structure so that you could use the opponentName as the key for opponents. Then you would be guaranteed to get a single opponent and could fetch it with:

var opponent = $firebase(_ref.child(opponentName)).$asObject();

If you're not using AngularFire, you can return a promise using Angular's $q service. The following should work:

factory.getLogo = function(opponent) {
    $q(function (resolve, reject) {
        function successCallback(snapshot) {
            resolve(snapshot.val().logo);
        };

        function cancelCallback(error) {
            reject(error);  // pass along the error object
        };

        _ref.orderByChild('name').equalTo(opponent)
            .on("child_added", successCallback, cancelCallback);
    });
};

You can assign the result of getLogo() to a scope variable, and bindings will update in the UI when Firebase returns the value.

Upvotes: 5

Related Questions