简体   繁体   中英

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 {{...}}. 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:

<title i18n>title</title>

HTML code after compilation:

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

Desired output:

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

The {{labels.title}} is implemented in controller.

Thank you for your help!

To dynamically compile DOM elements use the $compile service:

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

Please not that AngularJS templates are only compiled once during application bootstrap as long as you don't use $compile on your own.

AngularJS compile and linking phases

To understand why your code is not working you must understand AngularJS compile and linking phases. Once you load your application AngularJS compiles the HTML element which contains a ng-app attribute with the $compile service, eg

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

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. Once reached at the leaves of the dom the directive's post-link functions are called going back down to the root element.

Interpolation

Strings with expressions between {{ and }} are handled by AngularJS as special directives called interpolate directives. Just as any other directive these are created during compilation using the $interpolate service. The $interpolate service receives an interplated string with a number of expressions and returns an interplate function. 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.

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.

As explained above at this time AngularJS has already compiled the template so that it will never compile the interpolated string with your expression.

As I can see from your code you are trying to implement some kind of translate directive. Such directive must call the $compile function when it should consider interpolated strings and other AngluarJS template code in the translated string:

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. The translateProvider has an add method which you can use to add translations eg from a language bundle:

.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/

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

I think $compile may be what you're looking for. Try:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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