Angular $scope недоступен за пределами моей функции

Несколько новичок в Angular и javascript. У меня есть следующий контроллер, написанный для использования фабричной службы для доступа к локальному файлу JSON. Большая часть этого кода получена (или полностью взята) из это сообщение Дэна Валина. Я не могу получить доступ к переменной $scope.books вне функции и не могу понять, почему. console.log внутри функции дает мне объект, который я ищу, но тот, что снаружи, возвращает undefined. Что я здесь делаю неправильно? Спасибо.

app.controller('FormController', ['$scope', 'tocFactory', function ($scope, tocFactory) {

  $scope.books;

  getBooks();

      function getBooks() {
        tocFactory.getBooks().
          success(function(data, status, headers, config) {
            $scope.books = data;
            console.log($scope.books);
          }).
          error(function(data, status, headers, config) {
            // log error
     })
  }
  console.log($scope.books);
  }]);

person GMarsh    schedule 18.08.2015    source источник
comment
Все в порядке, console.log внутри success возвращает правильный объект. Добро пожаловать в мир асинхронных методов.   -  person Krzysztof Safjanowski    schedule 18.08.2015
comment
Я думаю, это зависит от вашего определения ОК. Я не могу привязать его к своему HTML или использовать в других частях моего контроллера в его текущей форме. Как я могу сделать так, чтобы я мог?   -  person GMarsh    schedule 18.08.2015
comment
Весь смысл привязки данных для пользовательского интерфейса заключается в том, что всякий раз, когда ваши данные изменяются, пользовательский интерфейс отражает это автоматически. Ваши привязки не должны быть правильными.   -  person David Zech    schedule 18.08.2015


Ответы (3)


Потому что вы делаете ajax, а затем выполняете следующую строку кода. Это не означает, что у вас есть гарантия, что ваш ответ ajax будет готов при выполнении следующей строки кода.

Вы всегда можете получить значение ответа внутри функции успеха $http, которая вызывается при успешном выполнении вызова ajax.

Прочтите здесь Как работает асинхронный вызов?

person Pankaj Parkar    schedule 18.08.2015

на самом деле это не проблема масштаба, а время. Функция getBooks выполняется асинхронно, поэтому, когда ваш консольный журнал появится, он, скорее всего, ни к чему не будет привязан. Вы можете легко проверить это с помощью интервала, чтобы увидеть, как это происходит:

app.controller('FormController', ['$scope', 'tocFactory', function($scope, tocFactory) {
  $scope.books;
  getBooks();
  function getBooks() {
    tocFactory.getBooks()
      .success(function(data, status, headers, config) {
        $scope.books = data;
        console.log($scope.books);
      })
      .error(function(data, status, headers, config) {
        // log error
      })
  }
  setInterval(function(){
    console.log($scope.books);
  }, 1000);
}]);
person wesww    schedule 18.08.2015
comment
что, если ответ займет больше 1 секунды? - person Pankaj Parkar; 18.08.2015
comment
мой быстрый демонстрационный код предлагает играть с setInterval, а не с setTimeout. Он просто будет повторять регистрацию каждую 1 секунду навсегда, поэтому, скорее всего, он выведет undefined один или несколько раз, а затем выведет какое-то значение после вызова .success. - person wesww; 18.08.2015
comment
ха-ха, не беспокойтесь, я понял. Ваш ответ является столь же хорошим объяснением проблемы, я проголосовал за него. - person wesww; 18.08.2015
comment
Я тоже сделал твое .. Ура :) - person Pankaj Parkar; 18.08.2015

Вы можете использовать службу $q для обработки асинхронного кода с промисами:

app.controller('FormController', ['$scope', '$q', 'tocFactory', function ($scope,  $q, tocFactory)
{

    var getBooks = function()
    {
        var deferred = $q.defer();

        tocFactory.getBooks().
        success( function(data, status, headers, config)
        {
            $scope.books = data;
            deferred.resolve();
        } ).
        error( function(data, status, headers, config)
        {
            deferred.reject();
        } );

        return deferred.promise;
    };

    getBooks().then( function(res)
    {
        console.log($scope.books); // success : your data
    }, function(res)
    {
        console.log($scope.books); // error : undefined
    } );

    console.log($scope.books); // undefined

} ] );

Я не тестировал этот код, но он должен работать и показывать вам принцип обещаний. Подробнее о сервисе $q: https://docs.angularjs.org/api/ng/service/$q

person Ziad    schedule 18.08.2015
comment
Нет необходимости использовать $q, так как $http уже возвращает обещание. Более того, методы успеха и ошибки теперь устарели — https://docs.angularjs.org/api/ng/service/$http — Устаревшие методы promise $http устарели. Вместо этого используйте стандартный метод then. Если для параметра $httpProvider.useLegacyPromiseExtensions установлено значение false, эти методы будут вызывать ошибку $http/legacy. - person Krzysztof Safjanowski; 18.08.2015