Как передать функцию обратного вызова once().then() в angularjs?

#javascript #angularjs #firebase #callback #firebase-realtime-database

#javascript #angularjs #firebase #обратный вызов #firebase-realtime-database

Вопрос:

У меня есть отдельная модель и контроллер для списка учителей.

Мой teacherModel.js :

 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;
}]);
  

Теперь из моего контроллера я вызываю getAllTeachers() метод с функцией обратного вызова:

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

Проблема в том, console.log($scope.teachers); что ничего не записывается в консоль. Я не думаю, что обратный вызов выполняется вообще.

Может кто-нибудь помочь мне разобраться, что я делаю неправильно, или предложить лучший способ добавить функциональность к данным модели из контроллера после асинхронного извлечения данных из firebase? Спасибо.

Комментарии:

1. Вы могли бы просто return ref.once('value); в вашем getAllTeachers(), поскольку это возвращает обещание, и отменить обратный вызов, чтобы в вашем контроллере вы могли просто вызвать teacher.getAllTeachers().then(функция (снимок) { // назначить снимок}); для оценки результата обещания.

Ответ №1:

Вы можете использовать тот факт, что once возвращает обещание firebase, чтобы вы могли изменить свой код следующим образом:

 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;
}]);
  

Это будет вести себя аналогично любому $http запросу, где он возвращает обещание. Теперь в вашем контроллере вы можете вызвать свой getAllTeachers() вот так:

 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;
    });
}]);
  

Обновить

Если вы хотите использовать службу $ q для вашего конкретного сценария, вы можете сделать следующее:

 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;
}]);
  

Использование метода все равно будет таким же. Вы просто вызываете then()

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

});
  

Еще следует отметить, что в getAllTeachers методе внутри вашей фабрики я не обрабатывал никаких случаев ошибок. Это было бы достигнуто путем отклонения обещания с помощью defer.reject(objectToSendBack) . Вы передаете любые данные, к которым хотите получить доступ, когда считаете, что вызов завершился ошибкой.

Просто передайте функцию для второго параметра в `then(successCallback, errorCallback) для обработки любых отклоненных обещаний.

Комментарии:

1. Спасибо, Джон. Это работает, однако я пытаюсь избежать какой-либо зависимости контроллера от firebase. поэтому, если мое хранилище данных изменится, я всегда могу изменить модель и принять обратный вызов. Например, я действительно хочу вернуть массив или обработанный объект контроллеру, а не ответ firebase ‘snapshot’ по умолчанию.

2. Я бы предложил затем использовать $q сервис для создания объекта defer и возврата его как обещание. Затем вы можете либо разрешить, либо отклонить результат вашего запроса firebase (или предварительно обработанный, например, возвращающий массив, как вы упомянули). Затем вы нормализуете поведение, поскольку возвращаете встроенный в angular объект promise (такой же, как $http ). Таким образом, вызывающие вашу службу пользователи оценивают обещания в размере $ q и ничего не знают о базовом вызове службы.

3. Привет, Джон, это кажется лучше. Я раньше не пользовался $q сервисами. Я проверю это. Спасибо. Возможно, вы можете отредактировать ответ, чтобы включить это вместо? Я думаю, это больше поможет другим.

4. @Sadeep Не беспокойтесь. Я обновил ответ, включив в него использование $q .

Ответ №2:

Я думаю, что вы на самом деле не вызываете обратный вызов, используйте 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. Спасибо за ответ. Я действительно пропустил скобки.