This post is intended for people who already have some familiarity with Angular 2-7 and up. In this article we are going to explore how Angular Directives can be used to help us design a site with CSS Grid. So what are the differences between a Directive and a Component and what use cases are there for the use of Directives.
An Angular Component must have a view attached to it. Whereas a directive adds behaviour to an existing DOM element or an existing component instance.
In this article we are going to design a website layout using Angular Directives just like in the following demo.
First, we need a Grid Directive that will transform a <div> element into a CSS Grid.
import {Directive, ElementRef, HostBinding, Input, Renderer2} from '@angular/core'; @Directive({ selector: '[d-grid]' }) export class CssGridDirective { @HostBinding('style.display') display = 'grid'; @Input() areas: string[][] | null = null; @Input() columns: string[] | null = null; @Input() rows: string[] | null = null; @HostBinding('style.grid-template-columns') get gridTemplateColumns() { return this.columns ? this.columns.join(' ') : null; } @HostBinding('style.grid-template-rows') get gridTemplateRows() { return this.rows ? this.rows.join(' ') : null; } @HostBinding('style.grid-template-areas') get gridTemplateAreas() { return this.areas ? this.areas.map(rows => `"${rows.join(' ')}"`).join(' ') : null; } constructor(private readonly el: ElementRef, private readonly renderer: Renderer2) { this.renderer.setStyle(this.el.nativeElement, 'height', '100vh'); } }
What is happening here? We created a directive that will be applied to a <div> element to create the CSS Grid. It has 3 Inputs.
- Areas – A two-dimensional array will be used to apply the grid-template-areas css rule to our grid
- Rows – An array of string i.e [‘1fr’, ‘1fr’, ‘1fr’]
- Columns – An array of string i.e [‘1fr’, ‘1fr’, ‘1fr’]
When Areas, Rows and Columns are set, the class getters and @HostBinding decorator will transform the arrays into css rules which will be applied to the <div> element.
And last, we need a GridAreaDirective which will be applied to the child elements of the <div> that we applied our GridDirective to.
import {Directive, ElementRef, HostBinding, HostListener, Input, Renderer2} from '@angular/core'; @Directive({ selector: '[d-grid-area]' }) export class CssGridAreaDirective { @HostBinding('style.grid-area') @Input('d-grid-area') area: string; @HostListener('mouseover') onMouseOver() { this.changeBgColor('lightgrey'); } @HostListener('mouseleave') onMouseLeave() { this.removeBgColor(); } constructor(private el: ElementRef, private renderer: Renderer2) { } public changeBgColor(color: string) { this.renderer.setStyle(this.el.nativeElement, 'background-color', color); } public removeBgColor() { this.renderer.removeStyle(this.el.nativeElement, 'background-color'); } }
The name of out CssGridAreaDirective will be ‘d-grid-area’. We can also use the same name for the input, thus we will apply the directive to a <div> element and set the input at the same time. Here we also have @HostBinding to set the css grid-area property and @HostListener to listen for the events on the <div> element to which this directive will be applied to. After creating the directives we must declare them in our AppModule.ts.
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { AppComponent } from './app.component'; import {CssGridDirective} from './css-grid-directives/CssGridDirective'; import {CssGridAreaDirective} from './css-grid-directives/CssGridAreaDirective'; @NgModule({ declarations: [ AppComponent, CssGridDirective, CssGridAreaDirective ], imports: [ BrowserModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
Now onto our AppComponent.ts where we will define out grid rows, columns and areas.
import {Component, OnInit} from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit { gridAreas: string[][] = []; gridRows: string[] = []; gridCols: string[] = []; ngOnInit(): void { this.gridRows = ['1fr', '5fr', '2.5fr']; this.gridCols = ['1fr', '5fr', '1fr']; this.gridAreas = [ ['header', 'header', 'header'], ['sidebar', 'main', 'aside'], ['sidebar', 'footer', 'footer'] ]; } }
And of course we need the app.component.html to display the CSS Grid. And that’s where we will apply the directives that we created.
<div d-grid [columns]="gridCols" [rows]="gridRows" [areas]="gridAreas" class="mainGrid"> <div d-grid-area="header"></div> <div d-grid-area="sidebar"></div> <div d-grid-area="main"></div> <div d-grid-area="aside"></div> <div d-grid-area="footer"></div> </div>
And last but not least is the app.component.css.
.mainGrid { grid-gap: 1rem; } .mainGrid div { background-color: lightsalmon; border: 2px solid black; }
And that’s it, after we run the app with ng serve we should see a layout very similar to CSS Grid Playground.
Source code can be found on GitHub.
Cheers.