CSS Grid With Angular Directives

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.

You may also like...

Leave a Reply

Your email address will not be published. Required fields are marked *