1

I have a simple Angular app, I'm trying to set it up so when a person clicks on an image, a loading message is shown, then when the image loads, it hides.

When I remove scope.loading = false;, the message is shown (obviously), but when I have that line, the message is never shown, even if the image takes 5+ seconds to load.

Can anyone see what's wrong?

app.controller('ProductsCtrl', function($scope, $interval) {
    $scope.images = LayerData['images'];
    $scope.mainImage = '/open/img/'+$scope.images[0].section+'/'+$scope.images[0].file;
    $scope.loading = true;
    $scope.main = function(img) {
        $scope.mainImage = '/open/img/'+img.section+'/'+img.file;
    }
});

app.directive('imageonload', function($timeout) {
    return {
        restrict: 'A',
        link: function(scope, element, attrs) {
            element.bind('load', function() {
                scope.$apply(function() {
                    scope.loading = false;
                });
            });

            scope.$watch(function () { return scope.mainImage; }, function (newValue) {
                if (newValue) {
                    scope.loading = true;
                    element.attr('src', scope.mainImage);
                }
            });

            element.attr('src', scope.mainImage);
        }
    };
});

The relevant HTML is just this, but if you need/want to see the rest let me know

<div class="row wrapper" ng-controller="ProductsCtrl">
    <div col>
        <div row>
            <div col>
                <h1>{$section.title}</h1>
            </div>
        </div>
    </div>



    {literal}
    <div col>
        <div row>
            <div col lg="7" md="7">
                <p ng-show="loading">Loading image...</p>
                <div id="mainImage">
                    <img ng-src="{{mainImage}}" imageonload style="max-width:100%;" />
                </div>
            </div>

            <div col lg="5" md="5">
                {/literal}
                {$sidebar}
                {literal}
            </div>
        </div>
    </div>
    <div col class="imgList">
        <div class="scrolls">
            <div class="imageDiv">


                    <img src="/assets/media/img/{{img.section}}/thumb-100-{{img.file}}" ng-click="main(img)" ng-repeat="img in images" />

            </div>
        </div>
    </div>
    {/literal}
</div>

This is using a mixture of Smarty and Angular so that's what the {literals} and {$...} are for.

TMH
  • 6,096
  • 7
  • 51
  • 88

2 Answers2

1

You're binding to a browser DOM event that is outside of the Angular framework in the link function of your imageonload directive.

You need to notify Angular of the variable update by issuing an $apply.

link: function(scope, element, attrs) {
    element.bind('load', function() {
        scope.$apply(function() {
            scope.loading = false;
        });
    });
}

EDIT

You're binding the src attribute of <img> before the imageonload directive binds to the load event.

<img ng-src="{{mainImage}}" imageonload style="max-width:100%;" />

Instead, get rid of the ng-src declaration in your HTML and set it inside the imageonload directive, where you can ensure that the load event gets bound to first.

Like so:

<img imageonload style="max-width:100%;" />

And then setting src after the bind call should hopefully solve your problem:

link: function(scope, element, attrs) {

    // Bind to load event (Same code as as above)

    // Set src attribute here *after* binding to the load event
    element.attr('src', scope.mainImage);
}
miqh
  • 3,624
  • 2
  • 28
  • 38
  • I can't say much more since you've not included how you actually bind your image path in your HTML. However, keep in mind that the binding to the `load` event must be done before the `src` property is set for your `img` tag, wherever that is. Even if you're not using jQuery, Angular uses jqLite, so this is relevant: http://stackoverflow.com/questions/3877027/jquery-callback-on-image-load-even-when-the-image-is-cached – miqh Jun 21 '14 at 13:55
  • Alright, I've updated my answer in response. Looks like you're doing what I said you shouldn't be doing in my previous comment - you're setting `src` before binding to the load event. – miqh Jun 23 '14 at 08:34
  • Ahh I see what you mean now, thanks. I've updated the JS in my question to what I currently have, does the watch look like a good way to update the image when the mainImage is updated? – TMH Jun 23 '14 at 08:56
0

I'm pretty sure that your problem is, that directives have a thing that's called "isolate scope". That means that the scope in the link-function within the directive is not your controller scope.

Which means that your $scope.loading variable never gets toggled, you have to give your directive a possibility to modify your controller variable.

https://docs.angularjs.org/guide/directive Check out the parts on isolate scope and transclude, that should get you up to speed.

Sorry for not elaborating more closely, just drop me a comment if you need any more help with that.

Cheers, Jan

jfornoff
  • 1,368
  • 9
  • 15