Utilizing effective CSS media queries within an Angular application

According to what I've read, it is not recommended in Angular to use the CSS hidden element to hide an element like this:

.container{
  background-color : powderblue;
  height : 50px;
  width : 100%
}

@media (max-width: 400px){
    .container{
        display: none;
    }

}
<div class="container"></div>

Instead, the preferred approach in Angular for showing or hiding elements is using the *ngIf directive.

Question

How can I make the * ngIf respond to the media query in a way that is consistent with Angular standards?

Answer №1

If you want to make your Angular project responsive, consider using the angular/breakpoints-angular-cdk library.

Here are the steps to follow:

First, run the following command in your terminal:

npm install @angular/cdk

Next, import the layout module and add it to the list of imports in your NgModule:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { LayoutModule } from '@angular/cdk/layout';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    LayoutModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})

After that, you can start using it in your component by importing the necessary classes from @angular/cdk/layout:

import { Component, OnInit } from '@angular/core';
import { BreakpointObserver, BreakpointState } from '@angular/cdk/layout';

@Component({ ... })
export class AppComponent implements OnInit {
  public showContainer: boolean;
  
  constructor(public breakpointObserver: BreakpointObserver) {}

  ngOnInit() {
    this.breakpointObserver.observe(['(min-width: 400px)'])
      .subscribe((state: BreakpointState) => {
        if (state.matches) {
          this.showContainer = true;
        } else {
          this.showContainer = false;
        }
      });
  }
}

For updated information on how to handle screen sizes in newer versions of Angular, be aware of the constant provided for screen size identification:

UPDATE: If you're using a newer version of Angular, utilize the Breakpoints constant:

import { Component, OnInit } from '@angular/core';
import { BreakpointObserver, BreakpointState, Breakpoints } from '@angular/cdk/layout';

@Component({ ... })
export class AppComponent implements OnInit {
  public showContainerInTablet: boolean;
  public showContainerInHandset: boolean;

  constructor(public breakpointObserver: BreakpointObserver) {}

  ngOnInit() {
    this.breakpointObserver.observe([
      Breakpoints.TabletPortrait,
      Breakpoints.HandsetLandscape
    ]).subscribe((state) => {
      const breakpoints = state.breakpoints;
      this.showContainerInHandset = false;
      this.showContainerInTablet = false;

      if (breakpoints[Breakpoints.TabletPortrait]) {
        this.showContainerInTablet = true;
        console.log("Screens match TabletPortrait");
      } else if (breakpoints[Breakpoints.HandsetLandscape]) {
        this.showContainerInHandset = true;
        console.log("Screens match HandsetLandscape");
      }
    });
  }
}

For more detailed tutorials on responsive design in Angular, visit the official Angular website.

Answer №2

Using Angular flex layout can provide a more efficient solution in this case. With its special responsive features, there's no need for media queries.

fxShow: This attribute determines whether the host element should be displayed or not.

<div fxShow [fxShow.xs]="isVisibleOnMobile()"></div>

fxHide: This attribute specifies if the host element should not be displayed.

<div fxHide [fxHide.gt-sm]="isVisibleOnDesktop()"></div>

Avoiding excessive CSS code, Angular flex layout is also highly compatible with angular material. Discover more about Angular Flex Layout here.

Answer №3

This base class that I've created has proven to be effective in my projects.

import { HostBinding, OnDestroy, OnInit } from '@angular/core';
import { MediaObserver } from '@angular/flex-layout';
import { Subscription } from 'rxjs';

export class MediaQueryClassBaseComponent implements OnInit, OnDestroy {
    @HostBinding('class.xl') private xl: boolean;
    @HostBinding('class.lg') private lg: boolean;
    @HostBinding('class.md') private md: boolean;
    @HostBinding('class.sm') private sm: boolean;
    @HostBinding('class.xs') private xs: boolean;

    private mediaObserverSubscription: Subscription | undefined = undefined;

    constructor(protected readonly mediaObserver: MediaObserver) {}

