Update: I put a bounty on this question. I am not looking for hacks or workarounds. I am looking for an official way to access the dom in an angular component, and an explanation why the behavior I see ($postLink running to early) seems to be contradictory to the official docs.
The official docs state (here):

更新:我对这个问题给予了赏金。我不是在寻找黑客或变通方法。我正在寻找一种正式的方式来访问角度组件中的dom,并解释为什么我看到的行为($ postLink运行到早期)似乎与官方文档相矛盾。官方文档声明(这里):

$postLink() - Called after this controller's element and its children have been linked. Similar to the post-link function this hook can be used to set up DOM event handlers and do direct DOM manipulation

$ postLink() - 在此控制器的元素及其子元素已被链接之后调用。与post-link函数类似,此挂钩可用于设置DOM事件处理程序并执行直接DOM操作

Original question: I have an example of the problem here -> http://plnkr.co/edit/rMm9FOwImFRziNG4o0sg?p=preview

原始问题:我在这里有一个问题的例子 - > http://plnkr.co/edit/rMm9FOwImFRziNG4o0sg?p=preview

I am using an angular component and I want to modify the dom in the post link function, but it doesn't work, it seems that the function runs too early, before the template is actually ready in the dom after all the angular processing.

我正在使用一个角度组件,我想修改后期链接功能中的dom,但它不起作用,似乎该功能运行得太早,在模板实际准备好之后在所有角度处理后的dom中。

In the html page, I have this:

在html页面中,我有这个:

<my-grid grid-id="'foo'"></my-grid>

The component is defined as:

该组件定义为:

appModule.component('myGrid',{
    controller: gridController,
    bindings: {
        "gridId": "<",
    },
    templateUrl: 'gridTemplate'
});

In the component template I have this:

在组件模板中我有这个:

<table id='{{$ctrl.gridId}}'>
...

(The binding itself works, there is no doubt. Eventually, in the html the id of the table is 'foo' as expected).

(绑定本身有效,毫无疑问。最终,在html中,表的id是'foo',如预期的那样)。

In the controller, I have something like this:

在控制器中,我有这样的事情:

function gridController($scope, $compile, $attrs) {
    console.log ("grid id is: " + this.gridId); // 'foo'

    this.$postLink = function() {
        var elem = document.getElementById(this.gridId);
        // do something with elem, but elem is null
    }
}

What I see when debugging is that when the $postLink function is executed, the table is in the dom but its id attribute is still {{$ctrl.gridId}} instead of foo, so document.getElementById() finds nothing. This seems in contrast to the documentation.
What am I missing? Is there a different way to access the dom in the component?

我在调试时看到的是,当执行$ postLink函数时,该表位于dom中,但其id属性仍为{{$ ctrl.gridId}}而不是foo,因此document.getElementById()找不到任何内容。这似乎与文档形成鲜明对比。我错过了什么?是否有不同的方法来访问组件中的dom?

Update 2: Today I realized the same problem occurs with the regular link function of directives, it is not limited to components. So apparently I misunderstood the meaning of "do direct DOM manipulation" - the link function runs on an element that is detached from the dom, so using the document object with selectors is useless.

更新2:今天我意识到指令的常规链接功能也出现同样的问题,它不仅限于组件。显然我误解了“直接操作DOM”的含义 - 链接函数在与dom分离的元素上运行,因此使用带有选择器的文档对象是没用的。

3 个解决方案

#1


18

The documentation regarding $postLink() is correct. It's called after its controller's element and its children have been linked. This doesn't mean that you'll see a directive's result immediately. Maybe it's calling $http and inserting the result once it arrives. Maybe it's registering a watcher which in turns sets the result, as most of Angular's built-in directives do.

关于$ postLink()的文档是正确的。在它的控制器元素和它的子元素被链接之后被调用。这并不意味着您将立即看到指令的结果。也许它正在调用$ http并在结果到达时插入结果。也许它正在注册一个观察者,它反过来设置结果,就像Angular的大多数内置指令那样。

The underlying issue in your case is that you want to perform DOM manipulations after the interpolations have been compiled, or better yet, after their registered watcher had had time to run once.

您的情况下的基本问题是您希望在编译插值后执行DOM操作,或者更好的是,在注册的观察者有时间运行一次之后。

Unfortunately, there isn't an official way to do this. Nevertheless, there are ways of accomplishing this, but you won't find them listed in the documentation.

不幸的是,没有官方的方法来做到这一点。尽管如此,有一些方法可以实现这一点,但您不会在文档中找到它们。

Two popular ways of running a function after the interpolations have been compiled are:

编译插值后运行函数的两种常用方法是:

  • using $timeout without a delay (as it defaults to 0): $timeout(function() { /* Your code goes here */ });

    使用$ timeout没有延迟(因为它默认为0):$ timeout(function(){/ *你的代码在这里* /});

  • using .ready() which is provided by Angular's jqLite

    使用由Angular的jqLit​​e提供的.ready()

In your case, you're much better off using a watcher to run a function once the element with the given ID exists:

在您的情况下,一旦具有给定ID的元素存在,您最好使用观察程序来运行函数:

var deregistrationFn = $scope.$watch(() => {
    return document.getElementById(this.gridId);
}, (newValue) => {
    if (newValue !== null) {
        deregistrationFn();

        // Your code goes here
    }
});

Finally, in my opinion, I believe that whenever you need to wait for the interpolations to be compiled, or for certain directives to insert their value, you're not following the Angular's way of building things. In your case, why not create a new component, myGridTable which requires myGrid as a parent, and add its appropriate logic there. This way, each component's responsibility is much better defined and it's easier to test things.

最后,在我看来,我相信无论何时你需要等待编译插值,或者某些指令插入它们的值,你都不会遵循Angular的构建方式。在你的情况下,为什么不创建一个新组件myGridTable,它需要myGrid作为父组件,并在那里添加适当的逻辑。这样,每个组件的责任都得到了更好的定义,并且更容易进行测试。

更多相关文章

  1. Javascript XML DOM将属性设置为特定元素
  2. 获取JavaScript数组元素的最大长度
  3. 获取html DOM元素内容的边框
  4. 是否有办法阻止contenteditable的插入符号出现在IE10的元素上?
  5. 获取拆分字符串数组的最后一个元素
  6. CSS3(jQUery?)当它悬停时隐藏元素“a”,这样就可以显示元素“b”
  7. 如何在没有pdf组件的移动浏览器中显示Base64编码的pdf
  8. 停止鼠标用javascript双击某些元素
  9. 在量角器中检索子元素的数组

随机推荐

  1. javascript中this的指向问题
  2. 两个iframe之间实现锚点功能
  3. [转]在网页中加入声音文件,并且用JavaScri
  4. 使用HTML文件中的React调用.js文件中的Ja
  5. jQuery在.each循环中获取输入值
  6. Javascript字符串替换为动态创建的模式。
  7. Qunit:如何在不知道acceptCallCount的情况
  8. 如何从控制器内的javascript函数调用角范
  9. 使用单个函数隐藏多个输入
  10. 在laravel中的Http Post使用fetch api给