【问题标题】:angularJS: Wait for ng-if to finish, to make sure that the DOM is readyangularJS:等待 ng-if 完成,以确保 DOM 已准备好
【发布时间】:2013-11-14 12:16:51
【问题描述】:

我正在使用 ng-if 来显示和隐藏一个元素。当元素出现时,我想调用一个服务,它在新元素中滚动到某个子元素(按 Id)。问题是,如果我尝试在将元素设置为可见后立即调用我的服务函数,则 DOM 似乎还没有准备好。

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

myApp.factory("ScrollService", function () {
    return {
        scroll: function (id) {
            console.log(document.getElementById(id));
        }
    };
});

function MyCtrl($scope, ScrollService) {
    $scope.visible = false;

    $scope.toggleVisibility = function () {
        $scope.visible = !$scope.visible;
        if ($scope.visible) {
            ScrollService.scroll("myId"); //output: null
        }
    };
}

document.getElementById() 将始终导致 null

这也是一个小提琴,它演示了问题:http://jsfiddle.net/Dpuq2/

那么有什么方法可以在被 ng-if 操作后,在 DOM 准备好后立即触发函数?

编辑

使用 MinkoGechev 的小提琴,我能够在更真实的环境中重现我的错误,并使用指令而不是服务:FIDDLE

问题似乎出现了,因为我在ng-if-container 内部使用了ng-repeat

<div ng-controller="MyCtrl">
    <div ng-if="visible"> 
        <div id="myId" data-scroll="itemId">
            <div id="xy"></div>
            <div ng-repeat="item in items" id="{{ item.number }}">{{ item.number }}</div>
        </div>
    </div>
    <button ng-click="toggleVisibility()">toggle</button>
</div>

这里是相应的指令加上控制器:

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

myApp.directive("scroll", function () {
    return {
        scope: {
            scroll: '='
        },
        link: function (scope) {
            scope.$watch('scroll', function (v) {
                console.log(v, document.getElementById(scope.scroll));
            });
        },
        transclude: true,
        template: "<div ng-transclude></div>"
    };
});

function MyCtrl($scope) {
    $scope.visible = false;
    $scope.itemId = "";
    $scope.items = [];
    for (var i = 1; i < 10; i++) {
        $scope.items.push({
            number: i,
            text: "content " + i
        });
    }

    $scope.toggleVisibility = function () {
        $scope.visible = !$scope.visible;
        if ($scope.visible) {
            $scope.itemId = "3";
        }
    };
}

因此,一旦我切换容器的可见性,我就会设置要滚动到的元素的 Id:

$scope.itemId = "3"

如果我使用从 1 到 10 的数字之一(由 ng-repeat 创建的元素的 ID),它将失败。如果我使用“xy”(与 ng-repeat 元素相邻的一个元素的 ID),它会成功。

【问题讨论】:

  • 请不要在控制器中实现 DOM,而是使用指令
  • @MaximShoustin 我没有将它实现到控制器中,而是实现到服务中。我也尝试使用指令,结果相同。
  • @basilikum 在指令的link() 函数中,您确定链接到该指令的元素的DOM 已准备就绪。在这里,您必须将元素滚动到他的孩子。
  • @Blackhole 也是一个不错的建议,但它似乎还没有完全准备好,因为 ng-repeat 中的元素尚不存在(请参阅更新的问题)。

标签: javascript angularjs dom


【解决方案1】:

以下是如何使用指令实现您正在寻找的效果:

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

myApp.directive("scroll", function () {
    return {
        scope: {
            scroll: '='
        },
        link: function (scope) {
            scope.$watch('scroll', function (v) {
                //The value is true, so the element is visible
                console.log(v, document.getElementById('myId'));
            });
        }
    };
});

function MyCtrl($scope) {
    $scope.visible = false;

    $scope.toggleVisibility = function () {
        $scope.visible = !$scope.visible;
    };
}

这里是DEMO(打开控制台查看日志)。

注意:AngularJS 强制分离关注点,从而使代码更具可读性和可维护性。使用“Angular 方式”时应遵循的规则之一是将所有 DOM 操作 ONLY 放在指令中。

【讨论】:

  • 感谢您的回答,我看到您的小提琴奏效了。不幸的是,当我在我的应用程序中实现它时,它不再起作用了。也许还有其他事情发生。我稍后再回复。
  • 我刚刚编辑了我的问题。看来,该错误与使用 ng-repeat 有关。你知道我做错了什么吗?
【解决方案2】:

您找到解决问题的方法了吗?

由于您提到问题似乎与 ng-repeat 有关,您是否尝试过“scope.$last”?

我绝不是一位经验丰富的 Web 开发人员,但我遇到过类似的问题,即工具提示不会显示在 ng-repeat “生成”的项目上,并让它与使用“应用工具提示”的指令一起工作范围.$last"

举个例子:

AppName.directive('directiveName', function () {
    return function (scope, element, attrs) {
        if (scope.$last) {
            <-- Your code here -->
        }
    };
});

也许有更多经验的人可以提供更多意见。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-03-29
    • 2011-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-06
    • 1970-01-01
    相关资源
    最近更新 更多