Clear localStorage onbeforeunload - html

I'm working with localStorage and I want to change a counter when closing the page.
I found that onbeforeunload is the event which can be useful, so I decided to call my function in it. (onunload doesn't work for me in firefox/chrome)
But when I try to call a function , it doesn't do anything.
Is there any way to call a function when closing/refreshing a tab ?
class MyLib{
constructor(){
window.onbeforeunload = function() {
this._onClosePage();
};
}
_onClosePage() {
let openWindowsCount = this._getItem('countOpenWindow');
openWindowsCount--;
if (openWindowsCount === 0) {
this._clearUp();
}
}
_clearUp() {
this._removeItem('countPagesVisited');
}
}
UPDATE
As suggested by Oday , I fixed the binding. But now , it works randomly.
In chrome, it doesn't catch the refresh event, but sometimes it catches the exit.
In firefox, it catches the exit, and randomly catches the refresh.
class MyLib{
constructor(){
document.getElementsByTagName('body')[0].onbeforeunload = this._onClosePage.bind(this);
}
_onClosePage() {
let openWindowsCount = this._getItem('countOpenWindow');
openWindowsCount--;
if (openWindowsCount === 0) {
this._clearUp();
}
}
_onClosePage() { // call it once the page is closed or refreshed
let openWindowsCount = localStorage.getItem('countOpenWindow');
openWindowsCount--;
localStorage.setItem('countOpenWindow' , openWindowsCount);
if (openWindowsCount === 0) {
this._clearUp();
}
}
_clearUp() {
localStorage.removeItem('countOpenWindow');
}
}

In order to clean the localStorage on beforeunload, instead of using window.onbeforeunload directly, you should use the window.addEventListener() method to start listening to beforeunload event. This also allows you to remove the event listener when you find fit.
See this explanation on Mozilla Developers Documentation:
Binding to this event can be used to prevent the browser from fully
caching the page in cases where content is rendered by javascript. In
certain circumstances when returning to a page that has executed
javascript in order to populate content, you may find the javascript
not running upon the return visit when navigating back. If
window.onbeforeunload has been bound (and thus triggered when leaving
that page) javascript in the page will be triggered on the subsequent
return visit and therefore update the content.
Full text here: https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeunload
This is probably the reason for the randomness you mentioned.
As an example, see above React component that adds the event listener when it finishes mounting, and than, when it is unmounted it removes the event listener. It is a very elementary example, but I tested it on Firefox 60, Chrome 69 canary, Safari 11.1 and it worked properly.
import React, { Component } from 'react'
class App extends Component {
constructor(props) {
super(props)
this.handleLoad = this.handleLoad.bind(this)
this.handleBeforeunload = this.handleBeforeunload.bind(this)
}
componentDidMount() {
this.handleLoad()
window.addEventListener('beforeunload', this.handleBeforeunload)
}
componentWillUnmount() {
window.removeEventListener('beforeunload', this.handleBeforeunload)
}
render() {
return (
<div className="App">
<p>
Hello World!
</p>
</div>
)
}
handleLoad() {
let countOpenWindow = parseInt(localStorage.getItem('countOpenWindow'), 10)
if (!countOpenWindow) {
countOpenWindow = 0
}
localStorage.setItem('countOpenWindow', ++countOpenWindow)
}
handleBeforeunload() {
let countOpenWindow = parseInt(localStorage.getItem('countOpenWindow'), 10)
if (countOpenWindow > 1) {
localStorage.setItem('countOpenWindow', --countOpenWindow)
} else {
localStorage.removeItem('countOpenWindow')
}
}
}
export default App

You need to capture the context of your class inside the onbeforeunload event handler. Currently, the 'this' refers to the window which fires the event.
constructor(){
let that = this;
window.onbeforeunload = function() {
that._onClosePage();
};
}

Related

checkbox should remains checked after refresh the page refresh

So here I'm using checkbox to hide and show templates. I just want code that will make checkbox remains checked after page refresh in angular 9 .Please help me.
You can do 2 things:
Use RxJS and as soon as the checkbox is checked, you can use Behavior Subject to store the data and on reload or refresh event, read the behavior subject and patch the value
Use sessionStorage, and do the same. As soon as its checked, store it in sessionStorage, on reload, try to read from sessionStorage and patch the value again.
Implementation:
// Using session storage
someForm.controls.checkboxFormControl.valueChanges.subscribe(
data => {
if (data) {
sessionStorage.setItem('checkboxChecked', true)
}
})
onRefreshEvent() {
let sessionStorageValue = sessionStorage.setItem('checkboxChecked');
if (sessionStorageValue && (sessionStorageValue === true)) {
this.someForm.contols.checkBoxControl.patchValue(true)
}
}
//// Same with Behavior Subject as well
// Create a Behavior Subject in your Service file
checkboxCheckedSource: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
checkBoxObservable: Observable<boolean> = this.checkboxCheckedSource.asObservable();
setCheckboxValue(checked: boolean) {
this.checkboxCheckedSource.next(checked);
}
getCheckboxValue(): Observable<boolean> {
return checkBoxObservable;
}
// In your component.ts file
someForm.controls.checkboxFormControl.valueChanges.subscribe(
data => {
if (data) {
this.yourService.setCheckboxValue(true)
}
})
onRefreshEvent() {
this.yourService.getCheckboxValue().subscribe(checked => {
if (checked) {
this.someForm.contols.checkBoxControl.patchValue(true);
}
})
}
If its only one checkbox, i would prefer the sessionStorage way of doing it, else if you are storing a lot of checkboxes use BehaviorSubject!!

How to update the html page view after a timeout in Angular

I am trying to display a routerlink name based on a condition. I want to display the div section routerLink name if condition is true.If i check {{isNameAvailable}}, first it displays false and after this.names got the values it shows true.Since in the component getDetails() method is asynchronous this.names getting the values after html template render.Therefore this routerLink does n't display.Therefore I want to display div section after some time. (That 's the solution i have) Don't know whether is there any other solution.
This is my html file code.
<main class="l-page-layout ps-l-page-layput custom-scroll bg-white">
{{isNameAvailable}}
<div class="ps-page-title-head" >
<a *ngIf ="isNameAvailable === true" [routerLink]="['/overview']">{{Name}}
</a>
{{Name}}
</div>
</main>
This is my component.ts file
names= [];
isNameAvailable = false;
ngOnInit() {
this.getDetails()
}
getDetails() {
this.route.params.subscribe(params => {
this.names.push(params.Names);
console.log(this.names);
this.getValues().then(() => {
this.isNameAvailable = this.checkNamesAvailability(this.names);
console.log(this.isNameAvailable);
});
});
}
resolveAfterSeconds(x) {
return new Promise(resolve => {
setTimeout(() => {
resolve(x);
}, 900);
});
}
checkNamesAvailability(names) {
console.log(names);
return names.includes('Sandy');
}
async getValues() {
await this.resolveAfterSeconds(900);
}
And console.log(this.isLevelAvailable); also true. What I can do for this?
1.You do not have anything to show in the HTML only the isNameAvailable, because you do not have any assignment in the Name variable.
2.It is better to use the angular build-in async pipe,
when you want to show the returned value from observables.
3.When you are using the *ngIf directive you can skip *ngIf ="isNameAvailable === true" check because the variable is boolean type, you gust write *ngIf ="isNameAvailable", it will check also for null but NOT for undefined
It is working because the *ngIf directive is responsible for checking and rendering the UI, you can see how many times the directive is checking by calling an function and print and answer in the console.
By any chance do you have changeDetection: ChangeDetectionStrategy.OnPush docs set in component annotation? That might explain this behaviour. With it Angular run change detection only on component #Input()'s changes and since in your case there were non it did not run change detection which is why template was not updated. You could comment that line to check if that was cause of the issue. You are always able to run change detection manually via ChangeDetectorRef.detectChange() docs which should solve you problem
constructor(private cd: ChangeDetectorRef) {}
...
getDetails() {
this.route.params.subscribe(params => {
...
this.getValues().then(() => {
this.isNameAvailable = this.checkNamesAvailability(this.names);
this.cd.detectChanges(); // solution
console.log(this.isNameAvailable);
});
});
}
This stackblitz show this bug and solution. You can read more about change detection here
You could use RxJS timer function with switchMap operator instead of a Promise to trigger something after a specific time.
Try the following
import { Subject, timer } from 'rxjs';
import { takeUntil, switchMap } from 'rxjs/operators';
names= [];
isNameAvailable = false;
closed$ = new Subject();
ngOnInit() {
this.getDetails()
}
getDetails() {
this.route.params.pipe(
switchMap((params: any) => {
this.names.push(params.Names);
return timer(900); // <-- emit once after 900ms and complete
}),
takeUntil(this.closed$) // <-- close subscription when `closed$` emits
).subscribe({
next: _ => {
this.isNameAvailable = this.checkNamesAvailability(this.names);
console.log(this.isNameAvailable);
}
});
}
checkNamesAvailability(names) {
console.log(names);
return names.includes('Sandy');
}
ngOnDestroy() {
this.closed$.next(); // <-- close open subscriptions when component is closed
}

