React component breaks on browser back/forward button - javascript

I have two forms, one is called profile, the other is settings. I'm using introjs to have a guided tour through these two forms. If I only move forward through the tour using the introjs 'Next Step' button there are no issues (first image). But, if I use the browser back or forward button, my forms look like the second image.
Code on profile page that utilize introjs:
runTour() {
if (this.state.showTour === true) {
const tourObj = {
userId: Meteor.userId(),
page: 'profile',
};
introJs.introJs().setOption('doneLabel', 'Next step').start().oncomplete(()
=> {
changeIntroTour.call(tourObj);
browserHistory.push('/user/settings');
})
.onexit(() => {
changeIntroTour.call(tourObj);
});
}
}
componentWillReceiveProps(nextProps) {
this.existingSettings(nextProps);
if (nextProps.intro.length > 0) {
this.setState({
showTour: nextProps.intro[0].profileTour,
}, () => {
this.runTour();
});
}
}

The issue was I was not calling introJs.exit() on componentWillUnmount. This was keeping my instance of introJs running from one component to the next which caused the bug.

Related

How can I mimic onbeforeunload in a Vue.js 2 application?

I have a Vue component that is tracking when it is "dirty" (e.g. unsaved). I would like to warn the user before they browse away from the current form if they have unsaved data. In a typical web application you could use onbeforeunload. I've attempted to use it in mounted like this:
mounted: function(){
window.onbeforeunload = function() {
return self.form_dirty ? "If you leave this page you will lose your unsaved changes." : null;
}
}
However this doesn't work when using Vue Router. It will let you navigate down as many router links as you would like. As soon as you try to close the window or navigate to a real link, it will warn you.
Is there a way to replicate onbeforeunload in a Vue application for normal links as well as router links?
Use the beforeRouteLeave in-component guard along with the beforeunload event.
The leave guard is usually used to prevent the user from accidentally
leaving the route with unsaved edits. The navigation can be canceled
by calling next(false).
In your component definition do the following:
beforeRouteLeave (to, from, next) {
// If the form is dirty and the user did not confirm leave,
// prevent losing unsaved changes by canceling navigation
if (this.confirmStayInDirtyForm()){
next(false)
} else {
// Navigate to next view
next()
}
},
created() {
window.addEventListener('beforeunload', this.beforeWindowUnload)
},
beforeDestroy() {
window.removeEventListener('beforeunload', this.beforeWindowUnload)
},
methods: {
confirmLeave() {
return window.confirm('Do you really want to leave? you have unsaved changes!')
},
confirmStayInDirtyForm() {
return this.form_dirty && !this.confirmLeave()
},
beforeWindowUnload(e) {
if (this.confirmStayInDirtyForm()) {
// Cancel the event
e.preventDefault()
// Chrome requires returnValue to be set
e.returnValue = ''
}
},
},
The simplest solution to mimic this fully is as follow:
{
methods: {
beforeWindowUnload (e) {
if (this.form_dirty) {
e.preventDefault()
e.returnValue = ''
}
}
},
beforeRouteLeave (to, from, next) {
if (this.form_dirty) {
next(false)
window.location = to.path // this is the trick
} else {
next()
}
},
created () {
window.addEventListener('beforeunload', this.beforeWindowUnload)
},
beforeDestroy () {
window.removeEventListener('beforeunload', this.beforeWindowUnload)
}
}

Framework 7 Vue how to stop Firebase from listening to changes when on different pages?

Suppose I have pageA where I listen for a firebase document changes
export default {
mounted() {
this.$f7ready(() => {
this.userChanges();
})
},
methods: {
userChanges() {
Firebase.database().ref('users/1').on('value', (resp) => {
console.log('use data has changed');
});
}
}
}
Then I go to pageB using this..$f7.views.current.router.navigate('/pageB/')
If on pageB I make changes to the /users/1 firebase route I see this ,message in the console: use data has changed, even though I'm on a different page.
Any way to avoid this behavior, maybe unload the page somehow?
I tried to stop the listener before navigating away from pageA using Firebase.off() but that seems to break a few other things.
Are you properly removing the listener for that specific database reference? You'll have to save the referenced path on a dedicated variable to do so:
let userRef
export default {
mounted() {
this.$f7ready(() => {
this.userChanges();
})
},
methods: {
userChanges() {
userRef = Firebase.database().ref('users/1')
userRef.on('value', (resp) => {
console.log('use data has changed');
});
},
detachListener() {
userRef.off('value')
}
}
}
That way you only detach the listener for that specific reference. Calling it on the parent, would remove all listeners as the documentation specifies:
You can remove a single listener by passing it as a parameter to
off(). Calling off() on the location with no arguments removes all
listeners at that location.
More on that topic here: https://firebase.google.com/docs/database/web/read-and-write#detach_listeners

Is it bad practice to use a global navigation provider in ionic3 with lazy loading?

