window.onstorage is not working with Angular - javascript

I'm trying to create window event listener that must listen for the event in the other tab, where opened another instance of the same application.
Some service:
public validateItemAgainstServer = (item: EspApplication) => {
...
window.localStorage.setItem('item', "|")
...
});
}
Component
constructor(private winRef: WindowRef) {
winRef.nativeWindow.addEventListener('storage', function (e) {
console.log("storage event occured");
}, false);
window.addEventListener('storage', function (e) {
console.log("storage event occured");
}, false);
}
WinRef
import { Injectable } from '#angular/core';
function _window(): any {
// return the global native browser window object
return window;
}
#Injectable()
export class WindowRef {
get nativeWindow(): any {
return _window();
}
}
Regretfully, onstorage event was not fired. Is it possible to fix this solution or may be there is some better ideas of how to synchronize two tabs in Angular?

Check the live example here!
#HostListener('window:storage')
onStorageChange() {
console.log('change...');
}

Related

How to invoke typescript method from javascript

I have written a code to read an data from table(in table I have a edit button to edit the record values).
So for that I have an typescript code in ngOnInit method as below. The config is read by an table in employee.html.
export class EmployeeComponent implements OnInit {
config = null;
ngOnInit(): void {
this.config = {
events: {
click(event, context) {
alert('name: ' + context.getData('nameCol'));
this.doSomething(context.getData('nameCol'))
}
}
}
doSomething(name) { // function declaration
console.log('Param1: ' + data);
}
}
On click of the edit button, click(event, context) method is getting invoked and also getting the alert on the browser with the data that I am trying to edit. But I am not able access this.doSomething() method from click method(doSomething method is not getting invoked). Any clue how to access this.doSomething() from click method.
The issue with the scope of this which refers to config.events. You need to save the reference before hand by creating a variable at the beginning of NgOnInit. Try the following:
ngOnInit(): void {
const that = this;
this.config = {
events: {
click(event, context) {
alert('name: ' + context.getData('nameCol'));
that.doSomething(context.getData('nameCol'));
}
}
}
use arrow function instead of function expression which has own this.
click(prop) { this refers to click } if you use arrow function like
click: (prop) => { this refers to class instance }
export class EmployeeComponent implements OnInit {
config = null;
ngOnInit(): void {
this.config = {
events: {
click: (event, context) => {
alert('name: ' + context.getData('nameCol'));
this.doSomething(context.getData('nameCol'))
}
}
}
doSomething(name) { // function declaration
console.log('Param1: ' + data);
}
}

Storage event listener doesn't execute on current window

I have an angular 8+ application, and i am trying to work with localstorage and sessionstorage. Listening to sessionstorage or localstorage changes. Since its angular i am using the Event Manager class :
constructor( private elementRef: ElementRef, private eventManager: EventManager) {
this.eventManager.addGlobalEventListener('window', 'storage', () => {
console.log('Event listener.....');
});
}
When i session storage set item, the above function does not execute. it is only executed when i go to the DevTools -> Application -> Storage then remove the item. I have read somewhere that the reason it is not executing is because it cant execute on the current browser tab/current window. Is the a way to make the event listener execute on current window or browser tab When the button is clicked ? i am using Chrome browser.
I can think of solution like create a service wrapper over localStorage object and use the EventManager to track the localStorage changes on other tab
local-storage.service
#Injectable({
providedIn: "root"
})
export class LocalStorageService {
private change = new EventEmitter();
constructor(private eventManager: EventManager) {
this.eventManager.addGlobalEventListener(
"window",
"storage",
({ key, oldValue, newValue }) => {
if (key) { // this mean new item has been set
this.change.emit({
type: localStorageAction.set,
key,
oldValue,
newValue
});
} else { // if key is null this mean the localstorage is cleared
this.change.emit({
type: localStorageAction.clear
});
}
}
);
}
subscribe(handler) {
return this.change.subscribe(handler);
}
clear() {
localStorage.clear();
this.change.emit({
type: localStorageAction.clear
});
}
getItem(key: string) {
return localStorage.getItem(key);
}
key(index: number) {
return localStorage.key(index);
}
get length() {
return localStorage.length;
}
removeItem(key: string) {
localStorage.removeItem(key);
this.change.emit({
type: localStorageAction.remove,
key
});
}
setItem(key: string, value) {
const oldValue = localStorage.getItem("key");
localStorage.setItem(key, value);
this.change.emit({
type: localStorageAction.set,
key,
oldValue,
newValue: value
});
}
}
now by this service you can track changes like set,remove item,clear
demo 🚀🚀
in the demo stackblitz keep change the localStorage that why you get notify about that changes.
you need to use this service instead of the localStorage object so you can get notify of the changes in the same window.
subscribe method return a subscription object.

AngularJS $rootScope.$on alternative in context of migration to Angular2

Our AngularJS project had start it's long way to the modern Angular.
The ngMigration util recommend me to remove all the $rootScope dependecies because Angular doesn't contain a similar concept like $rootScope. It is pretty simple in some cases but I don't know what to do with event subscription mechanisms.
For example I have a some kind of Idle watchdog:
angular
.module('myModule')
//...
.run(run)
//...
function run($rootScope, $transitions, Idle) {
$transitions.onSuccess({}, function(transition) {
//...
Idle.watch(); // starts watching for idleness
});
$rootScope.$on('IdleStart', function() {
//...
});
$rootScope.$on('IdleTimeout', function() {
logout();
});
}
On which object instead of $rootScope I have to call the $on function if I want to get rid of the $rootScope?
UPD
The question was not about "how to migrate on Angular2 event system". It was about how to remove a $rootScope dependencies but keep a event system. Well it seems to be impossible.
I don't know what to do with event subscription mechanisms.
Angular 2+ frameworks replace the $scope/$rootScope event bus with observables.
From the Docs:
Transmitting data between components
Angular provides an EventEmitter class that is used when publishing values from a component. EventEmitter extends RxJS Subject, adding an emit() method so it can send arbitrary values. When you call emit(), it passes the emitted value to the next() method of any subscribed observer.
A good example of usage can be found in the EventEmitter documentation.
For more information, see
Angular Developer Guide - Observables in Angular
You can implement TimeOutService which will do the log out after x minutes (in this case 15 min) of inactivity or it will reset the timer after certain action(s).
import { Injectable, OnDestroy } from '#angular/core';
import { Router } from '#angular/router';
import { Observable, Subject, Subscription, timer } from 'rxjs';
import { startWith, switchMap } from 'rxjs/operators';
import { AuthService } from 'path/to/auth.service';
#Injectable()
export class TimeoutService implements OnDestroy {
limitMinutes = 15;
secondsLimit: number = this.limitMinutes * 60;
private reset$ = new Subject();
timer$: Observable<any>;
subscription: Subscription;
constructor(private router: Router,
private authService: AuthService,
) {
}
startTimer() {
this.timer$ = this.reset$.pipe(
startWith(0),
switchMap(() => timer(0, 1000))
);
this.subscription = this.timer$.subscribe((res) => {
if (res === this.secondsLimit) {
this.logout();
}
});
}
resetTimer() {
this.reset$.next(void 0);
}
endTimer() {
if (typeof this.subscription !== 'undefined') {
this.subscription.unsubscribe();
}
}
logout(): boolean {
this.authService.signOut().subscribe((res) => {
this.subscription.unsubscribe();
});
return false;
}
ngOnDestroy():void {
this.subscription.unsubscribe();
}
}
And in the AppComponent have listener which will reset timeout on certain actions
In case as bellow it listens for keyboard strokes, mouse wheel, or mouse click
constructor(
private timeoutService: TimeoutService
) {
}
#HostListener('document:keyup', ['$event'])
#HostListener('document:click', ['$event'])
#HostListener('document:wheel', ['$event'])
resetTimer () {
this.timeoutService.resetTimer();
}

How to trigger a Javascript event on url change

Currently, I am dealing with an angular project, I need to trigger an event on routing change/URL change from one page to another page (as it is an angular project page wont be refreshed).
I have tried with different scenarios in index.html page like below:
window.onbeforeunload = function () {
//not fit;
});
window.onhashchange = function () {
//not fit;
});
Can we have any other solutions to trigger an event on url change (I don't want to use listeners in angular).
You can do this by implementing hashchange even on window
And if you are using jQuery, this plugin might help you.
Or in your case check pure JS code
function hashHandler() {
this.oldHash = window.location.hash;
this.Check;
var that = this;
var detect = function() {
if(that.oldHash != window.location.hash) {
alert("HASH CHANGED - new has" + window.location.hash);
that.oldHash = window.location.hash;
}
};
this.Check = setInterval(function() { detect() }, 100);
}
var hashDetection = new hashHandler();
Pure demo code example for angular with router:
import { Component, OnInit } from '#angular/core'; // I import Location so that I can query the current path
import { Location } from '#angular/common'; // I also import Router so that I can subscribe to events
import { Router } from '#angular/router';
#Component(
{ selector: 'app-top-nav',
templateUrl: './top-nav.component.html',
styleUrls: ['./top-nav.component.scss'] }
)
export class TopNavComponent implements OnInit {
route: string;
constructor(location: Location, router: Router) { // within our constructor we can define our subscription
// and then decide what to do when this event is triggered.
// in this case I simply update my route string.
router.events.subscribe((val) => { if(location.path() != '') { this.route = location.path(); } else { this.route = 'Home' } });
}
ngOnInit() {
}
}
The above code should work.

