简体   繁体   English

双向数据绑定不适用于Angular.js中的指令

[英]Two-way data binding does not work with directives in Angular.js

I try to implement a directive, which has to update specific code block with angular-notation {{...}}. 我尝试实现一个指令,它必须使用angular-notation {{...}}更新特定的代码块。 The problem is that the updated code is not compiled anymore. 问题是更新的代码不再编译。

The directive: 指令:

.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);
};
}])

Simple HTML code: 简单的HTML代码:

<title i18n>title</title>

HTML code after compilation: 编译后的HTML代码:

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

Desired output: 期望的输出:

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

The {{labels.title}} is implemented in controller. {{labels.title}}在控制器中实现。

Thank you for your help! 谢谢您的帮助!

To dynamically compile DOM elements use the $compile service: 要动态编译DOM元素,请使用$compile服务:

element.html(value);

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

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

In the case of your example it would look like this: 在您的示例的情况下,它将如下所示:

.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);
    };
}]);

You can find more information here: http://docs.angularjs.org/api/ng.$compile 您可以在此处找到更多信息: http//docs.angularjs.org/api/ng.$compile

Please not that AngularJS templates are only compiled once during application bootstrap as long as you don't use $compile on your own. 请注意,只要您不自行使用$ compile,AngularJS模板只会在应用程序引导期间编译一次。

AngularJS compile and linking phases AngularJS编译和链接阶段

To understand why your code is not working you must understand AngularJS compile and linking phases. 要了解代码无法正常工作的原因,您必须了解AngularJS编译和链接阶段。 Once you load your application AngularJS compiles the HTML element which contains a ng-app attribute with the $compile service, eg 加载应用程序后,AngularJS将编译包含带有$ compile服务的ng-app属性的HTML元素,例如

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

Compile phase 编译阶段

$compile identifies all directives in your HTML template calling each directive's compile function working its way up from your angular root element ($rootElement) through the html dom tree. $ compile标识HTML模板中的所有指令,调用每个指令的编译函数从角度根元素($ rootElement)到html dom树。

Linking phase 链接阶段

Each compile function returns a post linking and optionally a pre-linking function. 每个编译函数返回一个帖子链接和可选的预链接函数。 Once AngularJS has compiled the whole dom below the root element it starts to call the pre-link function in the same way has it has called the compile function before. 一旦AngularJS在根元素下面编译了整个dom,它就会以之前调用编译函数的方式开始调用预链接函数。 Once reached at the leaves of the dom the directive's post-link functions are called going back down to the root element. 一旦到达dom的叶子,指令的post-link函数就会被调用回到根元素。

Interpolation 插值

Strings with expressions between {{ and }} are handled by AngularJS as special directives called interpolate directives. 具有{{和}}之间表达式的字符串由AngularJS作为称为插值指令的特殊指令处理。 Just as any other directive these are created during compilation using the $interpolate service. 正如使用$ interpolate服务在编译期间创建的任何其他指令一样。 The $interpolate service receives an interplated string with a number of expressions and returns an interplate function. $ interpolate服务接收带有多个表达式的内部字符串,并返回一个内部板函数。 The post-link function of the interpolate directives create a watch on the interpolate function so that they can update the html node once any expression in the interplated string changes. 插值指令的post-link函数在插值函数上创建监视,以便一旦插入的字符串中的任何表达式发生更改,它们就可以更新html节点。

A translate module 翻译模块

When we now look at your code you are actually setting the text of an html element to an AngularJS interpolated string with an expression wrapped between {{ and }} in the post-link function of your directive. 当我们现在查看您的代码时,您实际上是将一个html元素的文本设置为一个AngularJS插值字符串,其中一个表达式在指令的post-link函数中包含在{{和}}之间。

As explained above at this time AngularJS has already compiled the template so that it will never compile the interpolated string with your expression. 如上所述,此时AngularJS已经编译了模板,因此它永远不会使用您的表达式编译插值字符串。

As I can see from your code you are trying to implement some kind of translate directive. 正如我从您的代码中看到的那样,您正在尝试实现某种translate指令。 Such directive must call the $compile function when it should consider interpolated strings and other AngluarJS template code in the translated string: 当它应该在翻译的字符串中考虑插值字符串和其他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
            }
        },
    };
}]).

The translate directive uses a translate service to get the actual translation. translate指令使用翻译服务来获取实际翻译。 The translateProvider has an add method which you can use to add translations eg from a language bundle: translateProvider有一个add方法,您可以使用它添加翻译,例如从语言包中添加:

.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
}).

Using the module 使用该模块

You can now use the module as follows: 您现在可以按如下方式使用该模块:

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

I have created a jsFiddle with a full example: http://jsfiddle.net/jupiter/CE9V4/2/ 我用一个完整的例子创建了一个jsFiddle: http//jsfiddle.net/jupiter/CE9V4/2/

另外,对于翻译,我建议http://github.com/pascalprecht/angular-translate

I think $compile may be what you're looking for. 我认为$compile可能就是你想要的。 Try: 尝试:

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

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

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