ng-repeat не обрабатывает FileReader

Итак, вот как выглядит мой взгляд:

<body ng-controller="AdminCtrl">
<img ng-repeat="pic in pics" ng-src="{{pic}}" />
<form ng-submit="postPic()">
    <input id="photos" type="file" accept="image/*" multiple/>
    <button>Add Pics</button>
</form>

А это контроллер:

app.controller('AdminCtrl',['$scope', function($scope){
    $scope.pics =[];

    $scope.postPic = function() {
        var files = $('#photos').get(0).files;

        for (var i = 0, numFiles = files.length; i < numFiles; i++) {
            var photoFile = files[i];
            var reader = new FileReader();
            reader.onloadend = function(e){
                $scope.pics.push(e.target.result);
                console.log($scope.pics);
            };
            reader.readAsDataURL(photoFile);
        }
    };

Хотя я выбираю много файлов, и они также отображаются в консоли (хотя и асинхронно), я не могу обновить представление на основе обновления $scope.pics. Разве за $scope.pics не следят? Почему это происходит?


person noobavistic    schedule 09.05.2015    source источник


Ответы (1)


Проблема в том, что вы изменяете объект $scope асинхронно, поэтому angular не знает об изменениях, которые ему необходимо обработать. Angular не «следит» постоянно за вашим объектом $scope. Причина, по которой вам обычно не нужно явно использовать $scope.$apply(), заключается в том, что angular будет автоматически делать это за вас большую часть времени, если вы находитесь внутри экосистемы angular (например, конструкторы контроллеров, функции $scope и т. д.).

app.controller('AdminCtrl',['$scope', function($scope){
    $scope.pics =[];

    $scope.postPic = function() {
        var files = $('#photos').get(0).files;

    for (var i = 0, numFiles = files.length; i < numFiles; i++) {
        var photoFile = files[i];
        var reader = new FileReader();
        reader.onloadend = function(e){
            $scope.pics.push(e.target.result);
            console.log($scope.pics);
            $scope.$apply(); // force digest cycle
         };
         reader.readAsDataURL(photoFile);
    }
};

По той же причине angular предоставляет службу $timeout, которая является всего лишь оболочкой для setTimeout, но автоматически запускает для вас цикл дайджеста: https://stackoverflow.com/a/19609851/580487

TLDR; Асинхронная функциональность [помимо встроенных вещей angular] находится за пределами экосистемы angular, поэтому вы должны сообщать angular об изменениях $scope через $scope.$apply()

person tommybananas    schedule 09.05.2015
comment
Еще один побочный вопрос, если я определяю модель, например: <input id="photos" ng-model="photos" type="file" accept="image/*" multiple/> Почему я не могу установить здесь что-то вроде var files = $scope.photos в контроллере? $scope.photos кажется undefined? - person noobavistic; 09.05.2015
comment
Я не думаю, что angular поддерживает это. Я бы проверил: stackoverflow.com/a/29018763/580487 или github.com/angular/angular.js/issues/1375 - person tommybananas; 09.05.2015