AngularJS指令中link()函数中的DOM元素未准备就绪

6
我正在创建一个AngularJS指令,该指令应该包含一个基于C3.js的图表。问题是C3库无法看到它应该依附的DOM元素。该指令的link函数大致如下:
link: function(scope, element, attrs) {
    scope.someid = 'id';
    scope.chart = c3.generate({
        bindto: "#somechart"+scope.someid,
        data: {
            columns: [
                ['data1', 30, 200, 100, 400, 150, 250],
                ['data2', 50, 20, 10, 40, 15, 25]
            ]
        }
    });         
    console.log($("#somechart"+scope.someid).size()); // this is a test, outputs 0
}

该指令的模板包含以下内容:
<div id="#somechart{{ scope.someid }}">...</div>

问题在于 c3.generate()bindto 无法看到 #somechartid。 我放置了一个 console.log(),输出为0,这意味着在调用 link 函数时该元素目前不在 DOM 中。
如果我从浏览器的控制台甚至从一些 ng-click 调用相同的图表生成代码,则会呈现图表。
是否有一种方法可以在不使用类似于 $timeout 的解决方案的情况下克服此问题?
更新时间是 2014-07-15 15:33,已更改代码示例并添加了指令模板中的相关行。

1
请问能否更具体地说明为什么链接函数无法看到 #somechartid?它是在模板上加载导致延迟,还是在页面加载后动态生成的 #somechartid? - DineshKP
该指令的模板由templateUrl提供。涉及到的DOM元素出现在模板中,它的id属性被指定为id="#somechart{{ data.id }} - Chief Crocodile Wrestler
你的id属性中是否实际包含一个#符号?id="#somechart{{data.id}}"。我注意到另一件事情,那就是你的id属性是由angular动态设置的,但你正在引用一个“#somechartid”,这个元素可能在你的页面上并不存在。 - DineshKP
页面上的id不包含#。那是个笔误,抱歉,已经修复了。我更新了代码并包括了模板中相关的行。问题在于我无法在链接函数中初始化C3图表,因为图表的div尚未出现在DOM中。如果我稍后尝试通过某些ng-click或其他方式来完成它,它就可以工作。 - Chief Crocodile Wrestler
可能是[DOM在指令的链接函数中还没有准备好。hacky Timeout是唯一的解决方案吗?]的重复问题(https://dev59.com/I4fca4cB1Zd3GeqPmbaR)。 - Johann
3个回答

1

如果您想操作尚未生成的DOM,请在链接函数中使用$timeout函数。请参见this


我提到过我不想使用$timeout,因为这似乎是一个hackish的解决方案。 - Chief Crocodile Wrestler
抱歉,我错过了。所以我也对解决方案感兴趣。 - Jan Barta
2
结果证明,$timeout$evalAsync终究是解决这个问题的方案。@Teq1提出了一个问题,引导我找到了这个这个。那里的讨论解释了问题、解决方案以及使用$evalAsync$timeout的具体细节。 - Chief Crocodile Wrestler
AngularJS的链接功能文档:请注意,包含templateUrl指令的子元素尚未被编译和链接,因为它们正在等待异步加载其模板,并且它们自己的编译和链接已被暂停,直到发生那种情况。他们说链接用于“后编译”,但超时是必要的……他们在使用最终在常见场景中无用的示例方面并不友好。 - user3338098
@Ventura 在外部模板被加载、解析并注入到 DOM 后,element 才会存在。链接函数在模板“加载”之后但在 templateUrl 加载之前执行。这是我对该过程的最佳不完整理解。 - user3338098

0

你尝试过类似这样的东西吗?

link: function(scope, element, attrs) {
    scope.chart = c3.generate({
        bindto: element[0],
        data: {
            columns: [
                ['data1', 30, 200, 100, 400, 150, 250],
                ['data2', 50, 20, 10, 40, 15, 25]
            ]
        }
    });         
}

这是一种非常类似于Teq1建议的方法,但它并不起作用。我对“本地”Angular解决方案感兴趣,可以在DOM准备就绪时触发它。 - Chief Crocodile Wrestler

0

也许使用 element.find('#id') 可以帮助:

link: function(scope, element, attrs) {
    var item = element.find('#somechartid');
    scope.chart = c3.generate({
        bindto: item,
        data: {
            columns: [
                ['data1', 30, 200, 100, 400, 150, 250],
                ['data2', 50, 20, 10, 40, 15, 25]
            ]
        }
    });         
}

尝试了一下,没用。我查看了C3的源代码,他们使用了D3的select()函数,该函数又使用了querySelector()函数。无论如何,我更感兴趣的是,是否有Angular中的本机解决方案可以让我在元素在DOM后触发事件。 - Chief Crocodile Wrestler
请检查此问题的解决方案:https://dev59.com/W2ct5IYBdhLWcg3wPLGB 可能经典的$(window).load(function()会起作用。 - Teq1
1
好的,我已经查看了您提供的链接。$evalAsync解决了这个问题!在此处讨论的问题和答案(https://dev59.com/gWQm5IYBdhLWcg3w2R1J)以及(https://dev59.com/8GQm5IYBdhLWcg3w8Sne#17239084)解释了问题,解决方案以及使用$evalAsync和$timeout的具体细节。 - Chief Crocodile Wrestler

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