How to refresh tabs in ionic 4

I have a master - detail tab in ionic 4, the master tab gets the list of items that I've created in detail page, but when i create an item and i returned to the tab page, it doesn't refresh automatically.
I've tried using ionDidEnter, ionWillEnter, but nothing works.
This is my code in detail page:
async saveActividad() {
await loading.present().then(async () => {
await this.db.insertItem(item);
this.router.navigateByUrl('/tabs/tab1');
});
}
And this is my code in master page ("tab1")
ionViewWillEnter() {
this.db.getItems()
.then(
(data) => {
if (data !== '') {
this.list = data;
}
}
);
}
You have an TyPo in the event method call.
Instead of:
ionWillEnter() {
// todo
}
Try:
ionViewWillEnter() {
// todo
}
Ionic Page Events explanation
Name - Description
ionViewWillEnter - Fired when the component routing to is about to animate into view.
ionViewDidEnter - Fired when the component routing to has finished animating.
ionViewWillLeave - Fired when the component routing from is about to animate.
ionViewDidLeave - Fired when the component routing to has finished animating.
2) Or if only your tabs are just non-component based html, you could play with the *ngIf directive and invoke a method before returning true:
E.g.:
<section class="master-detail" *ngIf='showIfAvailable();'></section>
async showIfAvailable() {
this.list = await this.db.getItems();
if (list) {
return true;
}
}
Refresh data when function is invoked!
-Best Regards.

