Styling components in Angular 2
In my last post I went through creating a few simple components. In this post I’m going to play around with styling in those components …
Adding styles and using ngClass
First, I’m going to change the results so that we see all the customers highlighting the matches. So, it will look something like this …
SearchComponent
needs to change to …
import { Component } from "angular2/core";
import { Customer } from "./customer";
import { CriteriaComponent } from "./criteria.component";
import { ResultsComponent } from "./results.component";
@Component({
selector: "search",
directives: [CriteriaComponent, ResultsComponent],
template: `
<h1>Search for a company</h1>
<div>
<criteria (update)="handleSearch($event)"></criteria>
<results [data]="customers"></results>
</div>
`
})
export class SearchComponent {
customers: Customer[];
constructor() {
this.customers = [
{ name: "Alfreds Futterkiste", status: "" },
{ name: "Berglunds snabbköp", status: "" },
{ name: "Cactus Comidas para llevar", status: "" },
{ name: "Chop-suey Chinese", status: "" },
{ name: "Du monde entier", status: "" },
{ name: "Ernst Handel", status: "" },
{ name: "France restauration", status: "" },
{ name: "Island Trading", status: "" },
{ name: "Let's Stop N Shop", status: "" },
{ name: "Maison Dewey", status: "" },
{ name: "Paris spécialités", status: "" },
{ name: "Que Delícia", status: "" },
{ name: "Rancho grande", status: "" }
];
}
private updateCustomers(criteria: string) {
for (let i = 0; i < this.customers.length; i = i + 1) {
this.customers[i].status = "";
if (this.customers[i].name.toLowerCase().indexOf(criteria) === 0) {
this.customers[i].status = "highlight";
}
}
}
private handleSearch(args): void {
var i, ret;
this.updateCustomers(args.value);
}
}
- On line 21 we are now initialising a status property on each customer
- On Lines 23 to 30 we have an updateCustomers() method which sets the status property on a customer to highlight if matched
ResultsComponent
needs to change to …
import { Component, Input } from "angular2/core";
import { NgClass } from "angular2/common";
import { Customer } from "./customer";
@Component({
selector: "results",
directives: [NgClass],
styles: [
`
.highlight {
background: yellow;
}
`
],
template: `
<h2>Results</h2>
<ul style="{listStyle}">
<li *ngFor="#customer of data">
<span [ngClass]="customer.status">{{ customer.name }}</span>
</li>
</ul>
`
})
export class ResultsComponent {
@Input() data: Customer[];
constructor() {}
}
- We now have a highlight style name defined on line 10
- On line 18 we use the ngClass directive to set the class on the span to the customer’s status property
Encapsulation
I’m now going to play around to see whether the style leaks out of the component.
First, lets reference the style in the parent component, SearchComponent …
@Component({
selector: 'search',
directives: [CriteriaComponent, ResultsComponent],
template: `
<h1 class="highlight">Search for a company</h1>
<div>
<criteria (update)="handleSearch($event)" ></criteria>
<results [data]="customers"></results>
</div>
`
})
… and we see that the style doesn’t leak up - “Search for a company” doesn’t have a yellow background …
Now let’s add a child component to ResultSubComponent to see if the style leaks down. ResultsComponent is now …
import { Component, Input, ViewEncapsulation } from "angular2/core";
import { NgClass } from "angular2/common";
import { Customer } from "./customer";
import { ResultSubComponent } from "./resultsub.component";
@Component({
selector: "results",
directives: [NgClass, ResultSubComponent],
styles: [
`
.highlight {
background: yellow;
}
`
],
template: `
<h2>Results</h2>
<resultsub></resultsub>
<ul style="{listStyle}">
<li *ngFor="#customer of data">
<span [ngClass]="customer.status">{{ customer.name }}</span>
</li>
</ul>
`
})
export class ResultsComponent {
@Input() data: Customer[];
@Input() matches: Customer[];
constructor() {}
}
… and the code for ResultSubComponent
is …
import { Component } from "angular2/core";
@Component({
selector: "resultsub",
template: `
<span class="highlight">is this text highlighted?</span>
`
})
export class ResultSubComponent {
constructor() {}
}
On line 6 we reference the highlight class. If we run the app we see that the style doesn’t leak down - “is this text highlighted?” doesn’t have a yellow background.
This is great - generally, this is what we want. However, what if we do want the styles to leak down into sub components? We can make use of the ViewEncapsulation enum on the encapsulation property in the search component …
@Component({
encapsulation: ViewEncapsulation.Native,
selector: 'results',
directives: [NgClass, ResultSubComponent],
styles: [`
.highlight {
background: yellow;
}
`],
template: `
<h2>Results</h2>
<resultsub></resultsub>
<ul style={listStyle}>
<li *ngFor="#customer of data">
<span [ngClass]="customer.status" >{{customer.name}}</span>
</li>
</ul>
`
})
Line 2 sets the encapsulation to “Native” which means the shadow DOM is used. The result is what we want - the styles leak down into the sub components …
However, although this works in Chrome, it doesn’t work in Firefox, IE or Edge because they don’t support shadow DOM yet. Here are all the encapsulation modes …
- None. No encapsulation - the styles will leak up and down
- Native. Shadow DOM encapsulation - the styles will leak down, but this only works in Chrome at the moment
- Emulated. The default encapsulation - the styles do not leak out