Search code examples
javascriptangularjsangularjs-components

Applying dynamically created CSS to a component using ng-style


I have an angularjs component to create stylized pictures, but the style is not correctly applied the first time I visit a page (refreshing the pages shows the correct style). The CSS that is applied is generated dynamically based on the height and width properties of the image being styled.

Can anybody tell me why the CSS is applied on reload but not on the original load, and how to fix it so the CSS is applied the first time the page is loaded? Thanks!

In HTML I create a stylized picture:

<stylized-picture image-source="someImage.jpg"></stylized-picture>

The stylizedPicture component:

.component('stylizedPicture', {
    templateUrl: 'src/components/stylized-picture.template.html',
    bindings: {
      imageSource: '@imageSource',
    },
    controller: 'StylizedPictureController as stylizedPictureController'
});

stylized-picture.template.html:

<div ng-style="stylizedPictureController.outerCSS">
    <div ng-style="stylizedPictureController.innerCSS"></div>
       <div ng-style="stylizedPictureController.imgDivCSS">
           <img ng-src="{{stylizedPictureController.imageSource}}">
       </div>
    </div>
</div>

stylizedPictureController: The CSS is based on the image width/height.

.controller('StylizedPictureController', StylizedPictureController);

StylizedPictureController.$inject = ['$scope'];
function StylizedPictureController($scope) {
    var ctrl = this;

    ctrl.$onChanges = function() {
      var img = new Image();
      img.addEventListener("load", function() {
        ctrl.imgWidth = img.naturalWidth;
        ctrl.imgHeight = img.naturalHeight;

        // Fancy stuff to stylize image based on img h/w removed
        // simplified for example
        var paddingBottom = 100;
        ctrl.paddingBottom = paddingBottom;
        ctrl.outerCSS = {"padding-bottom: " + paddingBottom + "%"};
        ctrl.innerCSS = {"padding-bottom: " + paddingBottom + "%"};
        ctrl.imgDivCSS = {"padding-bottom: " + paddingBottom + "%"};
      });
      img.src = ctrl.imageSource;
    };
};

I have tried using the image.onLoad() method instead of the ctrl.$onChanges funtion/img eventListener, but have the same results. I have also tried setting ng-style inline, e.g. ng-style="{'padding-bottom': stylizedPictureController.paddingBottom}" but it doesn't make a difference.


Solution

  • I have found a solution, and thought I'd post it here in case anybody else has this problem in the future.

    Adding:

    $scope.$apply()
    

    to the end of the image load EventListener, after the CSS has been generated and stored on the controller, fixes the issue. I believe this is happening because the image load is outside of the angularjs scope, and so when it finishes, angular doesn't know anything has changed and therefore doesn't update the ng-style binding. Forcing it to update the bindings with $scope.$apply() updates the ng-style.