Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When do we know the actual width of an element in Angular?

I'm tring to create a directive that will center a div.

So far, I have this code:

app.directive("setcenter", function () {
    return {
        scope:{
            setcenter: '='
        },
        link: function (scope, element, attrs) {

            scope.$watch('setcenter', function (newValue) {
                if (newValue == true) {
                    var width = element.width();
                    element.css('position', 'absolute');
                    element.css('top', '80px');
                    element.css('left', '50%');
                    element.css('z-index', '200');
                    element.css('margin-left', '-' + width / 2 + 'px');
                }
            });
        }
    }
});

The problem is the width of the element. The whole point for this directive is that the div that uses this directive, don't have a width set. I want this directive to figure out the width and center the div.

The problem I encounter is that when the directive is invoked, the actual width of the div is not yet known. When I use this in my situation, the div is 800px, but when the page is finished loading, the div is 221px.

So, what can I do to wait till the actual width is known of the div?

like image 550
Martijn Avatar asked Jul 18 '13 14:07

Martijn


4 Answers

First, I only have used this logic when I defined a controller for a directive rather than a link function. So defining it in a link function instead may cause different behavior, but I suggest you try it there first and if you can't get it to work then switch to using a controller.

As far as I can tell, the only change you would need to make this work would be to change $scope to scope calls and $element to element since the dependency injected objects become standard link function parameters.

  $scope.getElementDimensions = function () {
    return { 'h': $element.height(), 'w': $element.width() };
  };

  $scope.$watch($scope.getElementDimensions, function (newValue, oldValue) {
    //<<perform your logic here using newValue.w and set your variables on the scope>>
  }, true);

  $element.bind('resize', function () {
    $scope.$apply();
  });

The idea for this usage came to me after reading a very similar type of usage about watching the $window rather than the current element, the original work can be found here.

Angular.js: set element height on page load

like image 171
James Eby Avatar answered Nov 12 '22 08:11

James Eby


James' answer led me to:

app.directive('measureInto', function () {
        return {
            restrict: 'A',
            link: function (scope, element, attrs) {
                scope.$watch(function() {
                    return element[0].clientWidth;
                }, function(value){
                    scope[attrs.measureInto] = element[0].clientWidth + 10;
                });
            }
        };
    });

So, at runtime, I add this and assign into whatever scope variable I want the width of the element I'm looking for

like image 35
Michael Burdi Avatar answered Nov 12 '22 10:11

Michael Burdi


I had a similar issue and found that the dimensions were reliably correct when all the ng-ifs (or anything else using ngAnimate) on the page had been resolved - it's possible something similar is happening here. If so, this would do the trick without adding any new listeners:

$scope.tryGetElementDimensions = function () {
    if (!angular.element("your-element") || ((angular.element("your-element")[0].classList).contains("ng-animate")
       $timeout(function() {
           $scope.tryGetElementDimensions()
       })
    }
    else {
        $scope.getElementDimensions()
    } 

$scope.getElementDimensions = function (){
    //whatever you actually wanted to do
}

link: function (scope, element, attrs) {
    $scope.tryGetElementDimensions()
}

Angular adds ng-animate and ng-enter, ng-leave classes while it's animating and you can be confident it's finished when these classes have all been removed. $timeout without a second argument just waits for the next digest.

like image 1
strom2357 Avatar answered Nov 12 '22 08:11

strom2357


Can't comment yet, therefore this answer. Found a similar solution like the one strom2357 is suggesting. $timeout works really well to let you know when the dom is ready, and it is super simple. I am using this solution to get the ui-view element width. Found it in a fiddle.

var app = angular.module('app', []);

app.controller('MyController', function($timeout, $scope){

  $timeout(function(){
    //This is where you would get width or height of an element
    alert('DOM ready');
  });

  alert('DOM not ready');

});
like image 1
Jette Avatar answered Nov 12 '22 09:11

Jette