문제

UPDATE Plunker to project: http://plnkr.co/edit/oKB96szQhqwpKQbOGUDw?p=preview

I have an AngularJS project that uses AngularJS Bootstrap grids. I need all of the grid elements to have the same size so they stack properly. I created an angularJs directive that auto resizes the grid element when placed in said grid element. I have 2 directives that do this for me Directive 1: onload Directive 2: imageonload

Directive 2 works. If the grid element uses an image, after the image loads then the directive triggers an event that sends the grid elements height to all other grid elements. If that height sent out via the event is greater than that of the grid element which is listening to the event then that listening grid element changes it's height to be the greater height. This way the largest height becomes the height for all the grid elements.

Directive 1 does not work. This one is placed on the outer most grid elements html element, and is triggered when the element loads. The problem is that when the element loads and the onload directive is called AngularJS has not yet filled out the data in said grid element. The outcome is that the real height after AngularJS data binds is not broadcast as an event.

My only solution I have thought of (but haven't tried) is to add an image url to an image that exists but doesn't have any data in it, and place that in the grid element (the one that didn't have any images before placing the blank one in). I could then call imageonload instead of onload and I pretty sure the angularjs data binding will have taken place by then.

the problem is that that is pretty hacky. I would rather be able to have not an image in the grid element, and be able to call my custom onload directive and have the onload directive calculate the height AFTER angularJS data binds to all of the data binding variables in the grid element.

Here is my imageonload directive

.directive('imageonload', function($rootScope) {
    return {
    restrict: 'A',
    link: function(scope, element, attrs) {
        scope.heightArray = [];
        scope.largestHeight = 50;
        element.bind('load', function() {
            broadcastThumbnailHeight();
        });

        scope.$on('imageOnLoadEvent', function(caller, value){
            var el = angular.element(element);
            var id = el.prop('id');
            var pageName = el.prop('title');
            if(pageName == value[0]){
                if(scope.largestHeight < value[1]){
                   scope.largestHeight = value[1];
                    var nestedString = el.prop('alt');
                    if(nestedString == "")
                        nestedString = "1";
                    var nested = parseInt(nestedString);
                    nested = nested - 1;
                    var inte = 0;
                    var thumbnail = el["0"];
                    var finalThumbnailContainer = thumbnail.parentElement;
                    while(inte != nested){
                        finalThumbnailContainer = finalThumbnailContainer.parentElement;
                        inte++;
                    }
                    var innerEl = angular.element(finalThumbnailContainer);
                    var height = value[1];
                    innerEl.height(height);
                }
            }
        });
        scope.$on('findHeightAndBroadcast', function(){
            broadcastThumbnailHeight();


        });
        scope.$on('resetThumbnailHeight', function(){
            scope.largestHeight = 50;


        });
        function broadcastThumbnailHeight(){
            var el = angular.element(element);
            var id = el.prop('id');
            var alt = el.prop('alt');
            if(alt == "")
                alt = "1";
            var nested = parseInt(alt);
            nested = nested - 1;
            var pageName = el.prop('title');
            var inte = 1;
            var thumbnail = el["0"];
            var finalThumbnail = thumbnail.parentElement;
            while(inte != nested){
                finalThumbnail = finalThumbnail.parentElement;
                inte++;
            }
            var elZero = el["0"];
            var clientHeight = finalThumbnail.clientHeight;
            var arr = [];
            arr[0] = pageName;
            arr[1] = clientHeight;
            $rootScope.$broadcast('imageOnLoadEvent', arr);
        }
    }
};

})

And here is my onload directive

.directive('onload', function($rootScope) {
return {
    restrict: 'A',
    link: function(scope, element, attrs) {
        scope.largestHeight=100;
        getHeightAndBroadcast();
        scope.$on('onLoadEvent', function(caller, value){
            var el = angular.element(element);
            var id = el.prop('id');
            var pageName = el.prop('title');
            if(pageName == value[0]){
                if(scope.largestHeight < value[1]){
                    scope.largestHeight = value[1];
                    var height = value[1];
                    el.height(height);
                }
            }
        });
        function getHeightAndBroadcast(){
            var el = angular.element(element);
            var h = el["0"].children;
            var thumbnailHeightElement = angular.element(h);
            var pageName = el.prop("title");
            var clientHeight = thumbnailHeightElement["0"].clientHeight;
            var arr = [];
            arr[0] = pageName;
            arr[1] = clientHeight;
            if(clientHeight != undefined)
                $rootScope.$broadcast('onLoadEvent', arr);
        }
    }
};

})

Here is an example of one of my grid elements that uses imageonload. Note the imageonload directive in the image html element. This works. There is also an onload directive on the outer most html of the grid element. That does not work. I have stepped through this carefully in Firebug and saw that the onload was calculating the height before AngularJS data binding was complete.

<div class="thumbnail col-md-3" id="{{product.id}}" title="thumbnailAdminProductsGrid" onload>
<div class="row">
    <div class="containerz">
        <div class="row-fluid">
            <div class="col-md-2"></div>
            <div class="col-md-7">
                <div class="textcenterinline">
                    <!--tag--><img class="img-responsive"  id="{{product.id}}" title="imageAdminProductsGrid" alt=6 ng-src="{{product.baseImage}}" imageonload/><!--end tag-->
                </div>
            </div>
        </div>
    <div class="caption">
        <div class="testing">
            <div class="row-fluid">
                <div class="col-md-12">
                    <h3 class="">
                        <!--tag--><a href="javascript:void(0);" ng-click="loadProductView('{{product.id}}')">{{product.name}}</a><!--end tag-->
                    </h3>
                </div>
            </div>

            <div class="row-fluid">
                <div class="col-md-12">
                    <p class="lead"><!--tag--> {{product.price}}</p><!--end tag-->
                </div>
            </div>
            <div class="row-fluid">
                <div class="col-md-12">
                    <p><!--tag-->{{product.inStock}} units available<!--end tag--></p>
                </div>
            </div>
            <div class="row-fluid">
                <div class="col-md-12">
                    <p class=""><!--tag-->{{product.generalDescription}}<!--end tag--></p>
                </div>
            </div>
            <!--tag-->
            <div data-ng-if="product.specialized=='true'">
                <div class="row-fluid">
                    <div class="col-md-12" ng-repeat="varCat in product.varietyCategoriesAndOptions">
                        <b><h4>{{varCat.varietyCategoryName}}</h4></b>
                        <select ng-model="varCat.varietyCategoryOption" ng-options="value.varietyCategoryOptionId as value.varietyCategoryOptionValue for (key,value) in varCat.varietyCategoryOptions">
                        </select>
                    </div>
                </div>
            </div>
            <!--end tag-->
            <div class="row-fluid">
                <div class="col-md-12">
                    <!--tag--><div ng-if="product.weight==0"><b>Free Shipping</b></div><!--end tag-->
                </div>
            </div>


        </div>
    </div>
    </div>
</div>

Here is an example of one of the html for one of my grid elements that only uses the "onload" directive and not "imageonload"

<div class="thumbnail col-md-3" title="thumbnailCouponGrid" onload>
<div class="innnerContainer">

    <div class="text-center">
        {{coupon.name}}
        <br />
        <br />
        <b>Description</b>
        <br />
        {{coupon.description}}
        <br />
        <br />
        <button class="btn btn-large btn-primary" ng-click="goToCoupon()">View Coupon Details</button>
    </div>

</div>

The imageonload function might look a little confusing because I use the img html attribute "alt" to signal to the directive how many levels the imageonload is placed below the outermost html for the grid element. We have to have this so the directive knows which html element to set the new height on. also I use the "title" attribute to set which grid this grid resizing is for (that way you can use the directive multiple times on the same page for different grids and not have the events for the wrong grid triggered).

Does anyone know how I can get the "onload" directive to get called AFTER angularJS binds to the grid element?

Just for completeness here are 2 images (almost looks like just 1), the second is a grid that contains grid elements that have images and use the "imageonload" directive and the first is a grid that contains grid elements that do not use images and only uses the "onload" directive.

enter image description here enter image description here

도움이 되었습니까?

해결책

Encapsulating your getHeightAndBroadcast(); function within $timeout call seems to do the job.

Please see this Plunker: http://plnkr.co/edit/cncatsnFBrYm57tgPKHr?p=preview

It's a fork of yours with a small addition to your script.js namely:

Inject $timeout

link: function(scope, element, attrs, $timeout) {

Encapsulate getHeightAndBroadcast(); function within $timeout call

    // Use of $timeout with 0 delay fixes function applied before DOM update completion.
    $timeout(function() {
      getHeightAndBroadcast();  
    }, 0, true);

Hoping this is it! You might call $timeout without optional delay and invokeApply parameters.

Here is the official $timeout documentation: https://docs.angularjs.org/api/ng/service/$timeout

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top