为啥这个宽度监视 AngularJS 指令在切换 Bootstrap 选项卡时不起作用?

Posted

技术标签:

【中文标题】为啥这个宽度监视 AngularJS 指令在切换 Bootstrap 选项卡时不起作用?【英文标题】:Why does this width watching AngularJS directive not work upon switching Bootstrap tabs?为什么这个宽度监视 AngularJS 指令在切换 Bootstrap 选项卡时不起作用? 【发布时间】:2016-01-29 14:26:58 【问题描述】:

我在一个页面上有两个 Bootstrap 选项卡,每个选项卡都显示一个列表。我希望每个列表项都是一个正方形,并编写了这个 AngularJS 指令来将每个列表项的高度设置为等于其 Bootstrap 控制的动态宽度:

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

当我加载这个页面时,第一个选项卡的列表项显示为正方形,这意味着指令正在工作。但是当我单击另一个选项卡时,那里的列表项的默认高度为 100 像素。当我切换回第一个选项卡时,即使是那里的列表项现在也有默认高度。

这里出了什么问题?

更新 1:

这里是一个说明这个问题的 plunker 的链接:http://plnkr.co/edit/Pc7keuRXR9R3U6AhZmFE?p=preview

更新 2:

将 Bootstrap 替换为 UI-Bootstrap 的 Plunker 仍然存在同样的问题:http://plnkr.co/edit/rLE1wUAHKzJP6FVnGF6b?p=preview

更新 3:

Plunker with watch for tab switch 而不是宽度变化(解决原来的宽度 watch 不能正常工作的问题;将需要使用单独的指令来适应由于窗口大小调整导致的宽度变化):http://plnkr.co/edit/Y4Goe0cexq979JsIcBxl?p=preview

我仍然想了解为什么宽度表在标签切换后停止正常工作。

【问题讨论】:

可能是因为标签页被隐藏了,所以它的内容还没有尺寸。无论如何,如果你做了一个 plunker 之类的,它会有所帮助。 没错,一个笨蛋可以帮上大忙! @Price - 我对角度一无所知,但你不能只使用引导程序shown.bs.tab 事件来设置列表项内.holder 元素的height 吗?这是一个例子Plunker。就像我说的那样,我不知道这是否是使用 angular 执行此操作的正确方法,但它确实有效。 @Price - 当然,没问题。最好等待,看看是否有更好的角度背景的人可以告诉你这里发生了什么。一般来说,每当我想操作引导程序组件中的内容时,我总是依赖引导程序事件,但由于我没有角度技能,可能有一个完全不同的解决方案来解决你的问题,我不知道。 ;-) 另见github.com/angular/angular.js/issues/…。 【参考方案1】:

您的指令的观察者是正在发生的事情的一个摘要周期。如果您仔细观察,您会发现第一个选项卡单击并没有多大作用(没有记录到控制台)。您看到的是活动选项卡的元素被调整为隐藏选项卡元素的高度。所以隐藏标签的高度是正确的。我强烈建议使用引导程序的角度版本而不是通用版本。这样一来,一切(事件)都将在 angular 的控制之下。

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

编辑:这可能有助于理解 Angular 之外的摘要循环触发和回调:http://school.codecraftpro.com/courses/angularjs-1x-fundamentals/lectures/392422

edit2:也有用https://scotch.io/tutorials/how-to-correctly-use-bootstrapjs-and-angularjs-together

【讨论】:

谢谢!我认为 Angular 与标准的 Bootstrap 兼容。您如何确定观察者落后了一个摘要周期? 在页面加载时,您将看到 4-4 个控制台日志。单击选项卡 2 后,什么都没有。然后它又回来了。所以这基本上是因为 bootstrap js click 没有在 angular 中注册。将它包装在指令中可以解决这个问题,但由于 Angular 用户已经这样做了,你可以抽出一些时间:) 更正:未在该摘要周期内注册,但在一个周期后 感谢所有帮助!我创建了一个基于 ui-bootstrap 的新 plunker,但该指令似乎仍然落后于一个摘要周期。你能检查一下吗:plnkr.co/edit/rLE1wUAHKzJP6FVnGF6b?p=preview? ui.bootstrap.tabs 具有在选项卡选择上触发自定义表达式的设置。请检查此plnkr.co/edit/s5JepBEPMkvGW4Qbm4PH?p=preview 尽管这是一种不同的方法,但我的回答对于原始问题仍然有效。【参考方案2】:

我们知道引导选项卡切换的摘要超过了 div 的宽度。因此,与其关注 div 的宽度发生变化,不如关注标签何时发生变化。当标签更改时,让我们等待1 ms以确保我们在下一个摘要周期。然后获取宽度并更新您的盒子高度。

我不知道为什么会这样,但这是可行的解决方案。

这是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);

            );

        
    
);

【讨论】:

谢谢!根据另一个答案的 cmets,我们需要删除对 ngAnimate 的引用,以防止多次切换选项卡而弄乱此解决方案中的高度。 实际上,如果您还在 $timeout 中包含 requestAnimationFrame,这将防止布局抖动,并且您可以保持 nganimate。见:wilsonpage.co.uk/preventing-layout-thrashing 非常有趣!我刚刚更新了 plunker - plnkr.co/edit/UyxNI9gsOyBg6sNdcvYs?p=preview - 以使用 requestAnimationFrame ,它似乎正在工作。另一个大问题似乎是@ExpertSystem 提出的 jQuery 正在将 100% 的宽度转换为 100px,这也不好。

以上是关于为啥这个宽度监视 AngularJS 指令在切换 Bootstrap 选项卡时不起作用?的主要内容,如果未能解决你的问题,请参考以下文章

如何在我的angularjs指令中切换属性的值,如contenteditable?

为啥我应该在 AngularJS 中使用隔离范围指令?

指令容器的高度为 0px,宽度为 0px - angularjs

为啥 AngularJS 文档不在模型指令中使用点?

为啥 AngularJS 指令中不推荐使用 `replace` 属性? [复制]

为啥在使用 d3 创建 AngularJS 指令时使用 element[0] 而不是 element?