AngularJS: $scope.array.push() не обновляет представление, даже с помощью $apply

#angularjs #angularjs-scope #angularjs-ng-repeat #angularjs-controller

#angularjs #angularjs-scope #angularjs-ng-repeat #angularjs-контроллер

Вопрос:

Я пытаюсь изучить AngularJS, и есть одна вещь, которую я не понимаю, которая кажется, что весь интернет решается с помощью $scope.$apply , но я уже использую его, и он ничего не делает.

В принципе, я использую Twitter API для получения временной шкалы, и когда мы прокручиваем снизу, он загружает больше твитов. Эта часть работает, я использую фабрику для этого, но я могу отобразить полученный объект в консоли, у меня здесь нет проблем.

У меня есть такой вид, чтобы отображать данные:

 <div class='timeline' ng-controller='TimelineCtrl' is-scrolled='loadData()'>
    <div class='tweet' ng-repeat='p in posts'>
        <img class='portrait' src='{{p.user.profile_image_url}}' />
        <p>{{p.text}}</p>
        <p class='date'>{{p.created_at}}</p>
    </div>
</div>
  

Мой контроллер выглядит так:

     $scope.posts = [];

    // Load the original tweets list
    TwitterAPI.timeline($scope.count, function(data) {
        $scope.$apply(function() {
            $scope.maxId = data[data.length-1].id;
            $scope.sinceId = data[0].id;
            $scope.posts.push(data);
        });
    });
  

данные являются законными.

То, что я вообще не понимаю, и заставляет меня думать, что это очень легко решить, и я просто не вижу этого, заключается в том, что если я использую ‘= data’ вместо ‘push (data)’, представление обновляется. Даже когда я загружаю больше твитов, если я использую ‘=’, представление обновляется (с заменой содержимого, конечно, что не то, что я хочу).

Примечание: MaxID, поскольку ID и count инициализированы ранее, я не помещал их туда, поскольку не думаю, что это имеет значение.

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

1. Я не верю, что вы можете использовать Array.push() этот способ. Попробуйте concat вместо этого: $scope.posts.concat(data);

2. Вау, так много времени на это, и это потому, что я не знаю свой 101. Это то, что на 100% мне нужно было использовать $scope.posts = $scope.posts.concat(data) для объединения обоих массивов. Спасибо, чувак.

3. Посмотрите этот пример . Похоже, они используют Array#push, и, похоже, это работает.

4. Также Array.prototype.push.apply($scope.posts, data) работает, поскольку данные представляют собой массив, вы хотите добавить их все, не добавляя массив данных как отдельный элемент в массив posts .

Ответ №1:

Проблема, похоже, в том, что Angular NgRepeat останавливается, если он повторяет один и тот же объект более одного раза. Я создал jsFiddle для демонстрации.

В первом разделе вы можете добавлять строки в массив. Первая кнопка всегда добавляет один и тот же строковый объект, в то время как вторая каждый раз создает новый строковый объект. Обратите внимание, что как только вы дважды нажмете первую кнопку, не имеет значения, что вы добавляете в список.

Во втором разделе мы всегда добавляем новый объект, даже если все эти объекты содержат ссылку на один и тот же объект string . Это работает так, как и следовало ожидать.

Итак, чтобы сделать это явным ответом, убедитесь, что вещи, которые вы добавляете в свой список, являются отдельными объектами, и используйте объектные литералы для обеспечения этого, если это необходимо. Я бы предпочел Array#push over Array#concat , потому что последний каждый раз создает новый объект массива, и если у вас много элементов, это приведет к большому оттоку и большой сборке мусора.

HTML-код:

 <div ng-controller="Controller1">
    <button ng-click="addLine()">Add Line</button>
    <button ng-click="addNumber()">Add Number</button>
    <button ng-click="reset()">Reset</button>
    <div>{{lines}}</div>
    <div ng-repeat="line in lines">
        {{line}}
    </div>
</div>

<hr />

<div ng-controller="Controller2">
    <button ng-click="addObject()">Add Object</button>
    <button ng-click="reset()">Reset</button>
    <div>{{objects}}</div>
    <div ng-repeat="obj in objects">
        {{obj.data}}
    </div>
</div>
  

JavaScript:

 (function () {
    var myApp = angular.module('myApp', []);

    myApp.controller('Controller1', function ($scope) {
        $scope.lines = [];

        $scope.addLine = function () {
            $scope.lines.push('Hello world!');
        };

        $scope.addNumber = function () {
            $scope.lines.push('Line '   $scope.lines.length);
        };

        $scope.reset = function () {
            $scope.lines = [];
        };
    });

    myApp.controller('Controller2', function ($scope) {
        $scope.objects = [];

        $scope.addObject = function () {
            var obj = { data: 'Hello world!' };
            $scope.objects.push(obj);
        };

        $scope.reset = function () {
            $scope.objects = [];
        };
    });
})();
  

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

1. Спасибо, Крис, что произойдет, если вам нужно обновить существующий элемент массива? Например, список мог измениться на сервере, и вы хотите удалить его и обновить массив как новыми отдельными элементами, так и обновленными существующими.

Ответ №2:

Я считаю, что если вы структурируете свой ng-repeat как таковой (с отслеживанием по $index), он не остановится на дубликатах:

 <div class='tweet' ng-repeat='p in posts track by $index'>
...
</div>