加载内部指令后访问子元素图像

Access child element images after load inside directive

我正在尝试创建一个指令来对子图像执行缩放。这些图像是使用 ng-repeat 嵌入的,URL 来自父控制器中的 REST API 调用。这是指令的嵌入:

这是指令的模板:

<div>
    <img image-directive-item ng-src="{{image.url}}" ng-style="{'width': {{image.width}}, 'height': {{image.height}}}" ng-if="image.done" />
</div>

要缩放每张图片,我需要了解其周围其他图片的相关信息:width/height。目前,我正试图在绑定到图像的子指令中加载时将此信息传递给父级。这是我正在测试的代码:

angular.module("myApp")
.directive("imageDirective", function() {
    return {
        scope: { images: '=images'},
        link: function(scope){

            for(var i = 0; i < scope.images.length; i++) {
                console.log(scope.images[i].width, scope.images[i].height);
            }

        },
        templateUrl: _contextPath + 'static/html/imageDirective.html'
    };
})
.directive("imageDirectiveItem", function() {
    return {
        link: function(scope, element) {
            element.bind("load", function(e) {
                scope.image.width = this.naturalWidth;
                scope.image.height = this.naturalHeight;
            });
        }
    };
});

图像正在页面上加载,加载回调可以读取尺寸,但是控制台中的输出(来自父指令)对于所有图像都是 "undefined undefined",并且出现无法读取的错误将 width/height 设置为空值。

我想以这种格式进行处理,因为我需要按照它们在 DOM 中出现的顺序处理图像。然而,我正在努力让两者联系起来,而不是诉诸黑客。有什么想法吗?


下面的 New Dev 提供的解决方案对我有用。修改了将元素传递给父指令的方式,以便它从加载回调传递对象,其中包含我需要的信息。今天学到了一些关于 Angular 的新知识!

angular.module("myApp")
.directive("imageDirective", function() {
    return {
        scope: { images: '=images'},
        controller: function($scope){

            var toBeLoaded = $scope.images.length;

            this.childLoaded = function(childElement) {
                console.log(childElement.naturalWidth, childElement.naturalHeight);

                if(--toBeLoaded == 0) {
                    allChildrenLoaded();
                }
            };

            function allChildrenLoaded() {
                console.log("All children loaded.");
            }

        },
        templateUrl: _contextPath + 'static/html/imageGrid.html'
    };
})
.directive("imageDirectiveItem", function() {
    return {
        require: "^imageDirective",
        link: function(scope, element, attrs, imageDirectiveCtrl) {
            element.bind("load", function() {
                var e = this;
                scope.$apply(function() {
                    imageDirectiveCtrl.childLoaded(e);
                });
            });
        }
    };
});

控制台输出:

414 415
507 338
366 468
432 395
500 354
507 338
414 414
478 358
507 338
All children loaded.

如果您使用的是最新的浏览器(IE >= 11),您可以尝试MutationObserver

imageDirective 的 link 函数中试试这个。

var observer = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
            var target = mutation.addedNodes && mutation.addNodes[0],
                targetScope; 
            if (target) {
                target = angular.element(target);
                targetScope = target.scope();
                console.log(targetScope.width, targetScope.height) 
            }
        });    
});

var config = {childList: true};

observer.observe(element[0], config);

您打印 undefined undefined 的原因是因为在您执行 for 循环时已经加载了 none 个图像。

如果您需要 "process"(如您所说)父级中的结果,那么在作用域公开的对象上设置值可能不是最佳方法。例如,您如何知道它已完成加载?如果没有昂贵的深度 $watch,您怎么知道事情已经发生了变化?

我认为更好的方法是公开子指令可以调用的 API。这是通过在父控制器上公开一个函数并在子控制器上使用 require: "^imageDirective" 来完成的。

.directive("imageDirective", function(){
  return {
    // .. whatever you currently have
    controller: function($scope){
      var toBeLoaded = 0;
      this.registerChild = function(childElem){
         toBeLoaded++;
      };

      this.childLoaded = function(childElem){
         toBeLoaded--;
         // do whatever processing you need
      };
    }
  }
})
.directive("imageDirectiveItem", function(){
  return {
    require: "^imageDirective",
    link: function(scope, element, attrs, imageDirectiveCtrl){
       imageDirectiveCtrl.registerChild(element);

       element.bind("load", function(){
          scope.$apply(function(){
             imageDirectiveCtrl.childLoaded(element);
          })
       })
    }
  }
}