How can I get directive to fire after view loaded?

前端 未结 3 1658
眼角桃花
眼角桃花 2021-01-22 11:40

I know this has been asked a thousand time, but I think I\'ve tried every solution I\'ve read and I can\'t seem to get it to work.

I have an API from which I\'m getting

相关标签:
3条回答
  • 2021-01-22 12:08

    As others pointed out you need to wait until all that stuff is rendered which happens only after your api call returns with result. The signal for your directive would be the change of gallery.images array. So, make your directive watch for it like the following.

    Please pay attention to the newly introduced isolated scope - to me it sounds like it should be isolated, but I can't really say it for sure because I don't know the rest of your app code.

    Anyway, it tells angular that whatever value is used in photoswipe attribute in the template (see below) should be bound to the images property of directive's scope. Using such a construct ensures that this will happen on each and every change of gallery.images regardless of when and how it happens. All you need to do outside is just to change the collection when api call finishes and the rest will just work. No events (to me they are pretty cumbersome in angular 1.x), no connections between different components, no mixing of concerns. Pretty clean solution. However, you need to understand how $watchCollection() works, it's a bit different from regular $watch in that it performs shallow collection scan and does not compare objects in collection themselves but only their references.

    Another important moment is a cleanup. You must make sure that when something is getting removed from the collection then corresponding events and whatever else that plugin binds to elements is properly destroyed, otherwise you'll end up having memory leaks, unexpected events and poor performance.

    App.directive('photoswipe', function ($timeout) {
    return {
        replace: false,
        restrict: 'A',
        scope: {
            "images": "=photoswipe"
        },
        link: function photoSwipeLink(scope, element, attr) {
            scope.$watchCollection('images', function(value){
                // here $timeout() is necessary because this event will be fired
                // immediately after collection change
                // thus not giving angular time to render it
                // so, jquery plugin will be fired on the next angular digest
                // and everything will be rendered by then.
                $timeout(function () {
                    angular.element('#gallery a').photoSwipe({
                        enableMouseWheel: false,
                        enableKeyboard: false
                    });
                    // and now figure out how to clean it up!
                });
            });
        }
      };
    });
    

    Now to the template. Please note that first time I use photoswipe="gallery.images" attribute. This is what tells angular to bind gallery.images to the scope of photoswipe directive to images property.

    Since directive now introduces its own isolated scope everything inside it should account to this fact. That's why ng-repeat="image in images".

    <h1>{{ gallery.headline }}</h1>
    <ul id="gallery" photoswipe="gallery.images">
      <li class="gallery" ng-repeat="image in images">
        <a href="{{ image.url }}" alt="image.caption">
          <img ng-src="{{ image.thumb_url }}" class="thumb" alt="{{ image.caption }}" style="display: block;">
          <h3 class="headline">{{ image.caption }}</h3>
        </a>
      </li>
    </ul>
    

    I honestly did not test this code but apart from possible syntax errors it should work. Also, I want to stress one more time that I don't know the structure of the rest of your application, so introducing isolated scope can break it if something inside your directive is bound to some higher scope - but to me it is bad idea by itself because it introduces hidden interconnections between components.

    0 讨论(0)
  • 2021-01-22 12:17

    Yes, you can use events to signal when data are there. Though you maybe have to use $rootScope, depends on use case.

    But I think the most probable reason for your error is, that all $watchers are called by AngularJS the first time when initializing them with an undefined value.

    You should check that in your $watch function, could solve your problem immediately.

    0 讨论(0)
  • 2021-01-22 12:30

    I think this is happening because Angular is processing your directive before the results are bound to the list. It is probably a race condition. One way to solve this is to do

    $scope.$broadcast("picsDownloaded" ... 
    

    event in your controller after you get the results. On the directive, instead of

    scope.$watch(attr.photoswipe ....
    

    do this

    scope.$on("picsDownloaded" ....
    

    and in that handler you apply the Jquery Plugin.

    0 讨论(0)
提交回复
热议问题