Registering a connection event in ionic 2

I am new to ionic 2, I am trying to create an event and listen for changes to that event. The event would be triggered if the network connection state of my app changes. I am currently doing this in a provider called Connectivity and instantiating the provider in my root component. then in one of my pages, which i call maps which is another provider, maps uses the event created in the connectivity provider to check for connection then display a map. The problem is, the event does not seem to be triggered, and my map is not generated. here is my code
// connectivity provider
#Injectable()
export class Connectivity {
onDevice: boolean = true;
constructor( public platform: Platform, private events: Events){
this.onDevice = this.platform.is('cordova');
this.platform.ready().then(() => {
this.isOnline();
this.watchForNetworkChanges();
})
}
isOnline(): void {
if(this.onDevice && Network.type){
this.events.publish('network:connected', Network.type !== Connection.NONE);
} else {
this.events.publish('network:connected', navigator.onLine);
}
}
watchForNetworkChanges(): void {
// Watch network for a connection
Network.onConnect().subscribe(() => {
this.events.publish('network:connected', true);
});
Network.onDisconnect().subscribe(() => {
this.events.publish('network:connected', false);
});
}
}
//my maps provider
constructor(private events: Events){}
init(mapElement: any, pleaseConnect: any){
this.mapElement = mapElement;
this.pleaseConnect = pleaseConnect;
this.loadGoogleMaps();
}
loadGoogleMaps() {
this.events.subscribe('network:connected', (status) => {
let connected = status
if (!connected) {
this.disableMap();
}
else {
this.initMap().then(function () {
this.enableMap();
})
}
});
}
// home.page.ts
#ViewChild('map') mapElement: ElementRef;
#ViewChild('pleaseConnect') pleaseConnect: ElementRef;
constructor(public platform: Platform, public maps: GoogleMaps) {
this.showList = false;
this.initializeItems();
this.homePageStart();
}
homePageStart(){
this.platform.ready().then(() => {
this.maps.init(this.mapElement.nativeElement, this.pleaseConnect.nativeElement);
});
}
I believe your issue is with this here:
this.initMap().then(function () {
this.enableMap();
})
Since you are using function, the this points to the function object and not the class. Try using ()=>{} which has lexical scoping and takes the this object of the class.
this.initMap().then(()=> {
this.enableMap();
})
Documentation for Arrow Functions

Categories