Table of contents
Introduction
Since version 1.5 AngularJS introduced the component way of managing application architecture. It provides more benefits such as better encapsulation, isolated scope and well-defined lifecycle. It's also a step closer to the new the idea of Web Components running in the browser.
However, it has a lack of documentation for components, since for a long time there were only directives. Ironically, it should be easier to deal with new paradigm, but without having a clear guide it can be even harder. That's what happened to me when I needed to manage transclusion. This article describes how to use the transclusion in a "component way".
The task
In order to demonstrate the approach let's assume that we're working on "market app". It has only one language (English) and among all features, it has "veggie" component which looks like this:
<veggie>Spinach</veggie>
However, during internationalization process, the entire app went through "find and replace process" where for each specific language version "veggie" component started to have "language" attribute. Here's an example of Spanish variant:
<veggie data-language="es">Spinach</veggie>
Right now the task is to translate "veggie" content into the proper language. Here's an ordered list of tasks that will lead towards the goal:
- Get the component's content (in the mentioned case it's "Spinach")
- Read the
data-language
attribute (in the mentioned case it's "es"). - If the attribute does exist do the following:
- Pass content and language into the translationService and get value from it.
- Replace the original content with translated string or leave original content if there's no translation attribute
The code
In this situation, if the data-language
attribute
exists, original content of the component will be programmatically
replaced with a translated one. It means that we're dealing with
transclusion. In other words, transclusion is a process where initial
content is processed to be displayed as a new value. The content might
be modified or preserve the same value.
The tricky part is to actually read and manipulate transcluded
content. There's a lot of documentation for the directives
(for example using link
function), but there's no clear
instruction for the components
. For this special case,
AngularJS provides special $transclude
service that can be
injected in the same way as $scope
, $log
,
$window
or any other service.
Here's an example of the AngularJS component that does transclude magic:
// ...imports go here
class Veggie implements IOnInit {
public static $inject: string[] = ['translationService', '$transclude', '$element'];
private lang: string;
private content: string;
constructor(private translationService: TranslationService,
private $transclude: ng.ITranscludeFunction,
private $sce: ng.ISCEService,
private $element: ng.IAugmentedJQuery,
) { }
public $onInit() {
this.$transclude((clone: any) => {
this.content = clone.text(); // 1. Get the component's content
this.lang = angular.element(this.$element).attr('data-language'); // 2. Read the `data-language` attribute
if (this.lang) { // 3. If the attribute does exist do the following:
this.content = this.translationService.get(this.lang, this.content)); // 4. Pass content and language into the translationService and get value from it.
};
})
}
}
.component('veggie', {
app: '<span>{{$ctrl.content}}</span>', // 5. Replace original content with translated string or leave original content if there's no translation attribute
template: true,
transclude: Veggie,
controller: {
bindings'data-language': '<',
,
}; })
As we can see from the example above it's a good practice to pass anonymous function with a clone object to get a fresh clone of the original DOM and avoid double rendering.
Conclusion
This example demonstrates how to solve transclusion issues within new "component approach" in AngularJS. For more information and documentation please refer to the official AngularJS section of the transclusion.