Using proper CSS media queries in Angular - css

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.
Question
How can I get the * ngIf to react on the media query in an 'Angular fashion'?

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

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

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

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++;
}
}
See here

.container{
background-color : powderblue;
height : 50px;
width : 100%
}
#media (max-width: 400px){
.container{
display: flex;
}
}
<div class="container"></div>

Related

Property is not populating

I am learning Angular 11. I am following this code. My code is like below.
carousel.interface.ts
export interface Slide {
headline?: string;
src: string;
}
carousel.component.ts
import { Component, Input } from '#angular/core';
import { Slide } from './carousel.interface';
#Component({
selector : 'carousel',
templateUrl : './carousel.component.html',
styleUrls : ['./carousel.component.scss'],
})
export class CarouselComponent {
#Input() slides: Slide[];
currentSlide = 0;
constructor() { }
}
Are { Slide } and Slide[] same ? If same then why slides is not populating ?
I read this SO post. But I think issue of that post is different.

How can I toggle a class in a LitElement Web Component

I am working with precompiled stylesheet (from SASS) and only need to toggle classes.
I have two elements that will be writing to an event. Based on the event being true/false I want to to toggle a class on my component.
Would this work:
import { LitElement, html } from 'lit-element'
/**
*
*
* #export
* #class MenuMainButton
* #extends {LitElement}
*/
export class MenuMainButton extends LitElement {
static get properties() {
return {
name: { type: String },
toggled: { type: String }
}
}
constructor() {
super()
this.name = 'Menu'
this.toggled = ''
this.addEventListener('toggle-main-menu', this.handleEvents)
}
render() {
return html`
<a #click=${this._onClick} class="menu-button wk-app-menu-button app-menu-open ${this.toggled} govuk-body"
>${this.name}</a
>
`
}
handleEvents(event) {
this.toggled = event.toggle ? 'hide-item' : ''
}
_onClick() {
const toggleMainMenu = new CustomEvent('toggle-main-menu', {
toggle: this.toggled === '' ? 1 : 0
})
this.dispatchEvent(toggleMainMenu)
}
}
window.customElements.define('main-menu-button', MenuMainButton)
One way to make styles dynamic is to add bindings to the class or style attributes in your template.
The lit-html library offers two directives, classMap and styleMap, to conveniently apply classes and styles in HTML templates.
Styles - LitElement

How to optimally apply resize on large data in ACE editor?

I am using ace-editor in my angular app as a JSON editor, ace editor has a feature to dynamically resize based on the data.
When I have data within 1.5k lines it works seamlessly post that the chrome browser get hang, noticed that the CPU utilization is also high
Can any help me to identify how can I resolve this issue or any work around?
If it is an styling issue can anyone suggest how to fit the editor?
Resize logic:
ngAfterViewChecked() {
this.codeEditor.setOptions({
maxLines: this.codeEditor.getSession().getScreenLength(),
autoScrollEditorIntoView: true
});
this.codeEditor.resize();
}
editor.ts:
import {
Component, ViewChild, ElementRef, Input, Output, EventEmitter,
OnChanges, SimpleChanges
} from '#angular/core';
import * as ace from 'ace-builds';
import 'ace-builds/src-noconflict/mode-json';
import 'ace-builds/src-noconflict/theme-github';
const THEME = 'ace/theme/github';
const LANG = 'ace/mode/json';
export interface EditorChangeEventArgs {
newValue: any;
}
#Component({
selector: 'app-editor',
templateUrl: './editor.component.html',
styleUrls: ['./editor.component.css']
})
export class EditorComponent implements OnChanges {
#ViewChild('codeEditor') codeEditorElmRef: ElementRef;
private codeEditor: ace.Ace.Editor;
#Input() jsonObject;
#Input() readMode;
#Output() change = new EventEmitter();
data: any;
mode: any;
constructor() { }
ngOnChanges(changes: SimpleChanges) {
for (const properties of Object.keys(changes)) {
if (properties == 'jsonObject') {
const currentJSONObject = changes[properties];
if (currentJSONObject.currentValue && currentJSONObject.firstChange == false)
this.codeEditor.setValue(JSON.stringify(currentJSONObject.currentValue, null, '\t'), -1);
else if (currentJSONObject.currentValue == null && currentJSONObject.firstChange == false)
this.codeEditor.setValue("");
else
this.data = currentJSONObject.currentValue
}
if (properties == 'readMode') {
const currentReadMode = changes[properties];
if (currentReadMode.firstChange == false)
this.codeEditor.setReadOnly(currentReadMode.currentValue);
else
this.mode = currentReadMode.currentValue
}
}
}
ngAfterViewInit() {
const element = this.codeEditorElmRef.nativeElement;
const editorOptions: Partial<ace.Ace.EditorOptions> = {
highlightActiveLine: true,
displayIndentGuides: true,
highlightSelectedWord: true,
};
this.codeEditor = ace.edit(element, editorOptions);
this.codeEditor.setTheme(THEME);
this.codeEditor.getSession().setMode(LANG);
this.codeEditor.setShowFoldWidgets(true);
this.codeEditor.setHighlightActiveLine(true);
this.codeEditor.setShowPrintMargin(false);
if (this.data)
this.codeEditor.setValue(JSON.stringify(this.data, null, '\t'), -1);
this.codeEditor.setReadOnly(this.readMode);
if (this.mode)
this.codeEditor.setReadOnly(this.mode);
}
ngAfterViewChecked() {
this.codeEditor.setOptions({
maxLines: this.codeEditor.getSession().getScreenLength(),
autoScrollEditorIntoView: true
});
this.codeEditor.resize();
}
onChange(updatedJSON) {
this.change.emit({ newValue: updatedJSON });
}
}
editor.html:
<div ace-editor #codeEditor [autoUpdateContent]="true" [durationBeforeCallback]="1000" (textChanged)="onChange($event)"
(change)="onChange(codeEditor.value)" class="editor">
</div>
Editor CSS property:
.editor {
min-height: 750px;
width: 100%;
}
Using editor component across the application by simply adding following tag in HTML body
<app-editor [jsonObject]="data" [readMode]="readMode" (change)="onChange($event)"></app-editor>