I am using a global provider to open pages by passing the navigation controller from the page to the provider method. Is there any other way to do this better?
Main objective is not to repeat my self. Open page method I use in the globalProvider.ts is as follows. currentActive page is stored in a variable of the provider in case user logins to the app he will be redirected to the last active page (currentActive)
globalProvider.ts
openPage(navCtrl, page, params, setActive = true) {
this.showLoading();
if (setActive) {
this.currentActive = { root: page, params: params };
} else {
this.currentActive = { root: 'HomePage', params: null };
}
navCtrl.setRoot(page, params).then(res => {
if (!res) {
this.showError('Please login to view page!');
navCtrl.setRoot('LoginPage', {
activePage: this.currentActive.root
}).then(res => {
this.hideLoading();
}).catch(err => {
this.hideLoading();
});
}
this.hideLoading();
}).catch(error => {
this.hideLoading();
});
}
I don't think there is anything bad with this practice. I also do similar logic to avoid repeating the show loading and hide loading logic in every page.
However I also keep one more parameter, to help me decide if the page should be set as root or should be pushed on current stack.
This also helps me to track page views for analytics from a central place.

Cannot refresh information with navigate angular2

I have a little bit of a problem with the angular navigator 2
I'm in my detail projects page and I have a component that is in my detail page that will show me the next project.
So in my component I recall the same page which is' /projects/id
when I click in the navigation bar it changes the id well but the content does not change and I do not understand why because I make a router. navigate of my page.
my init controller projectDetail
ngOnInit() {
this.nextProjectId = this.activedRoute.snapshot.queryParams.nextProject;
const id = this.activedRoute.snapshot.params.id
this.projectsService.projectsAll().then( (projects) => {
if (projects[this.nextProjectId]) {
this.nextProject = projects[this.nextProjectId];
} else {
this.nextProject = projects[0];
}
});
this.projectsService.projectFind(id).then( project => this.project = project);
}
my function navigate in component nextProject
nextProjectDetail(id, nextProject) {
const next = parseInt(nextProject, 0) + 1
this.router.navigate(['projects/', id], { queryParams: {nextProject: next }}).then(
function(){
console.log('navigate success');
},
function(){
console.log('navigate failure');
}
);
}
I even tried to use this function in the init of detail projects to see when the url changes but it doesn't work.
this.params.subscribe(params => {
console.log("dfsd");
});
there is another way to refresh the same page in a component on the same page?
thanks

Navigating back to root - menu toggle button broken

I am using Ionic 2.
Page 1 (SearchPage) -> popover -> Page 2 (MapPage) -> Page 1 (SearchPage) (menuToggle not working)
I have a root page (SearchPage):
html
<ion-header>
<ion-navbar>
<button menuToggle>
<ion-icon name="menu"></ion-icon>
</button>
</ion-navbar>
</ion-header>
ts
presentPopover(event: Event): void {
let popover: Popover = this.popoverController.create(SearchPopOverPage, {
ev: event,
employeeModel: this.employeeModel
});
popover.present();
}
Popover
presentFilterMap(event: Event) {
this.viewCtrl.dismiss().then(() => {
this.nav.push(MapPage, {
ev: event,
employeeModel: this.employeeModel,
fromSearch: true
})
});
}
But when I try return to the root page (with a parameter), it displays the menu toggle button (3 lines), but when I click it it does not work (i.e. does nothing, where it should display the side menu).
ts (MapPage) file which returns to root:
this.nav.insert(0, SearchPage, {
employeeModel: this.employeeModel
});
If I try popToRoot(options), this works and the menu toggle button is working. However, it does not reload the page with the new parameter.
Any ideas how I should navigate back to the root page with a parameter please?
Thanks
UPDATE:
I have tried the following, but it does not go back to the root:
let options = {
employeeModel: this.employeeModel
};
this.nav.popToRoot(options);
UPDATE:
I have also tried changing the popovers call to the next page, but with little success. Now the back button on the MapPage works, but when I go to the root page, the menuToggle is still not responding to clicks.
presentFilterMap(event: Event) {
this.nav.push(MapPage, {
employeeModel: this.employeeModel,
fromSearch: true
}).then(() => {
this.viewCtrl.dismiss();
});
}
If I don't dismiss the popover,
this.nav.push(MapPage, {
employeeModel: this.employeeModel,
fromSearch: true
});
then when I use the back button on the MapPage back to root, the popover is still there, and the menuToggle works as expected. But if I rather navigate back to the root page (which I need to do) then the popover is not there and the menuToggle is not responsive.
This means the issue is to do with the popover.
import { App } from 'ionic-angular';
constuctor(public app: App) {};
pushSignupPage() {
this.viewCtrl.dismiss().then(() => {
this.app.getRootNav().push(SignupPage);
});
}
this work for me. good luck
SOLUTION: Use events to handle the parameters and pop to root
let data = {
eModel: this.eModel
};
events.publish('employee:update', data);
this.nav.popToRoot();
In your SearchPage constructor:
events.subscribe('employee:update', (data) => {
//do whatever you need to do with your data:
let EModel = data[0].eModel;
});
when you navigate back from map page to search page
use :
this.navCtrl.push(SearchPage, { employeeModel: this.employeeModel });

Categories

Resources