Приложение AngularJS: невозможно обновить $index

Я работаю над приложением в AngularJS 1.6, используя API Giphy.com.

Есть плункер ЗДЕСЬ.

Я перебираю массив «гифов», поступающих из https://api.giphy.com/v1/gifs/trending?api_key=myApyKey, и отображаю их на карточках Bootstrap 4.

Есть просмотр единой функциональности giphy. В контроллере у меня есть:

// Create controller for the "giphyApp" module
app.controller("giphyCtrl", ["$scope", "$http", "$filter", "$timeout", function($scope, $http, $filter, $timeout) {
  var url = "https://api.giphy.com/v1/gifs/trending?api_key=PTZrBlrq8h2KUsRMeBuExZ5nHyn7dzS0&limit=120&rating=G";
  $scope.giphyList = [];
  $scope.search = "";
  $scope.filterList = function() {
    var oldList = $scope.giphyList || [];
    $scope.giphyList = $filter('filter')($scope.giphys, $scope.search);
    if (oldList.length != 0) {
      $scope.pageNum = 1;
      $scope.startAt = 0;
    };
    $scope.itemsCount = $scope.giphyList.length;
    $scope.pageMax = Math.ceil($scope.itemsCount / $scope.perPage);
  };

  $http.get(url)
  .then(function(data) {
      // giphy arary
      $scope.giphys = data.data.data;
      $scope.filterList();
      console.info($scope.giphys);

      // Paginate
      $scope.pageNum = 1;
      $scope.perPage = 24;
      $scope.startAt = 0;
      $scope.filterList();

      $scope.currentPage = function(index) {
        $("html, body").animate({
          scrollTop: 0
        }, 500);
        $timeout( function(){
          $scope.pageNum = index + 1;
          $scope.startAt = index * $scope.perPage;
        },0);
      };

      $scope.prevPage = function() {
        if ($scope.pageNum > 1) {
          $scope.pageNum = $scope.pageNum - 1;
          $scope.startAt = ($scope.pageNum - 1) * $scope.perPage;
        }
      };

      $scope.nextPage = function() {
        if ($scope.pageNum < $scope.pageMax) {
          $scope.pageNum = $scope.pageNum + 1;
          $scope.startAt = ($scope.pageNum - 1) * $scope.perPage;
        }
      };

      $scope.selectedIndex = null;
      $scope.selectedGiphy = null;

      $scope.fetchSinglegGiphy = function(giphy, index) {
        console.info(index);
        $scope.selectedIndex = index;
        $scope.selectedGiphy = giphy;
      }
    });
}]);

Сетки

<div class = "row grid" ng-if = "giphyList.length > 0">
  <div data-ng-repeat = "giphy in giphyList | limitTo : perPage : startAt" 
       class = "col-xs-12 col-sm-6 col-lg-4 col-xl-3 d-flex mb-4">
    <div class = "giphy d-flex flex-column w-100">
      <div class = "thumbnail pb-2 text-center" data-toggle = "modal"
           data-target = "#giphyModal"
           ng-click = "fetchSinglegGiphy(giphy, $index)">
        <img ng-src = "{{giphy.images.downsized.url}}" class = "img-fluid">
      </div>
      <div class = "text mt-auto">
        <p class = "m-0 meta">{{giphy.import_datetime | dateParse | date : "MMMM dd y" }}</p>
        <p class = "rating m-0">
          <i class = "fa fa-star" aria-hidden = "true"></i> {{giphy.rating | capitalize}}
        </p>
        <ul class = "list-unstyled mb-0 text-center">
          <li ng-if = "giphy.username != ''" class = "text-muted">{{giphy.type | capitalize}} file uploaded by
            <br><strong>{{giphy.username | capitalize }}</strong>
          </li>
          <li ng-if = "giphy.username == ''" class = "text-muted">{{giphy.type | capitalize}} file uploaded by
            <br> <strong>Unknown</strong>
          </li>
          <li class = "h6">{{giphy.title | titlecase }}</li>
        </ul>
      </div>
    </div>
  </div>
</div>

Модальный

<div class = "modal fade" id = "giphyModal">
  <div class = "modal-dialog">
    <div class = "modal-content">
      <div class = "modal-header">
        <h4 class = "modal-title h-3">{{selectedGiphy.title | titlecase }}</h4>
        <button type = "button" class = "close" data-dismiss = "modal">
        <span>&times;</span>
        </button>
      </div>
      <div class = "modal-body">
        <div class = "row">
          <div class = "col-12">
            <div class = "image image text-center">
              <img ng-src = "{{selectedGiphy.images.original.url}}"
                   alt = "{{selectedGiphy.title }}" class = "img-fluid">
            </div>
          </div>
        </div>
      </div>
      <div class = "modal-footer justify-content-between">
        <div class = "text-muted">Image ID: {{selectedGiphy.id}}</div>
        <div class = "btn-group btn-group-sm">
          <button type = "button" class = "btn btn-success" data-dismiss = "modal">
             <i class = "fa fa-times-circle"></i> Close
          </button>
        </div>
      </div>
    </div>
  </div>