Changing currentTime using React Component State

Heres a link to the sandbox. I'm looking for the best way to manipulate a <video> element's currentTime. In my sandbox I've been able to do that by having a button call a function seek() which changes the state of video, a reference to document.getElementById('video-player'), see below.
export default class VideoPlayer extends React.Component {
constructor(props) {
super(props);
this.state = {
source: props.source,
timestamp: props.timestamp
};
}
componentDidMount() {
this.setState({ video: document.getElementById('video-player') })
}
componentDidUpdate(prevProps) {
if (this.props !== prevProps) {
this.setState(
{ timestamp: this.props.timestamp },
() => { this.seek() }
);
}
}
seek = () => {
console.log(this.state.timestamp)
//works
this.state.video.currentTime = 1;
//doesn't work
//this.state.video.currentTime = this.state.timestamp;
}
render() {
return (
<div>
<video
id='video-player'
height='200px'
src={this.state.source}
type='video/mp4'
controls>
</video>
{/* I don't want this button. I want skip from the outside */}
<button onClick={this.seek}>Skip Inside Component</button>
</div>
);
}
}
The thing I can't figure out is that I can only manage to change currentTime inside the component because for whatever reason, I can't assign this.state.timestamp to the currentTime of my <video> reference (this.state.video.currentTime).
Ultimately the component should receive props.timestamp, provided by Redux, call seek() inside <VideoPlayer /> whenever Redux's state changes, and set the video's currentTime to this.state.timestamp.
Let me know if you guys need any more details. Thanks in advance!
EDIT: The above has been answered but I'm still having an issue. The sandbox I shared and just fixed still won't work in my rails project though. I had a feeling it wouldn't. When one of the three buttons in App.js is clicked, the state changes in Redux and is successfully received by <VideoPlayer>. This is confirmed by my console.log inside of seek() which returns the corresponding button's value to the console. However, the video's currentTime still doesn't change. Any ideas here?
In mapStateToProps your state is the timestamp, and not state.timestampReducer.
This should work:
const mapStateToProps = (state) => {
return {
timestamp: state
}
};
react state typically is used for managing your UI state, like: pause, playing, currentTime etc, therefore you should avoid assigning the dom element to the statethis.setState({ video: document.getElementById('video-player') })
if you want to change the state of react, never use this.state=something like your seek function does.
use setState function instead.
Use refs, not state, to access your video.
With Classes:
constructor(props) {
super(props);
this.videoRef = React.createRef();
}
...
this.videoRef.current.currentTime = this.state.timestamp;
With Hooks:
const videoRef = useRef(null);
...
videoRef.current.currentTime = timestamp;
And implement with:

