13

I have the following ng-repeat that grabs data from a $http.post call and stores it into $scope.data.

<div ng-repeat="key in [] | range:data.pages">
    <div class="pageBackground" id="page_{{ (key+1) }}" ng-style="{'background-image':'url(/images/{{data.id}}/{{(key+1)}}.png)'}">
    <!-- some random stuff here -->
</div>

What normal happens is the .pageBackground class will load before the background image does on the screen. I'd like nothing to show up until the background-image is actually loaded but I haven't been able to figure that out.

Does anyone have any suggestions?

bryan
  • 8,879
  • 18
  • 83
  • 166
  • Have you tried this approach? [Easiest Way To Delay Loading of Images in Agular @ StackOverflow](http://stackoverflow.com/questions/20229606/what-is-the-easiest-way-to-delay-loading-of-images-in-angular) – SL8t7 Feb 16 '16 at 00:27
  • Can you create a plnkr that reproduces this problem? It seems to me that @sp00m's answer should work. – Joe Enzminger Feb 16 '16 at 03:45

8 Answers8

5

I'm not sure there is a real solution for your problem. A workaround could be to use the Image object, as explained in this answer. For example as a directive:

angular.module("yourModule").directive("showOnceBackgroundLoaded", [function () {
  return {
    restrict: "A",
    scope: false,
    link: function (scope, element, attributes) {
      element.addClass("ng-hide");
      var image = new Image();
      image.onload = function () {
        // the image must have been cached by the browser, so it should load quickly
        scope.$apply(function () {
          element.css({ backgroundImage: 'url("' + attributes.showOnceBackgroundLoaded + '")' });
          element.removeClass("ng-hide");
        });
      };
      image.src = attributes.showOnceBackgroundLoaded;
    }
  };
}]);

Use:

<div ng-repeat="key in [] | range:data.pages">
    <div class="pageBackground" id="page_{{ (key+1) }}" show-once-background-loaded="/images/{{data.id}}/{{(key+1)}}.png">
    <!-- some random stuff here -->
</div>
Community
  • 1
  • 1
sp00m
  • 47,968
  • 31
  • 142
  • 252
  • Thanks for the help but this doesn't seem to work. Once the div re-appears, the background-image is still delayed in appearing – bryan Feb 09 '16 at 20:51
1

Look here for a solution:

How can I tell when a CSS background image has loaded? Is an event fired?

what you do is you create an invisible partner element loading the same image. if and only if that image has been loaded (onload event) you show your other element, so for example:

<div ng-repeat="key in [] | range:data.pages" ng-init="_imageLoaded={}">
    <div ng-show="_imageLoaded[$index]" class="pageBackground" id="page_{{ (key+1) }}" ng-style="{'background-image':'url(/images/{{data.id}}/{{(key+1)}}.png)'}">
    <img ng-show="false" src="/images/{{data.id}}/{{(key+1)}}.png" onload="_imageLoaded[$index] = true;" />
    <!-- some random stuff here -->
</div>
Community
  • 1
  • 1
Patrick Kelleter
  • 2,631
  • 1
  • 13
  • 19
  • Hey Patrick, thanks for the response. I'm not sure what `_imageLoaded={}` is coming from. Anyway you can give m more context. That questions doesn't help me too much :\ – bryan Feb 16 '16 at 16:28
  • hey, i use the imageLoaded object to remeber for each entry if the corresponding image has been loaded. so _imageLoaded.0 will be undefined in the first place - but when the image with $index = 0 is loaded it will put the entry to true - this happens in the onload function. your div on the other side has a ng-show "listening" to this variable. so when it switches from undefined to true the div will pop up. which is when the image has loaded, hence what you were asking for. the _imageLoaded is just one way to implement it – Patrick Kelleter Feb 16 '16 at 16:59
0

You can delay your startup function until the image loads.

  var img = new Image();
  img.onload = function(){
    loadData();
  };
  img.src = 'https://mdn.mozillademos.org/files/5397/rhino.jpg';

Then put an ng-if="imageLoaded" in the html and set that to true in the loadData function.

Ben Felda
  • 1,474
  • 10
  • 15
  • The amount of pages/images are never defined and the images will always be coming from a different source. This method seems a bit tedious for my example but thank you for the recommendation. – bryan Jan 26 '16 at 19:13
0

Here is a directive that embeds your image as a background, and waits to show itself until after the image has loaded. You can include variables inside the "mysrc" parameter as needed to generate different images (such as from within an ng-repeat).

angular.module('foo', [])
  .directive('myPageBackground', function() {
    return {
      scope: {
        key: '=', // used for ID of the element; if you set the ID externally to the directive you could omit this
        mysrc: '=' // used for the URL of the desired background image
      },
      template: '<div ng-show="showMe" class="pageBackground" id="page_{{key}}" ng-style="{\'background-image\':\'url({{mysrc}})\'}"></div>',
      link: function(scope) {
        var img = new Image();
        img.onload = function() {
          scope.showMe = true;
          scope.$apply();
        };
        img.src = scope.mysrc;
      }
    }
  });
.pageBackground {
  width: 200px; height: 200px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="foo">
  <!-- Done with hardcoded values here, just for demo: -->
  <div my-page-background key="'1'" mysrc="'http://placehold.it/200x200'"></div>
  <div my-page-background key="'2'" mysrc="'http://placehold.it/150x150'"></div>
  <div my-page-background key="'3'" mysrc="'http://placehold.it/100x100'"></div>
</div>
Daniel Beck
  • 20,653
  • 5
  • 38
  • 53
0

If you're always using the same class="pageBackground", then you background image is always the same. You can load it in the hidden image, so browser caches it:

<img src="url" style="display:none;"/>

Or preload with javascript on page load:

var img = new Image();
img.src = 'some url';

I believe, that post is triggered with user interaction, which happens definitely after page has been loaded, so the image already sits in browser and it will appear immediately when you apply class="pageBackground"

Hope this helps!

Roman Hutnyk
  • 1,549
  • 9
  • 14
0

You could use ngSrc directive instead of a div. In addition, you could try to use a tiny lazy loading image library such as ng-lazy-img.

I hope that helps!

FedeF
  • 457
  • 3
  • 9
0

Here's a fully-functioning example using the Image object.

function AppCtrl($scope) {
  $scope.images = [{
    src: 'https://farm4.staticflickr.com/3261/2801924702_ffbdeda927_d.jpg'
  }, {
    src: 'https://farm9.staticflickr.com/8455/8048926748_1bc624e5c9_d.jpg'
  }];
  angular.forEach($scope.images, function(image) {
    var img = new Image();
    img.onload = function() {
      $scope.$apply(function() {
        image.loadedSrc = image.src;
      });
    };
    img.src = image.src;
  });
}
.hasSrc {
  color: purple;
  padding: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app>
  <div ng-controller="AppCtrl">
    <div ng-repeat="image in images" ng-class="{ hasSrc: !!image.loadedSrc }" style="background-image: url({{image.loadedSrc}})">
      <h2>THIS IS SOME TEXT BEING USED</h2>
      <p>
        This text is being used for illustration purposes only.
      </p>
    </div>
  </div>
</div>

It should be easy enough to adapt this to suit your purposes.

Sarhanis
  • 1,577
  • 1
  • 12
  • 19
0

You can take help from angular promises. If the image is loaded as promised render the view else not.

You may already checked this link.

preloading-images-in-angularjs-with-promises

Thanks

MKAka
  • 232
  • 2
  • 9