简体   繁体   English

angularJS:等待在指令加载之前评估模板

[英]angularJS: wait for template to be evaluated before directive loads

The Situation 情况

Lets say I have a directive, that has to access certain elements via ID, inside the element on which the directive is defined. 假设我有一个指令,它必须通过ID在定义指令的元素内访问某些元素。 The problem, that can occur, is that by the time the directive is evaluated, the child-elements are not yet. 可能出现的问题是,在评估指令时,子元素还没有。 The result is, that I'm not able to access those elements by their ID. 结果是,我无法通过其ID访问这些元素。

Example

FIDDLE 小提琴

<div ng-controller="MyCtrl">
  <div color="elementId">
      <div ng-repeat="item in items" id="{{ item.id }}">
          {{ item.name }}
      </div>
  </div>
</div>

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

    myApp.directive("color", function () {
        return {
            restrict: "A",   
            link: function (scope, element, attributes) {

                var name = attributes.color,
                    el = element[0];

                scope.$watch(name, function () {
                    var id = scope[name];
                    console.log(id); //id1
                    console.log(element.children().eq(0).attr("id")); //{{ item.id }}
                    element.find("#"+id).css("background-color","red");
                });
            }        
        };
    });

    function MyCtrl($scope) {
        $scope.items = [
            { id:"id1", name:"item1" },
            { id:"id2", name:"item2" }
        ];

        $scope.elementId="id1";
    }

</script>

So my directive should just paint the background-color of the element with the id in $scope.elementId . 因此,我的指令应该只使用$scope.elementId的id $scope.elementId元素的背景颜色。 (Btw. I know I can handle this simple example much easier, it should just illustrate the general issue). (顺便说一句。我知道我可以更轻松地处理这个简单的例子,它应该只是说明一般问题)。 The problem is, that the ids of the elements inside ng-repeat are not there yet. 问题是,ng-repeat中的元素的id还没有。 As pointed out in the comment in the code, the id is still "{{ item.id }}". 正如代码中的注释所指出的那样,id仍然是“{{item.id}}”。 So angular didn't evaluate this part yet. 因此,角度尚未评估此部分。

Question

My obvious question is now: how can I make my directive to wait for descendent elements to be completely evaluated? 我现在明显的问题是:如何让我的指令等待后代元素被完全评估?

Further Explaination 进一步解释

In my real application I want to have a directive, that enables me to scroll to a certain elements on the page. 在我的实际应用程序中,我希望有一个指令,使我能够滚动到页面上的某些元素。 I also use a pagination directive to split up the elements I want to show. 我还使用分页指令来分割我想要显示的元素。 Because of the pagination, only the elements that are really visible, are in the DOM, so the invisible elements are already filtered out in my controller. 由于分页,只有真正可见的元素在DOM中,因此隐形元素已经在我的控制器中被过滤掉了。

I also have a sidebar, where are small links to ALL the elements (not only the visible ones). 我还有一个侧边栏,其中有所有元素的小链接(不仅是可见元素)。 When someone clicks on an element in the sidebar, two events should occur: 当有人点击侧边栏中的元素时,应该发生两个事件:

  1. jump to the correct page 跳转到正确的页面
  2. scroll to the corrent element 滚动到corrent元素

When I jump to the page, I basically have the situation, I described above. 当我跳到页面时,我基本上都有这种情况,我在上面描述过。 I have a complete new list of elements, that have to be processed by ng-repeat. 我有一个完整的新元素列表,必须由ng-repeat处理。 But directly after that, I try to tell my scroll-directive, that it should scroll the element with the ID "xy", but this ID is not assigned yet. 但是在那之后,我尝试告诉我的scroll-directive,它应该使用ID“xy”滚动元素,但是这个ID还没有分配。

Wrap your $scope.elementId = "Id1" with $timeout to notify angular to call listeners. 用$ timeout包装$ scope.elementId =“Id1”以通知angular调用侦听器。 (this can alternatively be done with $scope.$apply(), but it's causing another issue here) (也可以使用$ scope。$ apply()来完成,但这会导致另一个问题)

here is the jsfiddle link 这是jsfiddle链接

Code is - 代码是 -

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

    myApp.directive("color", ['$timeout',  function ($timeout) {
        return {
            restrict: "A",   
            link: function (scope, element, attributes) {
                console.log(element)
                var name = attributes.color,
                    el = element[0];

                 scope.$watch(name, function () {
                     var id = scope[name];
                     console.log(id); //id1
                     console.log(element.find("#"+id)); //{{ item.id }}
                     element.find("#"+id).css("background-color","red");
                 });
            }        
        };
    }]);

myApp.controller("MyCtrl", function($scope, $timeout) {
    $scope.items = [
        { id:"id1", name:"item1" },
        { id:"id2", name:"item2" }
    ];

    $timeout(function() {
        $scope.elementId="id1";
    });
});

If finally ended up writing a getElementById helper function, that returns a promise and has an internal interval, that check every 100ms if the element is present or not: 如果最终编写了一个getElementById辅助函数,它返回一个promise并有一个内部间隔,如果该元素存在与否则每100ms检查一次:

updated Fiddle 更新了小提琴

function getElementById(elementId) {
    var deferred = $q.defer(),
        intervalKey,
        counter = 0, 
        maxIterations = 50;

    intervalKey = setInterval(function () {
        var element = document.getElementById(elementId);
        if (element) {
            deferred.resolve(element);
            clearInterval(intervalKey);
        } else if (counter >= maxIterations) {
            deferred.reject("no element found");
            clearInterval(intervalKey);
        }
        counter++;
    }, 100);

    return deferred.promise;
}

In my given example, I would use it like this: 在我给出的例子中,我会像这样使用它:

getElementById(id).then(function (element) {
    $(element).css("background-color","red");
}, function (message) {
    console.log(message);
});

It's still not my preferred solution, but it works and solves my problem for now. 它仍然不是我的首选解决方案,但它现在可以解决我的问题。 But I'm still curious, if there is any better approach to this. 但我仍然很好奇,如果有更好的方法。

As per Jim Hoskins article, the following snippet should help you. 根据Jim Hoskins的文章,以下代码段可以帮助您。

  scope.$watch(name, function () {
    setTimeout(function () {
      scope.$apply(function () {
        var id = scope[name];
        console.log(id); //id1
        console.log(element.find("#"+id)); //{{ item.id }}
        element.find("#"+id).css("background-color","red");
      }  
    }, 200))
  });

Posting this answer to help people save some time(of course it's helpful to read the complete article) 发布此答案可帮助人们节省一些时间(当然,阅读完整文章会很有帮助)

You should complete the directive including a controller option. 您应该完成指令,包括controller选项。

controller: function ($scope){
     $scope.items = [
        { id:"id1", name:"item1" },
        { id:"id2", name:"item2" }
    ];
}

This will create everything in the controller scope, and then you can access it from the controller of the view that uses this directive. 这将在控制器范围内创建所有内容,然后您可以从使用此指令的视图的控制器访问它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM