Reputation: 183
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!
Upvotes: 2
Views: 4762
Reputation: 43023
Additionally, for translation I would recommend http://github.com/pascalprecht/angular-translate.
Upvotes: 2
Reputation: 4056
Please not that AngularJS templates are only compiled once during application bootstrap as long as you don't use $compile on your own.
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, e. g.
<html ng-app="MyApp"></html>
$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.
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.
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.
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 e. g. 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
}).
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/
Upvotes: 5
Reputation: 4047
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
Upvotes: 8
Reputation: 925
I think $compile
may be what you're looking for. Try:
var bindLabel = $compile('{{labels.' + element.text() + '}}')(scope);
Upvotes: 0