</div>

Эта часть приложения работает нормально.

Я ожидал, что смогу легко добавить следующая и предыдущая картинка в модальное окно выше:

<div class = "controls text-center">
    <a href = "#" ng-click = "fetchSinglegGiphy(giphy, $index = $index - 1)" class = "left">
      <i class = "fa fa-chevron-left"></i>
    </a>
    <a href = "#" ng-click = "fetchSinglegGiphy(giphy, $index = $index + 1)" class = "right">
      <i class = "fa fa-chevron-right"></i>
    </a>
</div>

К моему удивлению, это не работает. Когда я нажимаю на любой из элементов управления, файл GIF остается прежним, а весь текст в модальном окне исчезает.

Вопросы:

  1. Что не так с этим подходом?
  2. Вы видите жизнеспособную альтернативу?

Не могли бы вы попробовать сделать $index - 1 вместо $index = $index - 1?

Giovanni 18.07.2019 13:49

@ Джованни Я сделал. Результат тот же.

Razvan Zamfir 18.07.2019 13:50

$index создается/управляется ng-repeat, вам не следует пытаться его редактировать.

Pierre Emmanuel Lallemant 18.07.2019 13:51

Может быть, вы могли бы сохранить индекс в локальном хранилище или что-то в этом роде?

Giovanni 18.07.2019 13:57

Ваш второй блок кода находится за пределами ng-repeat? Вы не можете использовать $index вне его.

vrdrv 18.07.2019 14:53

@vrdrv Я думал об этом. Вот почему я попробовал fetchSinglegGiphy(giphy, $scope.index - 1)".

Razvan Zamfir 18.07.2019 14:55

Разве не должно быть fetchSinglegGiphy(giphy, $scope.selectedIndex - 1)? И вы должны инициализировать selectedIndex чем-то (0?).

vrdrv 18.07.2019 14:59
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
7
304
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Когда вы перемещаете giphy в модальное окно, вы также должны отправить $index (что вы и сделали), чтобы в модальном окне вы знали, кто ваш индекс, независимо от $index.

<div class = "controls text-center">
    <a href = "#" ng-click = "fetchSinglegGiphy(giphy, selectedIndex - 1)" class = "left"><i class = "fa fa-chevron-left"></i></a>
    <a href = "#" ng-click = "fetchSinglegGiphy(giphy, selectedIndex + 1)" class = "right"><i class = "fa fa-chevron-right"></i></a>
 </div>

Эти элементы управления внутри div с директивой ng-repeat?

Miri Gold 18.07.2019 14:01

Нет. По коду видно, что они снаружи.

Razvan Zamfir 18.07.2019 14:03

Я хочу получить следующий/предыдущий элемент в сетке. Я тоже пробовал fetchSinglegGiphy(giphy, $scope.index - 1)".

Razvan Zamfir 18.07.2019 14:11

Я изменил свой ответ. Посмотри сейчас.

Miri Gold 18.07.2019 14:19

Б/у fetchSinglegGiphy(giphy, selectedIndex - 1). Результат тот же.

Razvan Zamfir 18.07.2019 14:24

When I click any one of the controls, the GIF file remains the same

Проверьте, соответствует ли giphy тому, что вы ожидаете:

$scope.fetchSinglegGiphy = function(giphy, index) {
   $scope.selectedIndex = index;
   $scope.selectedGiphy = giphy;
   if ( giphy != $scope.giphyList[index] ) {
      $scope.selectedGiphy = $scope.giphyList[index];
   };
};

Обновите selectedGiphy, если это не так.

Я добавил плунжер. Пожалуйста, взгляните на это. :)

Razvan Zamfir 26.07.2019 13:53

Вы не только используете $index вне блока ng-repeat, но также используете giphy за пределами этого блока, где он не определен.

Я предполагаю, что у вас есть $scope.giphyList, определенный в вашем контроллере. Так что все, что вам нужно сделать, это отправить файл index.

$scope.selectedIndex = null;
$scope.selectedGiphy = null;

$scope.fetchSinglegGiphy = function(index) {
   $scope.selectedIndex = index;
   $scope.selectedGiphy = $scope.giphyList[index];
}
<div class = "controls text-center">
    <a href = "#" ng-click = "fetchSinglegGiphy(selectedIndex-1)" class = "left">
      <i class = "fa fa-chevron-left"></i>
    </a>
    <a href = "#" ng-click = "fetchSinglegGiphy(selectedIndex+1)" class = "right">
      <i class = "fa fa-chevron-right"></i>
    </a>
</div>

Надеюсь, это поможет, Ваше здоровье

Я обновил свой код - добавил весь код контроллера. Пожалуйста, посмотрите и обновите свой ответ.

Razvan Zamfir 19.07.2019 21:59

