1

I have a social media feed where users can post videos. I'd like to add a spinner while the video is buffering using AngularJS. This is the HTML part:

<div class="media-list-item col-xs-12" ng-repeat="media in ::videos">
    <div class="embed-responsive embed-responsive-16by9">
        <video controls oncontextmenu="return false;"
               class="embed-responsive-item" frameborder="0" allowfullscreen preload="auto"
               id="{{media.Id}}">
            <source ng-src="{{::media.Url | trustAsResourceUrl}}" />
        </video>
        <i class="fa fa-spinner fa-pulse videoLoader" ng-show="media.loading" aria-hidden="true"></i>
    </div>
</div>

This is my script:

for (var i = 0; i < scope.videos.length; i++) {
var video = scope.videos[i];
video.loading = false;
var videoElement = angular.element(document.getElementById(video.Id))[0];

videoElement.onplaying = function () {
    video.loading = false;
    scope.$apply();
};

videoElement.onwaiting =  function () {
    video.loading = true;
    scope.$apply();
};

}

So I'm loading the videos from media, and set a 'loading' param to trigger the ng-show for each separate media (video), so when the video is 'onwaiting' (buffering) it should show the spinner, when it's 'onplaying' hide it. It works perfectly whenever I have only one video at the specific post, but whenever there are multiple videos in one post, the spinner only displayed on the last video. Any help appreciated.

gabor
  • 13
  • 2

2 Answers2

0

The problem is that the inner functions (event handlers) are referencing to the same video variable, and at the calling time it is equal to last video item. To resolve this issue you, should isolate loop body scope out of declaring of video variable. You can use IIFE, forEach etc. Personally, I would recommend you use .bind for changing scope context:

for (var i = 0; i < scope.videos.length; i++) {
   var video = scope.videos[i];
   video.loading = false;
   var videoElement = angular.element(document.getElementById(video.Id))[0];

   videoElement.onplaying = function (video) {
       video.loading = false;
       scope.$apply();
   }.bind(this, video);

   videoElement.onwaiting =  function (video) {
       video.loading = true;
       scope.$apply();
   }.bind(this, video);
}
Toby Speight
  • 27,591
  • 48
  • 66
  • 103
Maxim Kuzmin
  • 2,574
  • 19
  • 24
0

Just to note here, after several hours of research another solution to this is to separate the functions encapsulated in another function, according to THIS answer:

for (var i = 0; i < scope.videos.length; i++) {
var video = scope.videos[i];
video.loading = false;

var videoElement = angular.element(document.getElementById(video.Id));
videoElement.on('playing', videoOnPlaying(video));
videoElement.on('waiting', videoOnWaiting(video));

function videoOnPlaying(video) {
return function () {
    video.loading = false;
    scope.$apply();
}; }

function videoOnWaiting(video) {
return function () {
    video.loading = true;
    scope.$apply();
}; } }
Community
  • 1
  • 1
gabor
  • 13
  • 2