0

I have a separate model and a controller for a teachers list.

My teacherModel.js is:

app.factory('Teacher', [function() {
   function Teacher(teacher) {
     // constructor
   };
   Teacher.prototype = {
        setTeacher: function(teacher) {
            angular.extend(this, teacher);
        },
        getAllTeachers: function(callback) {
            var scope = this;
            var ref = firebase.database().ref('/xxx/teachers');
            ref.once('value').then(function(snapshot) {
                teachersList = snapshot.val();
                scope.setTeacher(teachersList);
                // THERE'S A PROBLEM HERE...
                // I'm trying to pass this callback from the controller:
                callback;
            });
        }
    };  
    return Teacher;
}]);

Now from my controller I call the getAllTeachers() method with a callback function:

app.controller('teacherMainCtrl', ['$scope', 'Teacher', function($scope, Teacher){
    var teacher = new Teacher()
    teacher.getAllTeachers(function() {
       $scope.teachers = teacher;
       console.log($scope.teachers);
    });
}]);

Problem is console.log($scope.teachers); is not logging anything to the console. I don't think the callback is being executed at all.

Can someone help me to figure out what I'm doing wrong or suggest a better way to add functionality to the model data from controller after the data is asynchronously retrieved from firebase? Thanks.

1
  • You could simply return ref.once('value); in your getAllTeachers() since that returns a promise and do away with the callback so that in your controller, you can just call teacher.getAllTeachers().then(function (snapshot) { // assign snapshot}); to evaluate the result of the promise. Commented Oct 23, 2016 at 13:25

2 Answers 2

2

You can leverage the fact that once returns a firebase promise so you can alter your code to the following:

app.factory('Teacher', [function() {
   function Teacher(teacher) {
     // constructor
   };
   Teacher.prototype = {
        setTeacher: function(teacher) {
            angular.extend(this, teacher);
        },
        getAllTeachers: function() {
            var scope = this;
            var ref = firebase.database().ref('/xxx/teachers');
            return ref.once('value').then(function(snapshot) {      
                return snapshot.val();
            });
        }
    };  
    return Teacher;
}]);

This would behave similarly to any $http request where it returns a promise. Now, in your controller, you can then call your getAllTeachers() like so:

app.controller('teacherMainCtrl', ['$scope', 'Teacher', function($scope, Teacher){
    var teacher = new Teacher()
    teacher.getAllTeachers().then(function (snapshotValues) {
        // What you returned in the promise above is populated in snapshotValues here
        $scope.teachers = snapshotValues;
    });
}]);

Update

If you want to use the $q service for your particular scenario, you can do the following:

app.factory('Teacher', ['$q', function($q) {
   function Teacher(teacher) {
     // constructor
   };
   Teacher.prototype = {
        setTeacher: function(teacher) {
            angular.extend(this, teacher);
        },
        getAllTeachers: function() {
            var defer = $q.defer();
            var scope = this;
            var ref = firebase.database().ref('/xxx/teachers');
            ref.once('value').then(function(snapshot) {      
                var val = snapshot.val();
                // Transform your data any way you want. 
                // Whatever you pass into resolve() will be available as a parameter in the subsequent then()

                defer.resolve(val);
            });

            return defer.promise;
        }
    };  
    return Teacher;
}]);

Using the method would still be the same. You simply just call then()

teacher.getAllTeachers()
    .then(function (whatYouPassedInResolve) {

});

Another thing to note is that in the getAllTeachers method inside of your factory, I did not handle any error cases. That would be achieved by rejecting the promise with defer.reject(objectToSendBack). You pass in any data you want accessible when you deem that call a failure.

Just pass in a function for the second parameter to the `then(successCallback, errorCallback) to handle any rejected promises.

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks John. This works, however, I'm trying to avoid any dependency for controller on firebase. so if I my data store changes I can always change the model and accept a call back. For example, I really want to return an array or a processed object to controller rather than the default firebase 'snapshot' response.
I'd suggest then using the $q service to create a defer object and return it as a promise. Then, you can either resolve or reject the result of your firebase request (or a pre-processed like returning an array as you mentioned). Then, you normalize the behavior as you're returning an angular built-in promise object (same as $http). That way, callers of your service are evaluating $q promises and know nothing about the underlying service call.
Hi John, this seems better. I haven't used $q services before. I will check this out. Thanks. Perhaps you can edit the answer to include this instead? I think it'll help others more.
@Sadeep No worries. I updated the answer to include the use of $q.
1

I think you are not calling the callback actually, use callback()

app.factory('Teacher', [function() {
   function Teacher(teacher) {
     // constructor
   };
   Teacher.prototype = {
        setTeacher: function(teacher) {
            angular.extend(this, teacher);
        },
        getAllTeachers: function(callback) {
            var scope = this;
            var ref = firebase.database().ref('/xxx/teachers');
            ref.once('value').then(function(snapshot) {
                teachersList = snapshot.val();
                scope.setTeacher(teachersList);
                // THERE'S A PROBLEM HERE...
                // Try this
                callback();
            });
        }
    };  
    return Teacher;
}]);

1 Comment

Thanks for the answer. I did indeed miss the brackets.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.