Я думаю, что мой ответ остается в силе. Вы перебираете gimphyList в своем ng-repeat. Вы можете вызвать fetchSinglegGiphy с $index там. Поэтому, когда вы нажимаете на миниатюру, $index назначается $scope.selectedIndex, а $scope.giphyList[index] — $scope.selectedGiphy. На данный момент ваш selectedIndex является $index выбранной миниатюры. Вне ng-repeat, когда вы вызываете fetchSinglegGiphy для перехода к следующему или предыдущему gimphy, вам нужно отправить selectedIndex +/- 1.

Umut 19.07.2019 22:19

Я реализовал ваш ответ, к сожалению, не только не работает, но и разрушает то, что у меня уже есть. :(

Razvan Zamfir 19.07.2019 22:22

Я создал ручку CodePen. У него нет модальности, но он делает то, что вы пытаетесь сделать. связь

Umut 20.07.2019 01:14

Я добавил плунжер. Пожалуйста, взгляните на это. Спасибо!

Razvan Zamfir 26.07.2019 13:52
Ответ принят как подходящий

index.html

<div class = "modal fade" id = "giphyModal">
   <div class = "modal-dialog">
      <div class = "modal-content">
         <div class = "controls text-center">
            <a href = "#" ng-click = "fetchSinglegGiphyModal('prev')" class = "left"><i class = "fa fa-chevron-left"></i></a>
            <a href = "#" ng-click = "fetchSinglegGiphyModal('next')" class = "right"><i class = "fa fa-chevron-right"></i></a>
         </div>
         <div class = "modal-header">
            <h4 class = "modal-title h-3">
               {{ selectedGiphy.title | capitalize }}
            </h4>
            <button type = "button" class = "close" data-dismiss = "modal">
               <span>&times;</span>
            </button>
         </div>
         <div class = "modal-body">
            <div class = "row">
               <div class = "col-12">
                  <div class = "image image text-center">
                     <img ng-src = "{{ selectedGiphy.images.original.url }}" alt = "{{ selectedGiphy.title }}" class = "img-fluid" image-on-load ng-hide = "modalImageLoading" />
                     <div class = "spinner-border" ng-if = "modalImageLoading"></div>
                  </div>
               </div>
            </div>
         </div>
         <div class = "modal-footer justify-content-between">
            <div class = "text-muted">
               Image ID: {{ selectedGiphy.id }}
            </div>
            <div class = "btn-group btn-group-sm">
               <button type = "button" class = "btn btn-success" data-dismiss = "modal">
                  <i class = "fa fa-times-circle"></i> Close
               </button>
            </div>
         </div>
      </div>
   </div>
</div>

МОДИФИКАЦИИ:

<a href = "#" ng-click = "fetchSinglegGiphyModal('prev')" class = "left"><i class = "fa fa-chevron-left"></i></a>
<a href = "#" ng-click = "fetchSinglegGiphyModal('next')" class = "right"><i class = "fa fa-chevron-right"></i></a>

fetchSinglegGiphyModal обрабатывает как предыдущий, так и следующий Гифи на основе условия, которое действует на значение аргумента prev или next, которое мы передаем в функцию.

Я также добавил следующий код, чтобы ввести загрузчик, который поставляется с начальной загрузкой, чтобы указать, что изображение GIF загружается в фоновом режиме.

<div class = "image image text-center">
   <img ng-src = "{{ selectedGiphy.images.original.url }}" alt = "{{ selectedGiphy.title }}" class = "img-fluid" image-on-load ng-hide = "modalImageLoading" />
   <div class = "spinner-border" ng-if = "modalImageLoading"></div>
</div>

app.js

$scope.modalImageLoading = false;

$scope.fetchSinglegGiphy = function (giphy, index) {
   $scope.selectedIndex = index;
   $scope.selectedGiphy = giphy;
   $scope.modalImageLoading = true;
};

$scope.fetchSinglegGiphyModal = function (dir) {
   let selectedGiphy = null;
   if (dir === "prev") $scope.selectedIndex -= 1; // To load previous
   else if (dir === "next") $scope.selectedIndex += 1; // To load next

   selectedGiphy = $scope.giphyList[$scope.selectedIndex];
   if (selectedGiphy) {
      $scope.selectedGiphy = selectedGiphy;
      $scope.modalImageLoading = true;
   }
};

$scope.fetchSinglegGiphyModal действует в соответствии с условием, которое мы передаем в параметре, и показывает соответствующие данные Гифи.

Я реализовал пользовательский директива, чтобы показать элемент изображения и скрыть загрузчик при загрузке изображения. Вот код директивы:

app.directive('imageOnLoad', function () {
   return {
      restrict: 'A',
      controller: 'giphyCtrl',
      link: function (scope, element, attrs) {
         element.bind('load', function () {
            scope.$apply(function () {
               scope.modalImageLoading = false;
            });
         });
      }
   }
});

Вот модифицированный плункер код.

Надеюсь, поможет!

Другие вопросы по теме