【问题标题】:Two-way data binding does not work with directives in Angular.js双向数据绑定不适用于 Angular.js 中的指令
【发布时间】:2013-05-21 16:48:57
【问题描述】:

我尝试实现一个指令,该指令必须使用角度符号 {{...}} 更新特定代码块。问题是更新的代码不再编译。

指令:

.directive('i18n', [function() {
'use strict';
return function(scope, element) {
        var bindLabel = '{{labels.' + element.text() + '}}',
        //create an empty object
        obj = {

        };
        obj[element.text()] = '';
        //extend the labels
        angular.extend(scope.labels, obj);
        element.text(bindLabel);
};
}])

简单的 HTML 代码:

<title i18n>title</title>

编译后的HTML代码:

<title i18n="">{{labels.title}}</title>

期望的输出:

 <title i18n="">This is my title :)</title>

{{labels.title}} 在控制器中实现。

感谢您的帮助!

【问题讨论】:

标签: javascript data-binding angularjs angularjs-directive


【解决方案1】:

要动态编译 DOM 元素,请使用 $compile 服务:

element.html(value);

// Compile the new DOM and link it to the current scope

$compile(element.contents())(scope);

在您的示例中,它看起来像这样:

.directive('i18n', [ '$compile', function($compile) {
'use strict';
return function(scope, element) {
        var bindLabel = '{{labels.' + element.text() + '}}',
        //create an empty object
        obj = {

        };
        obj[element.text()] = '';
        //extend the labels
        angular.extend(scope.labels, obj);

        // Fill element's body with the template

        element.html(bindLabel);

        // Compile the new element and link it with the same scope

        $compile(element.contents())(scope);
    };
}]);

您可以在这里找到更多信息:http://docs.angularjs.org/api/ng.$compile

【讨论】:

  • 谢谢!明天我去试试。
【解决方案2】:

请注意,AngularJS 模板只在应用程序引导期间编译一次,只要您不自己使用 $compile。

AngularJS 编译和链接阶段

要了解为什么您的代码不工作,您必须了解 AngularJS 的编译和链接阶段。一旦你加载你的应用程序,AngularJS 会编译 HTML 元素,该元素包含一个带有 $compile 服务的 ng-app 属性,例如。 g.

<html ng-app="MyApp"></html>

编译阶段

$compile 标识您的 HTML 模板中调用每个指令的编译函数的所有指令,该函数从您的角度根元素 ($rootElement) 向上通过 html dom 树。

链接阶段

每个编译函数都返回一个后链接和一个可选的预链接函数。一旦 AngularJS 在根元素下编译了整个 dom,它就会开始调用 pre-link 函数,就像它之前调用 compile 函数一样。一旦到达 dom 的叶子,指令的 post-link 函数就会被调用并返回到根元素。

插值

在 {{ 和 }} 之间具有表达式的字符串由 AngularJS 作为称为插值指令的特殊指令处理。就像任何其他指令一样,这些指令是在编译期间使用 $interpolate 服务创建的。 $interpolate 服务接收一个带有多个表达式的插值字符串并返回一个插值函数。 interpolate 指令的 post-link 函数在 interpolate 函数上创建一个监视,以便一旦插入字符串中的任何表达式发生更改,它们就可以更新 html 节点。

翻译模块

当我们现在查看您的代码时,您实际上是在将 html 元素的文本设置为 AngularJS 插值字符串,并在您的指令的链接后函数中的 {{ 和 }} 之间包含一个表达式。

正如上面所解释的,此时 AngularJS 已经编译了模板,因此它永远不会用你的表达式编译插入的字符串。

从您的代码中可以看出,您正在尝试实现某种翻译指令。此类指令在应考虑已翻译字符串中的插值字符串和其他 AngluarJS 模板代码时必须调用 $compile 函数:

directive('translate', ['$compile','translate', function factory($compile, translate) { 
    return {            
        priority: 10, // Should be evaluated before e. g. pluralize
        restrict: 'ECMA',
        link: function postLink(scope, el, attrs) {
            if (el.contents().length) {
                el.html(translate(el.text()));
                $compile(el.contents())(scope); // This is only necessary if your translations contain AngularJS templates
            }
        },
    };
}]).

翻译指令使用翻译服务来获取实际翻译。 translateProvider 有一个 add 方法,您可以使用它来添加翻译 e。 G。来自语言包:

.provider('translate', function() {
    var localizedStrings = {};
    var translateProvider = this;
    this.add = function(translations) {
        angular.extend(localizedStrings, translations);
    };
    this.$get = ['$log', '$rootScope', function ($log, $rootScope) {
        var translate = function translate(sourceString) {            
            if (!sourceString) {
                return '';
            }
            sourceString = sourceString.trim();
            if (localizedStrings[sourceString]) {
                return localizedStrings[sourceString];
            } else {
                $log.warn('Missing localization for "' + sourceString + '"');
                return sourceString;
            }
        };
        return translate;
    }];    
}).
config(function(translateProvider) {
    translateProvider.add({'My name is {{name}}': 'Mi nombre es {{name}}'}); // This might come from a bundle
}).

使用模块

您现在可以按如下方式使用该模块:

<div ng-app="myApp" ng-controller="MyCtrl">
    <span data-translate>My name is {{name}}</span>
</div>

我创建了一个带有完整示例的 jsFiddle:http://jsfiddle.net/jupiter/CE9V4/2/

【讨论】:

    【解决方案3】:

    【讨论】:

      【解决方案4】:

      我认为$compile 可能是您正在寻找的。试试:

      var bindLabel = $compile('{{labels.' + element.text() + '}}')(scope);
      

      【讨论】:

      • 你会想要 $compile 元素,而不是表达式。 $compile 需要 HTML 或元素。
      • AngularJS 文档说 $compile 需要一个字符串或一个 DOM 元素:“元素 - {string|DOMElement} - 元素或 HTML 字符串编译成模板函数。”
      猜你喜欢
      • 2017-04-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-07
      • 1970-01-01
      • 2018-01-03
      • 2013-06-15
      • 1970-01-01
      • 2014-11-03
      相关资源
      最近更新 更多