Apply styling to angular toast from within a component method

I need to apply a styling to a toast that will be used when a particular function is called. How do I do that?
toast.service.ts
import { Injectable, OnInit } from '#angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
#Injectable()
export class ToastrService {
public toasterStatus: BehaviorSubject<Message>;
constructor() {
this.toasterStatus = new BehaviorSubject<Message>(null);
}
showToaster(type: string, content: string) {
const toasterObj: Message = { severity: type, detail: content };
this.toasterStatus.next(toasterObj);
}
}
export interface Message {
severity: String;
detail: String;
}
my-component.ts
import { ToastrService } from '../../../toast.service';
#Component({
selector: '...',
templateUrl: './my.component.html',
styleUrls: ['./my.component.css']
})
export class MyComponent implements OnInit, OnDestroy {
constructor(private logService: LogService,
private toastrService: ToastrService,){...}
ngOnInit() {
this.idSubscription$ = this.route.params.subscribe(params => {
this.id = +params['id'];
});
this.logService.debug(this.id);
// check for error
if (my.error) {
this.logService.error('Error retrieving report templates.', reportTemplatesState);
this.toastrService.showToaster('danger', 'Error retrieving report templates.');
}
});
//I need to apply the styling below to this toastrService element within the component method
my.component.css
.toastSticky{
position: -webkit-sticky; /* Safari */
position: sticky;
top: 0;
}
You do this with the documentation :
MatSnackBarConfig
Configuration used when opening a snack-bar.
panelClass: string | string[] Extra CSS classes to be
added to the snack bar container.

How to check whether user has internet connection or not in Angular2?