    ngOnInit(): void {
        if (this.mediaObserverSubscription)
            return;
        this.mediaObserverSubscription = this.mediaObserver.media$.subscribe(x => {
            this.xl = x.mqAlias == 'xl';
            this.lg = x.mqAlias == 'lg';
            this.md = x.mqAlias == 'md';
            this.sm = x.mqAlias == 'sm';
            this.xs = x.mqAlias == 'xs';
        });
    }

    ngOnDestroy(): void {
        if (!this.mediaObserverSubscription)
            return;
        this.mediaObserverSubscription.unsubscribe();
        this.mediaObserverSubscription = undefined;
    }
}

If you use this base class as a parent for your component, the host element of your component will automatically receive a class based on the media query alias. For example...

<app-search-bar class="some-class" _nghost-c5 ...>

...will transform into...

<app-search-bar class="some-class lg" _nghost-c5 ...>

The added media query alias 'lg' will adapt according to the window size, simplifying the process of applying responsive styles for different screen sizes within your component's SCSS files.

:host-context(.sm, .md) { // styles specific to both sm and md media sizes
    .header {
        padding: 6px;
        width: 420px;
    }
}

:host-context(.lg, .xl) { // styles specific to both lg and xl media sizes
    .header {
        padding: 10px;
        width: 640px;
    }
}

You can find the complete file on my gist here.

Answer №4

Here is a customized solution I found online for hiding elements by removing them from the DOM instead of just using display:none, similar to *ngIf functionality in Angular.

    import {
        Input,
        Directive,
        TemplateRef,
        ViewContainerRef,
        OnDestroy,
        ChangeDetectorRef
    } from '@angular/core';

    @Directive({
        selector: '[mqIf]'
    })
    export class MqIfDirective implements OnDestroy {
        private prevCondition: boolean = null;
        i = 0;

        private mql: MediaQueryList;
        private mqlListener: (mql: MediaQueryList) => void;
        constructor(private viewContainer: ViewContainerRef,
                    private templateRef: TemplateRef,
                    private ref: ChangeDetectorRef) {
        }

        @Input()
        set mqIf(newMediaQuery: string) {
            if (!this.mql) {
                this.mql = window.matchMedia(newMediaQuery);

                this.mqlListener = (mq) => {
                    this.onMediaMatchChange(mq.matches);
                };
                this.mql.addListener(this.mqlListener);
            }

            this.onMediaMatchChange(this.mql.matches);
        }

        ngOnDestroy() {
            this.mql.removeListener(this.mqlListener);
            this.mql = this.mqlListener = null;
        }

        private onMediaMatchChange(matches: boolean) {
            if (matches && !this.prevCondition) {
                this.prevCondition = true;
                this.viewContainer.createEmbeddedView(this.templateRef);
            } else if (!matches && this.prevCondition) {
                this.prevCondition = false;
                this.viewContainer.clear();
            }

            if (this.i > 0) {
                this.ref.detectChanges();
            }
            else
                this.i++;
        }
    }

Check out more details here

Answer №5

.wrapper{
  background-color : skyblue;
  height : 50px;
  width : 100%
}

@media (max-width: 400px){
    .wrapper{
        display: grid;
    }

}
<div class="wrapper"></div>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips for adjusting the page display when errors occur during form submission

Within my django application, I have designed a page featuring a button that controls the operation of a clock. Initially, the page displays only the button, with a hidden form where users can input details such as their name and description. When the butt ...

The backdrop moving in a reverse direction

I recently made a tweak to this code that successfully moves the background in the opposite direction of the scroll. However, there is now an issue where it leaves a blank space at the top, even when the background is set to repeat. The change I made was s ...

Non-clickable links in a CSS menu

I managed to create a dropdown menu without relying on any JavaScript, but I'm encountering some issues with its functionality in Firefox and IE 10. This is how it appears: Both Link1 and Link2 are clickable links. In Chrome, clicking on them works s ...

Excessive Width Issue Caused by Bootstrap 4 Navbar Items

Having trouble with my Bootstrap 4 Navbar menu implementation. When I add more items, they overflow the container instead of floating correctly. Any CSS suggestions to temporarily fix this issue? <!DOCTYPE html> <html lang="en"> <head> ...

Halt the CSS transition on the preceding element

