skip to Main Content
//Approach 1: 
  dynamicWidthClass = computed(()=>{
    if (window.innerWidth > 992 && window.innerWidth <= 1284) {
      return "pl-25";
    } else if (window.innerWidth > 1284 && window.innerWidth <= 1828) {
      return "pl-18";
    } else if (window.innerWidth < 992 || window.innerWidth >= 1920) {
      return "pl-5";
    }
    return "pl-3"
  });
  windowInnerWidth = signal(window.innerWidth)
  
  


//Approach 2:
  dynamicWidthClass = signal("pl-3");

  @HostListener("window:resize", ["$event"])
  onResize(event) {
    if (window.innerWidth > 992 && window.innerWidth <= 1284) {
      this.dynamicWidthClass.set("pl-25");
    } else if (window.innerWidth > 1284 && window.innerWidth <= 1828) {
      this.dynamicWidthClass.set("pl-18");
    } else if (window.innerWidth < 992 || window.innerWidth >= 1920) {
      this.dynamicWidthClass.set("pl-5");
    }
  }




   <div
        class="wrapper d-flex flex-column flex-row-fluid vh-100"
        id="kt_wrapper"
      >
        <app-header></app-header>
        <div [ngClass]="dynamicWidthClass()">
          <router-outlet></router-outlet>
        </div>
        <app-footer class="mt-20r"
        ></app-footer>
      </div>

I have the following two approaches of an example style snippet I intend to use to make my Angular route elements nested within a parent component more responsive based on the viewport width changes, which approach could suit me better? I currently noticed that both the approaches work differently but both have one thing in common which is that they require a reload on each viewport resize.

Am I doing this wrong and do I have to only rely on CSS media queries or some other approach? The main challenge I face here is that my current project is primarily powered by Bootstrap which comes loaded with a crazy number of classes that are hard to track if I intend to personalize it with my own, so I’m trying to implement a more robust approach to make my code responsive without dealing with custom classes or removing bootstrap

2

Answers


  1. SASS Solution:

    I think you should prefer media queries for such requirements. I use the sass operator @extend to apply this class dynamically.

    @media only screen and (max-width: 1920px) {
      #kt_wrapper > div {
        @extend .pl-25;
      }
    }
    
    @media only screen and (max-width: 920px) {
      #kt_wrapper > div {
        @extend .pl-18;
      }
    }
    
    @media only screen and (max-width: 320px) {
      #kt_wrapper > div {
        @extend .pl-5;
      }
    }
    

    Angular Solution:

    As per Matthieu Riegler feedback, the code from fromEvent will trigger a change detection cycle so to make the code more efficient you can wrap the fromEvent inside a zone.runOutsideAngular so that change detection does not fire multiple times and use zone.run to update the signal during the final update, I also use debounceTime to reduce the number of times the fromEvent fires.


    So as per the feedback, we move the listener to the constructor and emit the value only when necessary, also you should make sure this subscription is unsubscribed on destroy.

    constructor() {
      this.zone.runOutsideAngular(() => {
        fromEvent(window, 'resize')
          .pipe(
            takeUntilDestroyed(),
            startWith(null),
            debounceTime(500),
            map(() => {
              let output = 'pl-3';
              const windowSignalValue = window.innerWidth;
              const innerWidth = windowSignalValue || 0;
              console.log(innerWidth);
              if (innerWidth > 992 && innerWidth <= 1284) {
                output = 'pl-25';
              } else if (innerWidth > 1284 && innerWidth <= 1828) {
                output = 'pl-18';
              } else if (innerWidth < 992 || innerWidth >= 1920) {
                output = 'pl-5';
              }
              this.zone.run(() => {
                this.dynamicWidthClass.set(output);
              });
            })
          )
          .subscribe();
      });
    }
    

    Full Code:

    import { Component, computed, inject, NgZone, signal } from '@angular/core';
    import { bootstrapApplication } from '@angular/platform-browser';
    import { fromEvent, map, startWith, Subscription } from 'rxjs';
    import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop';
    import { debounceTime } from 'rxjs/operators';
    @Component({
      selector: 'app-root',
      standalone: true,
      template: `
        {{dynamicWidthClass()}}
      `,
    })
    export class App {
      zone: NgZone = inject(NgZone);
      dynamicWidthClass = signal('');
    
      constructor() {
        this.zone.runOutsideAngular(() => {
          fromEvent(window, 'resize')
            .pipe(
              takeUntilDestroyed(),
              startWith(null),
              debounceTime(500),
              map(() => {
                let output = 'pl-3';
                const windowSignalValue = window.innerWidth;
                const innerWidth = windowSignalValue || 0;
                console.log(innerWidth);
                if (innerWidth > 992 && innerWidth <= 1284) {
                  output = 'pl-25';
                } else if (innerWidth > 1284 && innerWidth <= 1828) {
                  output = 'pl-18';
                } else if (innerWidth < 992 || innerWidth >= 1920) {
                  output = 'pl-5';
                }
                this.zone.run(() => {
                  this.dynamicWidthClass.set(output);
                });
              })
            )
            .subscribe();
        });
      }
    
      ngDoCheck() {
        console.log('do check');
      }
    }
    
    bootstrapApplication(App);
    

    Stackblitz Demo

    Login or Signup to reply.
  2. Similar to the suggested solution of using @media queries and extend you could make use of CSS container queries, which simplifies your process in the same way by removing the need to explicitly listen for viewport resize events.

    However, whereas @media queries are still bound to the viewport dimensions @container queries are solely concerned with the dimensions of the parent element, which can have it’s situational benefits depending on your use case.

    /* Define the container with a name */
    #kt_wrapper {
      container-name: myContainer;
      container-type: inline-size;
    }
    
    
    /* Target the named container using @container */
    @container myContainer (min-width: 0px) {
      #kt_wrapper > div {
        padding-left: 5px;
      }
    }
    
    @container myContainer (min-width: 321px) {
      #kt_wrapper > div {
        padding-left: 18px;
      }
    }
    
    @container myContainer (min-width: 921px) {
      #kt_wrapper > div {
        padding-left: 25px;
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search