Tutorial

Detect Responsive Screen Sizes in Angular

Updated on September 15, 2020
    Default avatar

    By Jecelyn Yeen

    Detect Responsive Screen Sizes in Angular

    While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.

    Most of the time, we use CSS media queries to handle responsive, screen size changes to layout our content differently. However, there are times where CSS media queries alone isn’t sufficient for that. We need to handle the responsiveness in our code.

    In this article, I would like to share about how to detect responsive breakpoints in Angular, with a twist - we don’t maintaining responsive breakpoint sizes in your Typescript code (because responsive breakpoints are already defined in CSS).

    We will use Angular with Bootstrap in this example, but it works for any CSS frameworks and classes. Let’s start.

    What’s the Plan

    We will be using CSS Classes to determine the current responsive breakpoints. There are 5 breakpoints in Bootstrap CSS. The CSS classes to determine the visibility of each breakpoints is:

    • Visible only on xs: .d-block .d-sm-none
    • Visible only on sm: .d-none .d-sm-block .d-md-none
    • Visible only on md: .d-none .d-md-block .d-lg-none
    • Visible only on lg: .d-none .d-lg-block .d-xl-none
    • Visible only on xl: .d-none .d-xl-block

    The CSS display property will be toggled between none or block. We will apply these classes to HTML elements.

    Everytime when screen size changes, we will loop and find the HTML element with style display: block, this is how we will detect the current breakpoint.

    Here is the code if you are too excited to see the solution: https://stackblitz.com/edit/angular-size.

    The Implementation: Component

    Let’s create an Angular component size-detector.

    The component HTML template:

    <!-- size-detector.component.html -->
    <div *ngFor="let s of sizes" class="{{s.css + ' ' + (prefix + s.id) }}">{{s.name}}</div>
    

    The component Typescript code:

    // size-detector.component.ts
    ...
    export class SizeDetectorComponent implements AfterViewInit {
      prefix = 'is-';
      sizes = [
        {
          id: SCREEN_SIZE.XS, name: 'xs', css: `d-block d-sm-none`
        },
        {
          id: SCREEN_SIZE.SM, name: 'sm', css: `d-none d-sm-block d-md-none`
        },
        {
          id: SCREEN_SIZE.MD, name: 'md', css: `d-none d-md-block d-lg-none`
        },
        {
          id: SCREEN_SIZE.LG, name: 'lg', css: `d-none d-lg-block d-xl-none`
        },
        {
          id: SCREEN_SIZE.XL, name: 'xl', css: `d-none d-xl-block`
        },
      ];
    
      @HostListener("window:resize", [])
      private onResize() {
        this.detectScreenSize();
      }
    
      ngAfterViewInit() {
        this.detectScreenSize();
      }
    
      private detectScreenSize() {
        // we will write this logic later
      }
    }
    

    After looking at the component code, you might be wondering where is those SCREEN_SIZE.* value come from. It is an enum. Let’s create the screen size enum (You may create a new file or just place the enum in same component file)

    // screen-size.enum.ts
    
    /_ An enum that define all screen sizes the application support _/
    export enum SCREEN_SIZE {
      XS,
      SM,
      MD,
      LG,
      XL
    }
    

    Also, remember to add Bootstrap to your project! You may add it via npm or yarn, but in this example, we will use the easier way. Add the cdn link in index.html.

    <!-- index.html -->
    <link rel="stylesheet" 
        href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
    

    The code is pretty expressive itself.

    1. First, we define a list of sizes that we support and the CSS classes that used to determine each breakpoints.
    2. In the HTML, we loop through the size list, create div element, assign css and display it. Also note that we give each div an additional unique css class is-<SIZE_ENUM>.
    3. We have a function detectScreenSize. This is where we will write our logic to detect the screen size changes. We will complete that later.
    4. We need to run the logic to everytime when screen size changes. We use the HostListener decorator to listen to the window resize event.
    5. We also need to run the logic when we first initialize the application. We need to run it during the AfterViewInit component lifecycle hook.

    The Implementation: Service & Component

    Now we have the component code “almost” ready, let’s start implementing our resize service.

    // resize.service.ts
    
    @Injectable()
    export class ResizeService {
    
      get onResize$(): Observable<SCREEN_SIZE> {
        return this.resizeSubject.asObservable().pipe(distinctUntilChanged());
      }
    
      private resizeSubject: Subject<SCREEN_SIZE>;
    
      constructor() {
        this.resizeSubject = new Subject();
      }
    
      onResize(size: SCREEN_SIZE) {
        this.resizeSubject.next(size);
      }
    
    }
    

    The resize service code is simple:

    1. We create a rxjs subject resizeSubject.
    2. We have a public method onResize that receive size as the parameter. It will then push the value to the resize stream. (We will call this method later in our size-detector component)
    3. Notice that we use distinctUntilChanged operator in the resize observable. We use that to reduce unnecessary notification. For example, when your screen size change from 200px to 300px, it is still consider as xs size in bootstrap. We don’t need to notify in that case. (You can remove the operator if you need)
    4. We export the the resize stream as observable via onResize$. Any components, services, directives, etc can then subscribe to this stream to get notify whenever size is changed.

    Next, let’s go back to our size-detector component and update the detectScreenSize logic.

    // size-detector.component.ts
    ...
    
    private detectScreenSize() {
        constructor(private elementRef: ElementRef, private resizeSvc: ResizeService) { }
    
        const currentSize = this.sizes.find(x => {
          // get the HTML element
          const el = this.elementRef.nativeElement.querySelector(`.${this.prefix}${x.id}`);
    
          // check its display property value
          const isVisible = window.getComputedStyle(el).display != 'none';
    
          return isVisible;
        });
    
        this.resizeSvc.onResize(currentSize.id);
    }
    
    ...
    

    Let’s breakdown and go through the logic together:

    1. First, we will need to inject the ElementRef and our newly created ResizeService to our component.
    2. Base on our CSS classes, at any point of time, there will be ONLY ONE HTML element visible. We loop through our sizes array and find it.
    3. For each size of our sizes array, we will use HTML5 element’s query selector to find the element by the unique css class we defined earlier on is-<SIZE_ENUM>.
    4. Once we find the current visible element, we then notify our resize service by calling the onResize method.

    Using the Service and Component

    You may place the size-detector component under our root component app-component. For example:

    <!-- app.component.html -->
    
    <hello name="{{ name }}"></hello>
    <!-- Your size-detector component place here -->
    <app-size-detector></app-size-detector>
    

    In this example, I have another hello-component in the app-component, but that doesn’t matter.

    Since I place the component in app-component, means I can use the ResizeService everywhere (directives, components, services, etc).

    For instance, let’s say I want to detect the screen size changes in hello-component, I can do so by inject the ResizeService in constructor, then subscribe to the onSizeChange$ observable and do what I need.

    // hello.component.ts
    
    @Component({
      selector: 'hello',
      template: `<h1>Hello {{size}}!</h1>`,
    })
    export class HelloComponent  {
    
      size: SCREEN_SIZE;
    
      constructor(private resizeSvc: ResizeService) { 
        // subscribe to the size change stream
        this.resizeSvc.onResize$.subscribe(x => {
          this.size = x;
        });
      }
    
    }
    

    In the above code, we detect the screen size changes and simply display the current screen size value.

    See it in action!

    One of the real life use case scenario might be you have accordion on screen. In mobile, you would like to collapse all accordion panels, show only the active one at a time. However, in desktop, you might want to expand all panel.

    Summary

    This is how we can detect the screen size changes without maintaining the actual breakpoint sizes in our JavaScript code. Here is the code: https://stackblitz.com/edit/angular-size.

    If you think of it, it is not very often that the user changes the screen size when browsing the app. You may handle the screen sizes changes application wide (like our example above) or just handle it everytime you need it (per use case / component basis).

    Besides that, if you don’t mind to duplicate and maintain the breakpoint sizes in JavaScript code, you may remove the component, move the detectScreenSize into your service and change a bit on the logic. It is not difficult to implement that. (Try it probably?)

    That’s all. Happy coding!

    Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

    Learn more about us


    About the authors
    Default avatar
    Jecelyn Yeen

    author

    Still looking for an answer?

    Ask a questionSearch for more help

    Was this helpful?
     
    1 Comments
    

    This textbox defaults to using Markdown to format your answer.

    You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

    just fyi, you added the constructor constructor(private elementRef: ElementRef, private resizeSvc: ResizeService) { } to the body of the detectSizeChanges function

    Try DigitalOcean for free

    Click below to sign up and get $200 of credit to try our products over 60 days!

    Sign up

    Join the Tech Talk
    Success! Thank you! Please check your email for further details.

    Please complete your information!

    Get our biweekly newsletter

    Sign up for Infrastructure as a Newsletter.

    Hollie's Hub for Good

    Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

    Become a contributor

    Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

    Welcome to the developer cloud

    DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

    Learn more
    DigitalOcean Cloud Control Panel