窗口调整大小指令

38

我试图使一个div在窗口大小调整时自动调整大小,搜索后发现使用指令是最好的解决方案。

模板:

<div elHeightResize ng-view ng-style="{ height: windowHeight }"></div>

指令:

myApp.directive('elheightresize', ['$window', function($window) {
    return {
        link: function(scope, elem, attrs) {
            scope.onResize = function() {
                var header = document.getElementsByTagName('header')[0];
                elem.windowHeight = $window.innerHeight - header.clientHeight;
            }
            scope.onResize();

            angular.element($window).bind('resize', function() {
                scope.onResize();
            })
        }
    }
}])

虽然我可以在scope.onResize函数中记录elem.windowHeight,但似乎无法将其应用于ngStyle

我还是忽略了什么吗?

编辑:

<div ng-view resize style="height: {{ windowHeight }}px">

这个解决方案似乎有效,但我还是想知道为什么使用ngStyle不起作用。


你的确切问题的答案(为什么 elem.windowHeight 不适用于 ngStyle)是,你必须手动应用它,通过触发一个 digest 循环,因为 .bind() 方法不被 Angular 拦截。所以在事件处理程序结束后(在 scope.onResize(); }) 之后),你应该添加 scope.$apply(); - f.ardelian
6个回答

47
我认为你忘记在scope.onResize方法的结尾调用scope.$apply();来触发脏检查循环。无论如何,我使用了以下指令(从这里获取),对我很有效:试着打开调试视图并改变视图高度:演示 Fiddle
app.directive('resize', function ($window) {
    return function (scope, element, attr) {

        var w = angular.element($window);
        scope.$watch(function () {
            return {
                'h': w.height(), 
                'w': w.width()
            };
        }, function (newValue, oldValue) {
            scope.windowHeight = newValue.h;
            scope.windowWidth = newValue.w;

            scope.resizeWithOffset = function (offsetH) {

                scope.$eval(attr.notifier);

                return { 
                    'height': (newValue.h - offsetH) + 'px'
                    //,'width': (newValue.w - 100) + 'px' 
                };
            };

        }, true);

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

并用法:

 <div  ng-style="resizeWithOffset(165)" 
       resize 
        notifier="notifyServiceOnChage(params)"
   >
    /** ... */
 </div>

虚拟控制器方法的使用:

$scope.notifyServiceOnChage = function(){
      console.log($scope.windowHeight);   
 };

[编辑]

这里是一个没有使用jQuery库,而是使用innerHeight的演示。

演示3:Fiddle


这很奇怪,当我复制/粘贴你的代码时,对我来说似乎什么都没有发生。然后我尝试将我的代码分离出来,它就可以工作了:http://jsfiddle.net/zbjLh/272/看起来我的应用程序中可能存在一些冲突,尽管那里没有发生任何特殊的事情。 - woutr_be
我对我的代码为什么不起作用很感兴趣,我可以复制/粘贴你的代码,但这并不能解决我的初始问题。 - woutr_be
1
对于其他人来说,我认为调用.height().width()假定w(或angular.element($window)的结果)是一个JQuery对象。换句话说,这需要页面上有jQuery。如果只使用jqLite似乎无法工作。 - mozz100
@mozz100 感谢您的更新,我已经添加了没有 jQuery 的演示。 - Maxim Shoustin
$watch在这里不能按预期工作,它总是返回一个新对象,因此脏检查认为它已经改变并且窗口大小已调整(我已经测试过了)。如果您使用相同的对象并只更改其xy字段,则仍然无法正常工作,因为Angular脏检查不是深度的,它不会注意到对象字段的更改。最好使用(w.width() * 10000) + w.height()(或者对于未来的监视器使用100000 :-)),然后每个宽度/高度组合都会接收到唯一的整数哈希值。 - Yuriy Dyachkov
显示剩余3条评论

7

由于我们使用指令,因此我们始终可以通过更改指令内部元素的高度来进行一些DOM操作。

例如:

var app=angular.module('App', []);
app.directive('elheightresize', ['$window', function($window) {
    return {
        link: function(scope, elem, attrs) {
            scope.onResize = function() {
                var header = document.getElementsByTagName('header')[0];
                elem.windowHeight = $window.innerHeight - header.clientHeight;
                $(elem).height(elem.windowHeight);
            }
            scope.onResize();

            angular.element($window).bind('resize', function() {
                scope.onResize();
            })
        }
    }
}])

实时示例:http://jsfiddle.net/choroshin/FJvyb/

这是一个关于IT技术的示例链接,你可以点击进入查看。

虽然这个方法可行,但我仍然对我的代码为什么不起作用感兴趣。 - woutr_be
3
可能已经晚了回答这个问题,但以防万一有人看到这里,上面的示例存在一个错误:调整大小时,移动并不总是导致 <div> 元素填充整个区域,并且周期性地 <header> 元素仅为文本的高度。需要在 scope.onResize(); 调用之后添加 scope.apply(); 来确保更改正确传播。 - Lew

5

你之前提出的问题已经有一段时间了,看起来你已经找到了解决方法...但同时你也想知道为什么 ng-style 没有起作用:

从 Angular 文档中可以看到 ng-style 的定义:

一个表达式,该表达式求值为一个对象,其键是 CSS 样式名称,值是这些 CSS 键对应的值。

所以在你的 ng-style 示例中,你提供了 height : 375; (如果 windowHeight 的值为 375),但这不是一个正确的值。

就像你在 workaround 中所做的那样,它应该具有单位。

windowHeight = ($window.innerHeight - header.clientHeight) + "px";


3
在链接函数中给出
scope.onResize = function() {
            var header = document.getElementsByTagName('header')[0];
            elem.windowHeight = $window.innerHeight - header.clientHeight;
        }


$window.onresize = function(){scope.onResize();scope.$apply();}

你需要在某个时候删除调整大小事件处理程序吗? - waxingsatirical

3

以下是使用CoffeeScript编写的极简代码:

app.directive "applyOnResize", ($window) ->
  ($scope, $element) ->
    $element($window).bind("resize", $scope.$apply)

同样的 Javascript 代码


0

这里有另一个版本,不使用angular $watch,只想监视窗口大小的变化:

function resize () {

        var windowHeight = window.innerHeight,
            windowWidth  = window.innerWidth;

        scope.windowHeight = windowHeight;
        scope.windowWidth  = windowWidth;

        console.log('w.innerHeight', windowHeight);
        console.log('w.innerWidth', windowWidth);

        //** If want to apply style on element, can do something like:
        var elemStyle = {
            width: windowWidth + 'px',
            height: windowHeight + 'px'
        };

        element.css(elemStyle);
    }       
    resize();

    //** On resize
    window.onresize = function () {
        resize();
        scope.$apply();
    }

    //** Destroy
    scope.$on('$destory', function () {
        window.off('resize')
    });  

小提琴


这是不好的做法...全局onresize处理程序等。请使用其他答案中的代码。 - reflog
我只是提供了一个不使用$watch的替代方案来实现结果,这就是为什么我写/扩展了代码示例。我相信我已经解释得很清楚了。顺便说一下,我认为全局onrpsize处理程序没有问题,这只取决于你如何使用它。想象一下同时有多个对话框的情况。你可能担心全局处理程序会始终监听事件变化。但是Angular指令可以随时销毁/移除。指令监听的作用域不会是一个“全局”的作用域/模块。 - Neaped

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接