【问题标题】:Template always compiles with old scope value in directive模板始终使用指令中的旧范围值编译
【发布时间】:2014-12-16 18:16:16
【问题描述】:

我有一个像这样工作的指令:

http://jsfiddle.net/smithkl42/cwrgLd0L/23/

App.directive('prettify', ['$compile', function ($compile) {
    var templateFn;
    return {
        restrict: 'E',
        scope: {
            target: '='
        },
        link: function (scope, element, attrs) {
            if (!templateFn) {
                var template = element.html();
                templateFn = $compile(template);
            }
            scope.$watch('target', function (newVal, oldVal) {
                var compiled = templateFn(scope);
                element.html('');
                element.append(compiled);
                var html = element.html();
                var prettified = prettyPrintOne(html);
                element.html(prettified);
            }, true);
        }
    };
}]);

问题是当我编译模板时,它总是使用target 属性的old 值进行编译。所以它开始显示这一点,即,它表现得好像没有什么可以替代:

然后,如果我向属性添加一个字符,它会显示这个,即scope.organization.message 属性的 previous 值:

调试显示范围指令的target 属性中的值在编译时是正确的。

我做错了什么? $compile 返回的模板函数是否有一些查看旧范围值的内容?还是……?

(另请参阅这个问题,它导致了这个问题:Using $compile in a directive triggers AngularJS infinite digest error。)

【问题讨论】:

    标签: javascript angularjs angularjs-directive angularjs-scope


    【解决方案1】:

    首先,您的 templateFn 变量的范围是工厂级别,但它是在实例级别填充的。这意味着第一次使用指令时,它将使用该元素的指令进行填充,之后的每次使用也将使用相同的模板,即使它实际上有不同的模板。

    您看似延迟绑定问题的原因与摘要周期以及 Angular 如何管理对 DOM 的更改有关。在处理范围更改时,所有范围观察者都会在对 DOM 进行任何更改之前进行处理。这样,所有 DOM 更改都被合并到一个批次中(至少对于那个周期),因此您不会一次进行多次更新,从而可能导致多次回流。因此,当您调用 element.html() 时,您是在 DOM 尚未更新以反映作用域上更改的值时这样做的。

    在这种特殊情况下,您还需要做很多额外的工作 - 调用 templateFn 将为您提供包含所需内容的 jQuery(或 jQLite)对象 - 无需将其添加到 DOM,并且然后把它拿回来,你可以直接打电话给html()

    这些逻辑都可以像这样合并(并正常工作):

    setTimeout(function () {
        var compiled = templateFn(scope).html();
        var prettified = prettyPrintOne(compiled);
        element.html(prettified);
    }, 0);
    

    将所有内容包装在setTimeout 中会强制摘要周期完成后评估逻辑。

    但是,一般来说,该指令的实现有点尴尬:

    • 如果每次使用都需要 HTML 模板(例如 <pre><code> 标记),则应通过 templatetemplateUrl 属性将其包含在指令本身中,而不是期望消费者知道这是必需的
    • 您可能无需使用$compile 就可以实现这一点 - 您可以将prettyPrintOne 的输出放在作用域上,然后在templatetemplateUrl 属性中指定的模板中绑定到它,或者您可以使用 jQuery 获取对将成为容器的任何元素的引用(也就是说,如果它不是顶级元素)并使用 html() 设置其内容。
    • 如果您确实需要允许在指令中定义其他模板化 HTML 内容,请查看在 directive guide 中定义的 transclude 选项。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-04-28
    • 1970-01-01
    • 2023-04-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-04
    相关资源
    最近更新 更多