I tried to pause a CSS transition and came across a question with a solution that seems similar: Is there a way to pause CSS transition mid-way? However, I couldn't make it work for my code. I suspect the issue lies with the before element. What cou ...

Personalize your Native Base tab design

Recently, I've delved into the world of React Native and now find myself in need of assistance with customizing my tabs. Utilizing advanced tabs by Native Base, I aim to transform this: https://i.stack.imgur.com/xhNwP.png Into something more akin ...

Is there a way to make the iOS Safari address bar shrink when scrolling, all while having a flexbox sticky footer inside a container?

I have implemented a standard flexbox sticky footer solution in my code: <body> <div class="wrapper"> <div class="header">Page Header</div> <div class="body"> <div class="b ...

Issues with Angular displaying filter incorrectly

Whenever a user chooses a tag, I want to show only the posts that have that specific tag. For instance, if a user selects the '#C#' tag, only posts with this tag should be displayed. This is how my system is set up: I have an array of blogs that ...

Difficulty with Angular 11 Material Theme showing Purple/Green color combination

After integrating Angular Material into my project using Angular-CLI, I opted for the Purple/Green pre-built theme. Despite the preview showing a dark background color for the theme, my background remains white. I'm puzzled as to why the color hasn&ap ...

Guide on how to duplicate a form upon clicking an "add" link in Angular 2

Is it possible to dynamically repeat a form in Angular2 when clicking on a link? I am looking for a way to add the same form below the previous one when a specific link is clicked. Any suggestions or immediate replies would be greatly appreciated. For ins ...

Choosing different elements using identical classes in JQuery

Struggling with a coding problem that seems like it should be an easy fix, but can't quite figure it out. The HTML code I have is as follows: <section class="actualite"> <div class="actualite-text"> <h3 class="title"&g ...

Issues with Angular 4 Rxjs subject subscription functionality

My application consists of a shared service named data.service.ts, which contains the following code: public pauseProjectTask$: Subject<any> = new Subject<any>(); pauseTaskProject(taskData, type){ this.pauseProjectTask$.next(taskData); ...

Organizing Data in Angular 2

I'm trying to modify this code so that it can sort both A-Z and Z-A using a single button. The current code only sorts from A-Z and doesn't work in reverse order. Here is the code I have, but it's not functioning correctly. sortType(sort: s ...

Exploring Angular 17 SSR: How to Determine if Component Output Event is Subscribed

Developing a toolbar component with an action button in Angular 17 SSR. The button is a generic one, and I am attempting to determine if the component output events are being observed to determine which buttons are displayed. Is the code below valid? < ...

Creating assets from typescript plugins in Angular 6: A comprehensive guide

Situation I am currently in the process of migrating from Angular 4 and Angular Seed to Angular 6 and Angular CLI. Challenge One issue I am facing is with dynamic loading of plugins within a component using SystemJS. SystemJS.import("client/plugins/" + ...

Component in Angular with an empty variable in TypeScript

I'm encountering an issue on my web page where I have a loop calling a component multiple times. I successfully pass data to the component, but the problem arises when I try to display the value of an object in the component. In the component's H ...

Transitioning from angular 7 to the latest version 12

Upgrading from Angular 7 to 12 has presented a series of issues for me. The main problem seems to be with Angular Material. I am looking for a solution to this. ./src/app/material.module.ts:13:89-110 - Encounter Error: 'MatAutocompleteModule' ( ...

Leveraging Angular 4-5's HttpClient for precise typing in HTTP requests

Utilizing a helper service to simplify httpClient calls, I am eager to enforce strong typing on the Observable being returned. In my service where I utilize the api Service and attempt to obtain a strongly typed observable that emits: export class ApiU ...

Struggling to retrieve dataset from Bootstrap 5 form while using Angular

With the combination of Angular and Bootstrap 5, I have crafted this unique HTML form: <div class="mb-3"> <label for="genreName"> Type name</label> <div *ngIf="!enterTheGenre" class="form-group&qu ...

Angular - HighChart TreeMap does not refresh with updated data

I've been working on updating my highcharts treemap with fresh data, but for some reason, it doesn't seem to reflect the changes when I click on update. The only thing that seems to change is the title. I've tried the suggested solution on ...