Boolean value doesn't change

I have a problem. After searching for hours I cannot find an explanation for this. I want to display a modal (from primeNG) and show it when the user clicks a button. This button calls (with an id) to my API REST and brings information, very simple. I receive the information, but when the modal should show, this doesn't happen.
map.component.ts
export class MapComponent implements OnInit {
public alteraciones: any[];
public alteracion: any = {};
display: boolean = false;
/*...*/
generateData(map: L.map) {
const data: any[] = [];
let marker: any;
L.geoJson(this.alteraciones, {
pointToLayer: (feature, latlng) => {
marker = L.marker(latlng, {
icon: this.getIconMarker(feature.properties.tipo_alteracion)
});
marker.on('click', (e) => {
this.getInfoAlteracion(feature.properties.id_alteracion); // <==
});
data.push(marker);
}
});
/*...*/
}
/**...**/
getInfoAlteracion(id_alteracion: string) {
this.mapService.getInfoAlteracion(id_alteracion).subscribe(
result => {
this.alteracion = result;
console.log(this.alteracion); // < == Information OK
this.display = true; // <== this variable should change but doesn't
},
err => console.log(err)
);
}
}
map.component.html
<p-dialog header="Info" [(visible)]="display" modal="modal" width="500" [responsive]="true">
<!--some code-->
<p-footer>
<button type="button" pButton icon="fa-close" (click)="display=false" label="Cerrar"></button>
</p-footer>
</p-dialog>
However, when I recompile or when I turn off the server, the value of the display variable changes, and it shows me the modal. I cannot find an explanation, any idea?
EDIT
Posible conflicts:
#asymmetrik/ngx-leaflet: 3.0.2
#asymmetrik/ngx-leaflet-markercluster: 1.0.0
EDIT 2
I also added a new marker with a new variable to change but doesn't work too. At this point, I think (and I'm 90% sure) that it's a problem of communication between component.ts and component.html.
Try to make that boolean display property public !
Finally, I solved the problem.Thanks to this link, I realized that it was a problem of compatibility between libraries. Leaflet event handlers run outside of Angular's zone, where changes to input bound fields will not be detected automatically. To ensure my changes are detected and applied, I need to make those changed inside of Angular's zone. Adding this to the code, finally, all works:
constructor(private mapService: MapService, private zone: NgZone) { }
marker.on('click', (e) => {
this.zone.run(() => {
this.getInfoAlteracion(feature.properties.id_alteracion);
});
});
data.push(marker);
}
Thanks to all for the help!