Component Styling vs. Attribute Directives vs. Global Styling, which to use?

Component Styling vs. Attribute Directives vs. Global Styling, which to use?

I was following a course on Angular 12 and got to a section where Angular Directives were taught, more specifically: attribute directives. The course gave a nice example of creating an [appHighlight] attribute directive to use for highlighting the styling of a paragraph in my application, an example also found in the official Angular documentation for custom attribute directives. Nice, I thought, but in my head, I was like: “CSS can do that too!” This started to form a question in my head: when should you use directives and when do you use regular globalized CSS to adjust your view, and on the other side, when is it best to implement the styling directly into your component instead of adding it as an attribute directive?

Component Styling vs. Attribute Directives

Looking on the internet, the latter was clear pretty fast: components are a type of directive and are always (a bundle of) elements, where directives can be much more than just a template. Directives can also be an attribute, an element name, a comment, or a CSS class. Components are used to create reusable HTML templates and are contained in an encapsulated view, meaning the styling cannot be used in different components.

When to use Component Styling:

So if you’re simply creating a block of code with some additional styling, stick to component styling! The styling of the component can be passed in its @Component-decorator and will only be used on the component itself (unless told otherwise).

import { Component } from "@angular/core";

@Component({
  selector: 'app-component-styling',
  template: `
    <p>This paragraph will be red and on hover turn blue. Paragraphs outside this component won’t.</p>
  `,
  styles: [`
    p { color: red; }
    p:hover { color: blue; }
  `]
})
export class ComponentStylingComponent {}

Attribute Directives vs. Global CSS

Directives however can be used more widely. Directives don’t need to have a template and can be used to design re-usable components in a more behavior-oriented manner. There’s more to directives though: we can also use a directive as an attribute. Therefore you need to create a new directive and pass it a CSS-selector. So wait, if I am going to do some styling changes and I have to set a CSS selector, why not stick to just global CSS classes? There’s a couple of reasons for that but the short answer can be found in the Angular 2 style guide: “Do use attribute directives when you have presentation logic without a template.”

When to use Attribute Directives:

A first reason would be to make sure the selector you are using is not accidentally altering the styling of components deeply nested in your app. Say you have a component on the navigation bar that should be highlighted when hovering and you have a button, somewhere deeply nested in your shop application using that same class. Using global CSS selectors can cause unintended styling on your components, especially when working on a big application.

A second reason would be that the logic behind the event firing on the directive is more than just a regular hover or active event. Say you would want to highlight the color of a paragraph red on January till June and blue on July till December. How would you create such styling in CSS? You wouldn’t! In these cases where more application logic is needed to implement your styling, you would use attribute directives.

<p appAttrDirective>This paragraph's color will be set by the attribute directive.</p>
import { Directive, ElementRef, HostListener, OnInit, Renderer2 } from "@angular/core";

@Directive({
  selector: '[appAttrDirective]'
})
export class AttrDirective implements OnInit {
  firstSemesterColor: string = 'red';
  secondSemesterColor: string = 'blue';

  constructor(private elementRef: ElementRef, private renderer: Renderer2) {}

  ngOnInit() { this.renderer.setStyle(this.elementRef.nativeElement, 'color', 'black'); }

  @HostListener('mouseenter') mouseOver(eventData: Event) {
    if (new Date().getMonth() <= 5) {
      this.renderer.setStyle(this.elementRef.nativeElement, 'color', this.firstSemesterColor);
    } else {
      this.renderer.setStyle(this.elementRef.nativeElement, 'color', this.secondSemesterColor);
    }
  }

  @HostListener('mouseleave') mouseLeave(eventData: Event) {
    this.renderer.setStyle(this.elementRef.nativeElement, 'color', 'black');
  }
}

To round up the reasons for choosing an attribute directive: what if we want to highlight the text differently on different components, but reuse the code of our attribute directive itself. Say we have a text we want to highlight, but on our shop page we want the color red and on the checkout page we want the color blue. By using attribute directives, we can pass along the color as a parameter, where for CSS, we would have to create a class for every possible color and put the color in the class or inline style the element which is not reusable.

<p appAttrDirective firstSemesterColor="green" secondSemesterColor="yellow">This paragraph's color will be set by the attribute directive with variable properties.</p>
import { Directive, ElementRef, HostListener, Input, OnInit, Renderer2 } from "@angular/core";

@Directive({
  selector: '[appAttrDirective]'
})
export class AttrDirective implements OnInit {
  @Input() firstSemesterColor: string = 'red';
  @Input() secondSemesterColor: string = 'blue';

  constructor(private elementRef: ElementRef, private renderer: Renderer2) {}

  ngOnInit() { this.renderer.setStyle(this.elementRef.nativeElement, 'color', 'black'); }

  @HostListener('mouseenter') mouseOver(eventData: Event) {
    if (new Date().getMonth() <= 5) {
      this.renderer.setStyle(this.elementRef.nativeElement, 'color', this.firstSemesterColor);
    } else {
      this.renderer.setStyle(this.elementRef.nativeElement, 'color', this.secondSemesterColor);
    }
  }

  @HostListener('mouseleave') mouseLeave(eventData: Event) {
    this.renderer.setStyle(this.elementRef.nativeElement, 'color', 'black');
  }
}

When to use Global Styling:

Then when should you use global styling instead of attribute directives? As you can read above, directives are mainly used when there is any form of logic involved. Is your styling just a regular hover on a button you are going to use through your entire application, you would not necessarily implement a directive but just add a class to the global stylesheet.

Conclusion

To round things up; Components are a type of directive with an encapsulated view. Styling elements within this will by default not be available outside of the component and will therefore not be reusable but also won’t unintendedly change different components. Global styling however is reusable but can make things messy if your styling is unintendedly used on elements you did not expect it to style. Setting up global styling for default items used through your entire application is not a bad practice though. Attribute directives are used when any form of logic is necessary for styling the element and no template is involved or when you want to pass different parameters to the directive.