Configuring our template from our component class

The Component metadata also supports several settings that contribute to easy template management and configuration. On the other hand, Angular 2 takes advantage of the CSS encapsulation functionalities of Web Components.

Internal and external templates

As our applications grow in size and complexity, chances are that our templates will grow as well, hosting other components and bigger chunks of HTML code. Embedding all this code in our component class definitions will become a cumbersome and unpleasant task and quite prone to errors by the way. In order to prevent this from happening, we can leverage the templateUrl property, pointing to a standalone HTML file that contains our component HTML markup.

Back to our previous example, we can refactor the @Component decorator of our PomodoroTimerComponent class to point to an external html file containing our template. Create a new file named pomodoro-timer.html in the same workspace where our pomodoro-timer.ts file lives and populate it with the same HTML we configured in our PomodoroTimerComponent class:

<div class="container text-center">
  <img src="assets/img/pomodoro.png" alt="Pomodoro"/>
  <countdown 
    [seconds]="25"
    (complete)="onCountdownCompleted()"
    #counter>
  </countdown>
  <p>
    <button class="btn btn-default"(click)="counter.seconds = 25">
      Reset countdown to 25 seconds
    </button>
  </p>
  <p *ngIf="counter.seconds < 10">
    Beware! Only
    <strong>{{ counter.seconds }} seconds</strong>
    left.
  </p>
</div>

Now, we can polish our @Component decorator to point to that file instead of defining the HTML inside the decorator metadata:

@Component({
  selector: 'pomodoro-timer',
  directives: [CountdownComponent],
  templateUrl: './pomodoro-tasks.html'
})
class PomodoroTimerComponent {
  // Class follows below...
}

Note

External templates follow a certain convention in Angular 2, enforced by the most popular Angular 2 coding style guides out there, which is to share the same filename than the component they belong to, including any filename prefix or suffix we might append to the component filename. We will see this when exploring component naming conventions in Chapter 5, Building an Application with Angular 2 Components. This way, it is easier to recognize, or even search with your IDE search built-in fuzzy finder tool, what HTML file is in fact the template of a specific component.

What is the threshold for creating standalone templates rather than keeping the template markup inside the component? It depends on the complexity and size of the template. Common sense will be your best advisor in this case.

Encapsulating CSS styling

In order to better encapsulate our code and make it more reusable, we can define CSS styling within our components. These internal style sheets are a good way to make our components more shareable and maintainable. There are three different ways of defining CSS styling for our components.

The styles property

We can define styles for our HTML elements and class names through the style property in the component decorator, like this:

@Component({
  selector: 'my-component',
  styles: [`
    p {
      text-align: center;
    }
    table {
      margin: auto;
    }`]
})

This property will take an array of strings containing CSS rules each and apply them to the template markup by embedding those rules at the head of the document as soon as we bootstrap our application. We can either inline the styling rules in a single line, or take advantage of ES2015 template strings to indent the code and make it more readable as depicted in the example above.

The styleUrls property

Just like styles, styleUrls will accept an array of strings, although each one will represent a link to an external style sheet though. This property can be used alongside the styles property as well, defining different sets of rules where required:

@Component({
  selector: 'my-component',
  styleUrls: ['path/to/my-stylesheet.css'],
  styles: [`
    p {
      text-align: center;
    }
    table {
      margin: auto;
    }`]
})

Inline style sheets

We can also attach the styling rules to the template itself, no matter whether it's an inline template or a template served through the templateUrl parameter:

@Component({
  selector: 'app',
  template: `
    <style> p { color: red; } </style>
    <p>I am a red paragraph</p>`
})

Managing view encapsulation

All the preceding sections (styles, styleUrls, and inline style sheets) will be governed by the usual rules of CSS specificity (https://developer.mozilla.org/en/docs/Web/CSS/Specificity). CSS management and specificity becomes a breeze on browsers that support Shadow DOM, thanks to scoped styling. CSS styles apply to the elements contained in the component but do not spread beyond its boundaries.

On top of that, Angular will embed these style sheets at the head of the document, so they might affect other elements of our application. In order to prevent this from happening, we can set up different levels of view encapsulation.

In a nutshell, encapsulation is the way Angular needs to manage CSS scoping within the component for both Shadow DOM-compliant browsers and those that do not support it. For all this, we leverage the ViewEncapsulation enum, which can take any of these values:

  • Emulated: This is the default option, and it basically entails an emulation of native scoping in Shadow DOM through sandboxing the CSS rules under a specific selector that points to our component. This option is preferred to ensure that our component styles will not be affected by other existing libraries on our site.
  • Native: Use the native Shadow DOM encapsulation mechanism of the renderer, and it only works on browsers that support Shadow DOM.
  • None: Template or style encapsulation is not provided. The styles will be injected as is into the document's header.

Let's check out an actual example. First, import the ViewEncapsulation enum into the script, and then create an encapsulation property with the Emulated value. Then, let's create a style rule for our countdown text so any <h1> (!) tag is rendered in dark red:

import { 
  Component,
  EventEmitter,
  Input,
  Output, 
  ViewEncapsulation
} from '@angular/core';
import { bootstrap } from '@angular/platform-browser-dynamic';

@Component({
  selector: 'countdown',
  template: '<h1>Time left: {{seconds}}</h1>',
  styles: ['h1 { color: #900 }'],
  encapsulation: ViewEncapsulation.Emulated
})
class CountdownComponent { 
  // Etc... 
} 

Now, click on the browser's dev tools inspector and check the generated HTML to discover how Angular 2 injected the CSS inside the page <HEAD> block. The just injected style sheet has been sandboxed to ensure that the global CSS rule we defined at the component setup in a very non-specific way for all <h1> elements only applies to matching elements scoped by the CountdownComponent component exclusively.

We recommend that you try out different values and see how the CSS code is injected into the document. You will immediately notice the different grades of isolation that each variation provides.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset