绑定数据后,从指令操作DOM

时间:2014-03-20 14:32:42

标签: javascript angularjs angularjs-directive

我的目标是创建一个指令,将一组不同的高度div排列成一个模式,消除它们之间的空白。就像Pinterest将divs镶嵌成马赛克一样:

Pinterest screenshot

我的方法是:

  1. 创建一个指令,将一组div包装为tessellate。

    <tessellate columns="6">
        <div ng-repeat="item in items" class="thumbnail">
            {{item.name}}
        </div>
    </tessellate>
    
  2. 使用指令模板首先设置列。 (tessellateTemplate.html)

    <div class="container">
        <div class="row">
            <div class="col-sm-{{12/columns}}" ng-repeat="i in numberToArray(columns) track by $index"></div>
        </div>
    </div>
    
  3. 使用该指令在绑定数据后抓取div ,并逐个将它们添加到最短高度的列中。

    app.directive('tessellate', function () {
        return {
            restrict: 'E',
            scope: {
                columns: '='
            },
            controller: ['$scope', function ($scope) {
                // Ensure number of columns is divisible by 12.
                if (isNaN($scope.columns) || 12 % $scope.columns != 0) {
                    $scope.columns = 6;
                }
    
                $scope.numberToArray = function (num) {
                    return new Array(num);
                };
            }],
    
            // Get child divs and add to template here.
            // Should I use compile or link??
    
            templateUrl: "/app/directives/templates/tessellateTemplate.html"
        };
    });
    
  4. 看起来很简单吧?我一直遇到的问题是,在数据绑定到它们之后,我无法弄清楚如何获取div 。这很重要,因为需要div的实际高度来决定它应该进入哪一列。

    绑定数据后,有没有办法在指令中操作DOM?

1 个答案:

答案 0 :(得分:0)

要回答我自己的问题,我能解决这个问题的唯一方法是使用$ timeout。我不是它的粉丝,但它有效。如果有人有更好的方法,请发一个答案。 :)

我修改了我的指令模板(tessellateTemplate.html)来转换div:

<div>
    <div class="container">
        <div class="row">
            <div class="col-sm-{{12/columns}}" ng-repeat="i in numberToArray(columns) track by $index"></div>
        </div>
    </div>
    <div ng-transclude></div>
</div>

然后我修改了我的指令以获得transclude: true并且还在$ timeout中进行细分(请不要嘲笑我的链式方法调用,已经有一段时间了,因为我必须使用JQuery:P ):

app.directive('tessellate', function () {
    return {
        restrict: 'E',
        replace: true,
        transclude: true,
        scope: {
            columns: '='
        },
        controller: ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) {
            // Ensure number of columns is divisible by 12.
            if (isNaN($scope.columns) || 12 % $scope.columns != 0) {
                $scope.columns = 6;
            }

            $scope.numberToArray = function (num) {
                return new Array(num);
            };

            $scope.getShortestColumn = function () {
                var cols = $element.children().first().children().first().children();
                var shortCol = null;
                angular.forEach(cols, function (col) {
                    col = angular.element(col);
                    if (shortCol == null || col.height() < shortCol.height()) {
                        shortCol = col;
                    }
                });
                return shortCol;
            };

            $timeout(function () {
                var divs = $element.children().first().next().children();
                for (var i = 0; i < divs.length; i++) {
                    $scope.getShortestColumn().append(divs[i]);
                }
            }, 0);
        }],
        templateUrl: "/app/directives/templates/tessellateTemplate.html"
    };
});

用法保持不变:

<tessellate columns="6">
    <div ng-repeat="item in items">
        {{item.name}}
    </div>
</tessellate>