DEV Community

Junaid Ramzan
Junaid Ramzan

Posted on • Edited on

1

Render content based on viewport size with a directive

In this post I am going to describe how to use angular https://material.angular.io/cdk/layout/overview to build a structural directive that controls the rendering of components.

Structural directives are directives which change the DOM layout by adding and removing DOM elements. They are prefixed with the asterisk symbol(*). You may have used (*ngIf, *ngSwitch...)

Detailed explanation here

npm i @angular/cdk // install the angular cdk
Enter fullscreen mode Exit fullscreen mode
import { BreakpointObserver, Breakpoints } from '@angular/cdk/layout'
import { Directive, Input, OnDestroy, TemplateRef, ViewContainerRef } from '@angular/core'
import { Subscription } from 'rxjs'

type BreakpointSizes = 'XSmall' | 'Small' | 'Medium' | 'Large' | 'XLarge' | 'Desktop' | `(${'max-width'|'min-width'}: ${number}px)`

const sizes = new Map([
  ['XSmall', Breakpoints.XSmall],
  ['Small', Breakpoints.Small],
  ['Medium', Breakpoints.Medium],
  ['Large', Breakpoints.Large],
  ['XLarge', Breakpoints.XLarge]
])

@Directive({
  standalone: true,
  selector: '[appIfViewportMatch]'
})
export class IfViewportMatchDirective implements OnDestroy {
  private subscription!: Subscription
  private hasView = false

  constructor(
    private templateRef: TemplateRef<any>,
    private viewContainer: ViewContainerRef,
    private bpObserver: BreakpointObserver
  ) { }

  @Input() set appIfViewportMatch(mq: BreakpointSizes) {
    if (this.subscription) return
    const size = sizes.get(mq)

    this.subscription = this.bpObserver.observe(size || mq).subscribe(({ matches }) => {
      this.render(matches)
    })
  }

  ngOnDestroy(): void {
    this.subscription && this.subscription.unsubscribe()
  }

  private render(matches: boolean) {
    if (!this.hasView && matches) {
      this.viewContainer.createEmbeddedView(this.templateRef)
      this.hasView = true
    } else  {
      this.viewContainer.clear()
      this.hasView = false
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

The directive listens for viewport sizes and when a media query is matched the content is rendered into the DOM.

It only subscribes once on the first set to avoid multiple subscriptions.

This type provides some intellisense for the accepted values. It also provides the option to provide a custom media query.

type BreakpointSizes = 'XSmall' | 'Small' | 'Medium' | 'Large' | 'XLarge' | 'Desktop' | `(${'max-width'|'min-width'}: ${number}px)`
Enter fullscreen mode Exit fullscreen mode

Examples:

<!-- Only renders when the viewport is more than 600px -->
<hello name="{{ name }}" *appIfViewportMatch="'(min-width: 600px)'"></hello>

<!-- Mobile view -->
<h1 *appIfViewportMatch="'XSmall'">On mobile</h1>
Enter fullscreen mode Exit fullscreen mode

Here you can see a working example

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay