最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Why does this width watching AngularJS directive not work upon switching Bootstrap tabs? - Stack Overflow

programmeradmin0浏览0评论

I have two Bootstrap tabs on a page and each tab displays a list. I want each list item to be a square and wrote this AngularJS directive to set the height of each list item to be equal to its Bootstrap controlled dynamic width:

app.directive('squareDiv', function () {
    return {
        restrict: "A",
        link: function (scope, element,attr) {
            scope.getWidth = function () {
                return element.width();
            };
            scope.$watch(scope.getWidth, function (width) {
                element.css({ 
                    height: width + 'px'
                });
            }, true);
        }
    };
});

When I load this page, the first tab's list items show up as squares which means the directive is working. But when I click on another tab, the list items there have the default height of 100 px. When I switch back to the first tab, even the list items there have the default height now.

What is going wrong here?

UPDATE 1:

Here is the link to a plunker that illustrates this problem:

UPDATE 2:

Plunker with Bootstrap replaced by UI-Bootstrap that still has the same problem:

UPDATE 3:

Plunker with watch for tab switch instead of width change (workaround for the original problem of width watch not working correctly; will need to use a separate directive for adapting to width changes due to window resizing):

I would still like to understand why the width watch stops working correctly after a tab switch.

I have two Bootstrap tabs on a page and each tab displays a list. I want each list item to be a square and wrote this AngularJS directive to set the height of each list item to be equal to its Bootstrap controlled dynamic width:

app.directive('squareDiv', function () {
    return {
        restrict: "A",
        link: function (scope, element,attr) {
            scope.getWidth = function () {
                return element.width();
            };
            scope.$watch(scope.getWidth, function (width) {
                element.css({ 
                    height: width + 'px'
                });
            }, true);
        }
    };
});

When I load this page, the first tab's list items show up as squares which means the directive is working. But when I click on another tab, the list items there have the default height of 100 px. When I switch back to the first tab, even the list items there have the default height now.

What is going wrong here?

UPDATE 1:

Here is the link to a plunker that illustrates this problem: http://plnkr.co/edit/Pc7keuRXR9R3U6AhZmFE?p=preview

UPDATE 2:

Plunker with Bootstrap replaced by UI-Bootstrap that still has the same problem: http://plnkr.co/edit/rLE1wUAHKzJP6FVnGF6b?p=preview

UPDATE 3:

Plunker with watch for tab switch instead of width change (workaround for the original problem of width watch not working correctly; will need to use a separate directive for adapting to width changes due to window resizing): http://plnkr.co/edit/Y4Goe0cexq979JsIcBxl?p=preview

I would still like to understand why the width watch stops working correctly after a tab switch.

Share Improve this question edited Nov 2, 2015 at 12:11 Price asked Oct 29, 2015 at 12:32 PricePrice 2,7034 gold badges19 silver badges43 bronze badges 12
  • 2 It may be because the tab is hidden, so its contents don't have dimensions yet. Anyway, it would help if you made a plunker or something. – George Kagan Commented Oct 29, 2015 at 12:39
  • 1 Thats right, a plunker could be of great help! – Ignacio Villaverde Commented Oct 29, 2015 at 13:34
  • 1 @Price - I have absolutely no knowledge about angular, but can't you just use bootstraps shown.bs.tab event to set the height of the .holder elements inside the list items ? Here is an example Plunker. Just like i said i have no clue if this is the correct way to do this with angular, but it works. – DavidDomain Commented Oct 31, 2015 at 13:34
  • 1 @Price - Sure, no problem. It's probably better to wait and see if someone with a better background on angular can tell you what's going on here. In general i always rely on bootstrap events whenever i want to manipulate content inside bootstraps ponents, but since i have no angular skills, there may be a totally different solution to your problem, which i am not aware of. ;-) – DavidDomain Commented Oct 31, 2015 at 13:50
  • 2 See also github./angular/angular.js/issues/…. – gkalpak Commented Nov 2, 2015 at 11:08
 |  Show 7 more ments

2 Answers 2

Reset to default 8 +25

Your directive's watcher is one digest cycle behind of what's happening. If you look closely enough, you will see that the very first tab clicking does not do much (nothing logged to console). What you see is your active tab's elements being resized to the hidden tabs elements heights. So the hidden tab's heights are correct. I would strongly advise using the angular version of bootstrap instead of the generic one. That way everything (events) will be in the control of angular.

https://angular-ui.github.io/bootstrap/

edit: this maybe useful for understanding digest cycle triggering and callbacks outside angular : http://school.codecraftpro./courses/angularjs-1x-fundamentals/lectures/392422

edit2: also useful https://scotch.io/tutorials/how-to-correctly-use-bootstrapjs-and-angularjs-together

We know that the digest of bootstrap tabs switching is ahead of the width of your divs. So instead of watching for the width of your divs to change, let's instead watch when your tab changes. When the tab changes, let's wait 1 ms to make sure we are on the next digest cycle. Then get the width and update your box height.

I have no idea why this is the case, but here is the solution that will work.

Here is a plunker example

Javascript

var app = angular.module("app",['ngAnimate', 'ui.bootstrap']);
app.controller('mainController',['$scope',function($scope){
    $scope.tabs = [
        {'title': 'Tab 1'},
        {'title': 'Tab 2'}
    ];
}]);

app.directive('squareDiv', function ($timeout) {
    return {
        restrict: "A",
        link: function (scope, element, attr) {

            scope.active = function() {
                return scope.tabs.filter(function(tab){
                    return tab.active;
                })[0];
            };

            scope.$watch(scope.active, function (tabActive) {
                $timeout(function() {
                    element.css({ 
                        height: element.width()
                    });
                }, 1);

            });

        }
    }
});

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论