How I would check internet connection in Angular2 at the time of API hitting, whenever in my app API is hit to server sometimes user is
offline (i mean without internet connection) so how would i check the internet connectivity ? is there some special status code for internet connectivity ?
or something else ?
PS:- i found navigator.onLine in angularJs but seems not working in angular2.
Source - How to check internet connection in AngularJs
update
as sudheer suggested in answer below navigator.onLine in working with angular2 but still not working properly why ?
working example here
(2018) Code updated for rxjs6
It totally works with angular2. Obviously it's different from angularJS because neither $scope nor $apply exist anymore. RxJS makes this easy, though! Tested on Chrome 53:
template:
<p>{{online$ | async}}</p>
component:
import { Observable, fromEvent, merge, of } from 'rxjs';
import { mapTo } from 'rxjs/operators';
#Component({ /* ... */ })
export class MyComponent {
online$: Observable<boolean>;
constructor() {
this.online$ = merge(
of(navigator.onLine),
fromEvent(window, 'online').pipe(mapTo(true)),
fromEvent(window, 'offline').pipe(mapTo(false))
);
}
}
Think about what 'offline' means for your use case!
An unplugged ethernet cable and a 3KB/s EDGE connection likely have the same implications for your app although the latter means you're not technically offline!
From a programmer's point-of-view being connected wirelessly with a very poor signal is actually a lot worse than being truely disconnected because it's a lot harder to detect!
The above code returning a false value means your absolutely offline as in disconnected. It returning true doesn't necessarily indicate that there's a practically usable connection.
At first, j2L4e's answer didn't work for me (testing in Chrome). I tweaked slightly by surrounding my bool in brackets in the ngIf and this ended up working.
<md-icon class="connected" mdTooltip="No Connection" *ngIf="!(isConnected | async)">signal_wifi_off</md-icon>
import { Component, OnInit } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import { Subscription } from 'rxjs/Subscription';
import 'rxjs/Rx';
#Component({
selector: 'toolbar',
templateUrl: './toolbar.component.html',
styleUrls: ['./toolbar.component.css']
})
export class ToolbarComponent implements OnInit {
isConnected: Observable<boolean>;
constructor() {
this.isConnected = Observable.merge(
Observable.of(navigator.onLine),
Observable.fromEvent(window, 'online').map(() => true),
Observable.fromEvent(window, 'offline').map(() => false));
}
ngOnInit() {
}
}
As i have checked navigator is global object like window. You can use in in angular2 and it worked fine for me.
import {Component} from 'angular2/core';
#Component({
selector: 'my-app',
template:`
navigator.onLine
{{onlineFlag}}
`
})
export class AppComponent {
public onlineFlag =navigator.onLine;
}
Using Angular 6+ and Rxjs 6+, you can do it in the following way:
import { Observable, fromEvent, merge, of } from 'rxjs';
import { mapTo } from 'rxjs/operators';
online$: Observable<boolean>;
constructor() {
this.online$ = merge(
of(navigator.onLine),
fromEvent(window, 'online').pipe(mapTo(true)),
fromEvent(window, 'offline').pipe(mapTo(false))
)
}
Here is a demo (toggle network in dev tools)
Safe Approach to listen to network states
Answers given above works well but are not considered safe approach.
1.Browser dependent objects like window should not be referenced directly, always check for platform.
2.Furthermore functionality like Network Connection must be encapsulated into a service.
Below is the ConnectionService which can be subscribed to listen network states. It follows the rxjs 6 style.
Complete Code
import { Injectable, Inject, PLATFORM_ID } from '#angular/core';
import { Observable, fromEvent, merge, empty } from 'rxjs';
import { isPlatformBrowser } from '#angular/common';
import { mapTo } from 'rxjs/operators';
#Injectable({
providedIn: 'root'
})
export class ConnectionService {
private connectionMonitor: Observable<boolean>;
constructor(#Inject(PLATFORM_ID) platform) {
if (isPlatformBrowser(platform)) {
const offline$ = fromEvent(window, 'offline').pipe(mapTo(false));
const online$ = fromEvent(window, 'online').pipe(mapTo(true));
this.connectionMonitor = merge(
offline$, online$
);
} else {
this.connectionMonitor = empty();
}
}
monitor(): Observable<boolean> {
return this.connectionMonitor;
}
}
in component, you may listen by subscribing to monitor() or directly into HTML using async pipe.
For Angular 9 - a very simple solution and comfortable using (thanks to this and this solutions):
1) Create new component:
ng g c NoConnection
no-connection.component.ts
import { Component, OnInit } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser'
import { HttpClient } from '#angular/common/http';
#Component({
selector: 'app-no-connection',
templateUrl: './no-connection.component.html',
styleUrls: ['./no-connection.component.css']
})
export class NoConnectionComponent implements OnInit {
isConnectionAvailable: boolean = navigator.onLine;
constructor(private httpClient: HttpClient) {
window.addEventListener('online', () => {
this.isConnectionAvailable = true
});
window.addEventListener('offline', () => {
this.isConnectionAvailable = false
});
}
ngOnInit(): void {
}
}
no-connection.component.html (customise page as you want)
<div>
<p style.color = "{{ isConnectionAvailable ? 'green' : 'red'}}"> {{ isConnectionAvailable ? 'Online' : 'Offline'}} </p>
<!-- https://stackoverflow.com/questions/13350663/greyed-out-waiting-page-in-javascript#answer-13350908 -->
<div id="blackout" class="noselect" style.display = "{{isConnectionAvailable ? 'none' : 'block'}}">
<br><br><br><br><br>
<p>No Internet connection!</p>
<br>
</div>
</div>
no-connection.component.css
#blackout {
width:100%;
height:100%; /* make sure you have set parents to a height of 100% too*/
position: absolute;
left:0; top:0;
z-index:10; /*just to make sure its on top*/
opacity: 0.5;
background-color:#333;
text-align: center;
font-size:25px;
color: white;
}
.noselect {
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Safari */
-khtml-user-select: none; /* Konqueror HTML */
-moz-user-select: none; /* Old versions of Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently
supported by Chrome, Opera and Firefox */
}
2) Use it anywhere you want - in my case the best place - is a root component:
app.component.html
<div>
<app-no-connection></app-no-connection>
<app-main></app-main>
</div>
Go with this simple Hack.
Working in angular 5 or later
constructor(){
setInterval(()=>{
if(navigator.onLine){
//here if it is online
}else{
//here if it is offline
}
}, 100)
}
write this in constructor of app.component.ts or your app bootstrap
No need of any external library ..
import { Injectable } from '#angular/core';
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor
} from '#angular/common/http';
import { Observable } from 'rxjs/Observable';
#Injectable()
export class InternetInterceptor implements HttpInterceptor {
constructor() { }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// check to see if there's internet
if (!window.navigator.onLine) {
// if there is no internet, throw a HttpErrorResponse error
// since an error is thrown, the function will terminate here
return Observable.throw(new HttpErrorResponse({ error: 'Internet is required.' }));
} else {
// else return the normal request
return next.handle(request);
}
}
}
Use this.
Without any external library.
public isOnline: boolean = navigator.onLine;
ngOnInit() {
console.log(this.isOnline);
}

Resources