11
votes

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?

1
beforeDestroy() on the component.tao
@tao afaik you cannot cancel the destroy process inside beforeDestroy by returning false or the likephil294
@phil294, no. you can't. It wouldn't make any sense. Think about it: you have a bunch of list elements (Vue components), one for each item in some data array. And you update the array. Some of your elements are gone from data. Now, do you really want your components to go: "... actually, I think I'm gonna linger in DOM for a little longer. I protest and I won't be destroyed, OK? Let me call my lawyer! Or was it layer?"tao

1 Answers

18
votes

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 = ''
    }   
  },
},