Reputation: 36620
I read that in Angular it is a very bad practice 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>
And I know the Angular way to show or hide an element is using the *ngIf
directive.
How can I get the * ngIf
to react on the media query in an 'Angular fashion'?
Upvotes: 26
Views: 70054
Reputation: 815
You can use angular/breakpoints-angular-cdk
follow these steps
on the terminal
npm install @angular/cdk
Then import the layout module and and add it to your NgModule’s list of imports
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]
})
right after you can use it in your component, just import these 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;
}
});
}
}
Check the docs it is a simple API
!! Attention to the new angular version, there is a better way of doing it !!
UPDATE IF YOU'RE USING A NEWER VERSION OF ANGULAR, THERE IS A CONSTANT TO HELP WITH THE SCREEN SIZE IDENTIFICATION
import { Component, OnInit } from '@angular/core';
import { BreakpointObserver, BreakpointState, Breakpoints } from '@angular/cdk/layout';
// NEW Breakpoints CONSTANT
@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 matches TabletPortrait");
}
else if (breakpoints[Breakpoints.HandsetLandscape]) {
this.showContainerInHandset = true;
console.log("screens matches HandsetLandscape");
}
});
}
}
Check the tutorial on angular website
Upvotes: 52
Reputation: 1
.container{
background-color : powderblue;
height : 50px;
width : 100%
}
@media (max-width: 400px){
.container{
display: flex;
}
}
<div class="container"></div>
Upvotes: -5
Reputation: 897
Angular flex layout is better solution for this. You wouldn't need media queries and it has special responsive feature to show and hide for example
fxShow: This markup specifies if its host element should be displayed (or not)
<div fxShow [fxShow.xs]="isVisibleOnMobile()"></div>
fxHide: This markup specifies if its host element should NOT be displayed
<div fxHide [fxHide.gt-sm]="isVisibleOnDesktop()"></div>
No need to write lot of css and it's very compatible with angular material. https://github.com/angular/flex-layout
Upvotes: 7
Reputation: 5063
I came up with the following base class and have found it works well.
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 inherit (extend) your component from this class, the host element of your component will have a class added to it with the media query alias. For example...
<app-search-bar class="some-class" _nghost-c5 ...>
...will become...
<app-search-bar class="some-class lg" _nghost-c5 ...>
Note the added media query alias 'lg' which will change according to the window size. This makes it easy to add responsive styles to each media size by wrapping the size-specific styles in your component's SCSS files. Like this...
: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;
}
}
I've put the full file on my gist https://gist.github.com/NickStrupat/b80bda11daeea06a1a67d2d9c41d4993
Upvotes: 5
Reputation: 51
Check here, it's forked solution found somewhere on internet with my customization, but it works for me (not only hiding element with display:none, but removing if from DOM - like *ngIf works)
import { Input, Directive, TemplateRef, ViewContainerRef, OnDestroy, ChangeDetectorRef } from '@angular/core'; /** * How to use this directive? * * ``` * * Div element will exist only when media query matches, and created/destroyed when the viewport size changes. * * ``` */ @Directive({ selector: '[mqIf]' }) export class MqIfDirective implements OnDestroy { private prevCondition: boolean = null; i = 0; private mql: MediaQueryList; private mqlListener: (mql: MediaQueryList) => void; // reference kept for cleaning up in ngOnDestroy() constructor(private viewContainer: ViewContainerRef, private templateRef: TemplateRef, private ref: ChangeDetectorRef) { } /** * Called whenever the media query input value changes. */ @Input() set mqIf(newMediaQuery: string) { if (!this.mql) { this.mql = window.matchMedia(newMediaQuery); /* Register for future events */ 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(); } /** * Infinitive loop when we fire detectChanges during initialization * (first run on that func) */ if (this.i > 0) { this.ref.detectChanges(); } else this.i++; } }
Upvotes: 0