Angular2 Quicky - Component ViewEncapsulation and WebComponents

Angular2 Quicky - Component ViewEncapsulation and WebComponents

Component is perhaps the most overloaded word when it comes to JavaScript frameworks in these days. Every single framework has it's own definition of what exactly a component is, also Angular2.

Components are the building blocks of ng2 apps. By default ng2 embeds components into the DOM. When ng2 renders your component, the entire contents of your template (string or loaded from the given url) will be placed inside of the component's selector.

By default there are tow different ways how Angular is injecting the view for a given component. ViewEncapsulation is responsible for defining how Views will be injected. If you create a component as shown in snippet 1

// Snippet 1

import {Component, Input} from 'angular2/core';

@Component({
  selector: 'todo-item'
  templateUrl: 'templates/todoitem.html'
})
export class TodoItemComponent{  
  @Input() caption: string;
}

Angular will use ViewEncapsulation.None. Which will result in the following DOM.

As soon as you change your component definition and provide either inline styles using the styles property or reference a Stylesheet file using the styleUrls property, Angular will use ViewEncapsulation.Emulated which will result in the following DOM. Let's review the updated @Component definition first

@Component({
  selector: 'todo-item',
  templateUrl: 'templates/todoitem.html',
  styleUrls: ['styles/todoitem.css']
})

Here the simple CSS which we provide at styles/todoitem.css

.todo-item-caption{
  color:purple;
}

Those changes will result in the following HTML

Notice the _ngcontent-uxg-2 attribute which is automatically added to the template's root node and _nghost-uxg-2 added to the selector? Why does Angular add such an attribute? Open the inspector and double check the styles you've provided.

Angular is adding those classes to enforce CSS styles only for the given component. It is emulating the Shadow DOM. So your CSS will look similar to the following when being rendered

.todo-item-caption[_ngcontent-uxg-2]{
  color:purple;
}

This works fine in most situations. But there are situations where you need more control over your template. Let's extend our example from above. Let's say we're referencing another stylesheet on an application wide level with the following content.

#stripped thousands of lines of CSS :)
span {  
 color: blue !important;
}

so every span will be rendered in blue. also our todo-item-caption our custom stylesheet which is trying to set the color to purple will be overruled by the .span class because of the !important operator.

WebComponents to the rescue

Angular ships with a third mode for ViewEncapsulation, the Native mode. Native means nothing more than using standard WebComponents instead of emulating or just injecting the HTML. The native encapsulation mode is easy to activate. Just alter your component definition to match the following

@Component({
  selector: 'todo-item',
  templateUrl: 'templates/todoitem.html',
  styleUrls: ['styles/todoitem.css']
  encapsulation: ViewEncapsulation.Native
})

Reload the app and verify the DOM. Now you will see that Angular is using a WebComponent to display the todo-item directive.

Using WebComponents you can ensure that your component is rendered as you want and not changed by stylesheets defined on an application wide level.

Here is the entire app published as plunkr where you will find the buy milk caption in purple, feel free to play around.

Comments

comments powered by Disqus