Retrieving images of child elements within a directive once they have finished loading

I am in the process of developing a directive that will handle scaling for child images. These images are being displayed using ng-repeat, with their URLs fetched from a REST API call made by the parent controller. Below is the code snippet for embedding the directive:

And here is the template structure for the directive:

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

To scale each image, I require information regarding the width and height of other images around it. Currently, I am trying to pass this information to the parent controller upon loading using a child directive bound to the image object. The following is the test code I am working with:

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;
            });
        }
    };
});

The images are getting loaded on the page, and the load callback successfully fetches the dimensions. However, the output in the console (from the parent directive) displays "undefined undefined" for all images, along with an error message stating the inability to set the width/height to an empty value.

I prefer this approach as I need to process the images based on their DOM order. Despite this, I am facing challenges in establishing a connection between the two without resorting to what seems like temporary solutions. Any suggestions or ideas would be greatly appreciated!


The solution provided by New Dev worked perfectly for me. I modified how the element is passed to the parent directive so that it carries the necessary information from the load callback. This method taught me something new about AngularJS today!

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);
                });
            });
        }
    };
});

Check out the console output below:

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

Answer №1

Your console is showing undefined undefined because the images have not finished loading when your loop runs.

If you want to handle the results in the parent component, setting values directly on the exposed scope object may not be the most effective approach. How can you determine when all the images have finished loading? How do you detect changes without using an intensive deep $watch?

A more efficient solution would be to create an API that child directives can access. This involves exposing a function in the parent controller and using require: "^imageDirective" in the child directives.

.directive("imageDirective", function(){
  return {
    // .. add your existing code here
    controller: function($scope){
      var toBeLoaded = 0;
      this.registerChild = function(childElem){
         toBeLoaded++;
      };

      this.childLoaded = function(childElem){
         toBeLoaded--;
         // implement any processing required
      };
    }
  }
})
.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);
          })
       })
    }
  }
}

Answer №2

If you're working with a modern browser (IE 11 or higher), consider using the MutationObserver feature.

Implement this code within the link function of your imageDirective.

var observer = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
            var target = mutation.addedNodes && mutation.addedNodes[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);

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Exploring the possibilities of personalized attributes/directives in HTML and Javascript

My current challenge involves understanding how vue.js and angular.js handle custom directives in HTML. Suppose I have a div like this: <div my-repeat="item in items"></div> To replicate the functionality of vue.js, do I need to search throug ...

What is the best method for installing a package specified as a peerDependency?

I'm in the process of creating a library and I'm looking to figure out how to specify and install a dependency under peerDependencies. I couldn't find any information about this in the npm documentation when using the command npm install: ...

Is there a way to transform data from a CSV format to a JSON format?

I've written the code to fetch data from a device in CSV format. Here are some sample data values: 1,1.635946,1.636609,1.640240,1.636091 2,1.642825,1.640267,1.639013,1.636568 3,1.636835,1.636022,1.637664,1.637144 4,1.641332,1.641166,1.637950,1.640 ...

What is the best way to add this dependency to an AngularJS application?

I am struggling to properly implement the injection of this dependency: https://cdnjs.cloudflare.com/ajax/libs/angularjs-dropdown-multiselect/2.0.0-beta.10/src/angularjs-dropdown-multiselect.js within my project. This is the current dependency injection s ...

Assigning a value to a variable within a directive

Is it possible to modify the position value within a directive? Controller: $scope.position = 0; Using a Chart directive on the page: <chart></chart> Within the chart directive, adjustments are made to the position: angular.module('a ...

Angular Datepicker MinDate function prevents selection of dates "behind" by one

I've encountered an issue with the Angular Bootstrap Datepicker where I'm setting the min-date attribute as follows: <input type="text" class="" uib-datepicker-popup="MM/dd/yyyy" show-button-bar="false" ng-model="vm.date" ng-change= ...

Angular directive does not focus on the text box

I've been working on creating text boxes using a directive and I want only the first text box to be in focus. To achieve this, I am utilizing another directive for focus control. Below is my script: <script> angular.module('MyApp',[]) ...

AJAX success object encounters an uncaught type error

After successfully executing one of my AJAX Posts, there is a logical test with the returned "data" object. Surprisingly, upon page load, JavaScript throws an uncaught type error stating that it cannot read a property of undefined on this line: success: f ...

The children of the <Grid item> component in material-ui are overlapping the parent because they have a width of 100%

One issue I'm facing is that when I have children with full width in a Grid item, they end up overlapping to the right side of their parent. To illustrate this problem, here's the code: https://codesandbox.io/s/rn88r5jmn import React, { Compo ...

MongoDB was successfully updated, however the changes are not being displayed on the redirected

I have implemented the delete action in Node/Express as a web framework, where it is structured within a higher-level route: .delete((req, res) => { db.collection('collection-name').findOneAndDelete({ topic_title: req.body.topic_title}, ...

Why am I unable to set an initial value in useState?

The variable tiles is an array of objects. var originalTiles = tiles; const [newTiles, setNewTiles] = useState(originalTiles); When I log newTiles to the console, I see undefined. What could be the reason for this? ...

Sharing controller methods in Angular.js is a key aspect of enhancing

In my current project, I originally used Knockout for the CMS functionality, but decided to switch to Angular because I preferred its features. One of the key sections in the CMS is dedicated to 'Users', featuring a table where headers can be cli ...

Third party scripts in NextJs fail to function properly when utilizing next/link

Hi there, I'm currently in the process of refactoring an HTML project into a Next.js app. Within this project, I have several 3rd party scripts such as 'jquery.min.js', 'slick.js', and custom JS files. To load these files, I am usi ...

Failure of html and javascript banner in Adobe AIR application

Here's a simple breakdown of the Adobe AIR application setup. The main window of the application is an HTML file, and the `application.xml` file looks like this: <initialWindow> <title>window title</title> <content>i ...

Unable to access the output of a Python file with AngularJS

I am brand new to using angularjs and currently tackling a project that requires my angularjs file to react differently based on the output of a python file. However, I keep encountering this specific error: angular.js:10765 GET http://harsha.seq-technolo ...

Development of multiple interactive 3D models for the website

I have created a stunning 3D model of a city featuring 4 unique buildings, and now I want to take it to the next level by making them interactive. My vision is for users to hover over a building and see text appear on top of it, with the option to click an ...

Unlocking the Count of ng-repeat Elements in Angular JS 1

I'm curious about how to obtain the count of items in ng-repeat using AngularJS. In this particular code, I'm interested in finding out the count of "skill" because I want to set a limit on it. If the count of skills exceeds 5, I only want to dis ...

Avoiding Dropdown Click Propagation in Bootstrap and AngularJS

When using Bootstrap 3 and AngularJS, I have a list group where each item has a dropdown menu aligned to the right. I want it so that when I click on the dropdown, only the dropdown menu is displayed without triggering the "itemSelected" function for the i ...

Exploring Sankey Color Schemes in Apache ECharts

In my Angular app, I am working with a sankey chart and exploring different chart options. Here is the current implementation: this.chartOptions = { color: ["#922752", "#ff9822", "#4390e1", "#53bcbc"], tooltip: { ...

What is the best way to make a canvas element always follow the movement of the mouse cursor

Currently, I am delving into the world of HTML5 canvas and experimenting with a program that rotates a triangle to face the location of the mouse pointer. The functionality is mostly there, but it seems to skip half of the intended rotation. I am curious i ...