arrow-rightgithublinkedinscroll-to-topzig-zag

Mastering transclusion inside AngularJS Component

Last Updated On

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:

  1. Get the component's content (in the mentioned case it's "Spinach")
  2. Read the data-language attribute (in the mentioned case it's "es").
  3. If the attribute does exist do the following:
  4. Pass content and language into the translationService and get value from it.
  5. 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.
      }
    });
  }
}

app.component('veggie', {
  template: '<span>{{$ctrl.content}}</span>', // 5. Replace original content with translated string or leave original content if there's no translation attribute
  transclude: true,
  controller: Veggie,
  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.