beforeRouteLeave не работает мгновенно при использовании с модальной и эмиссионной функцией

#vue.js #vue-router

Вопрос:

У меня есть приложение Vue со многими дочерними компонентами. В моем случае у меня есть некоторые родительско-дочерние компоненты, подобные этому. Проблема в том, что в некоторых дочерних компонентах у меня есть раздел для редактирования информации. В случае, если пользователь ввел некоторую информацию и перешел на другую страницу, но не сохранил ее, будет отображен режим, предупреждающий пользователя. Я следовал инструкциям на beforeRouteLeave, и это сработало хорошо, но у меня возникла проблема. Когда я нажму кнопку » Да » в модальном режиме, я выдам функцию @yes='confirm' родительскому компоненту. В функции подтверждения я установлю this.isConfirm = true . Затем я проверяю эту переменную внутри перед выходом, чтобы подтвердить навигацию. Но на самом деле, когда я нажимаю кнопку » Да » в модальном режиме, экран не перенаправляется сразу. Мне нужно нажать еще раз, чтобы перенаправить. Помогите мне с этим делом введите описание изображения здесь

Ответ №1:

Вы можете создать базовый компонент, подобный следующему, а затем унаследовать (расширить) от него все компоненты уровня страницы/маршрута, в которых вы хотите реализовать функциональность (предупреждение о несохраненных данных).:

 <template>
  <div />
</template>
 
 <script>
import events, { PAGE_LEAVE } from '@/events';

export default
{
  name: 'BasePageLeave',
  beforeRouteLeave(to, from, next)
  {
    events.$emit(PAGE_LEAVE, to, from, next);
  }
};
</script> 
 

events.js это просто глобальный автобус событий.

Затем в своем компоненте на уровне страницы вы сделаете что-то вроде этого:

 <template>
  <div>
    .... your template ....
    <!-- Unsaved changes -->
    <ConfirmPageLeave :value="modified" /> 
  </div>
</template>
 
 <script>
import BasePage from 'src/components/BasePageLeave'; 
import ConfirmPageLeave from 'src/components/dialogs/ConfirmPageLeave'; 

export default
{
  name: 'MyRouteName',
  components:
  {
    ConfirmPageLeave,
  },
  extends: BasePage, 
  data()
  {
    return {
      modified: false,
      myData:
      {
        ... the data that you want to track and show a warning
      }
    };
  }.
  watch:
  {
    myData:
    {
      deep: true,
      handler()
      {
        this.modified = true;
      }
    }
  },
 

ConfirmPageLeave Компонент представляет собой модальное диалоговое окно, которое будет отображаться, когда данные будут изменены И пользователь попытается уйти:

 <template>
  <v-dialog v-model="showUnsavedWarning" persistent>
    <v-card flat>
      <v-card-title class="pa-2">
        <v-spacer />
        <v-btn icon @click="showUnsavedWarning = false">
          <v-icon>mdi-close</v-icon>
        </v-btn>
      </v-card-title>
      <v-card-text class="pt-2 pb-3 text-h6">
        <div class="text-h4 pb-4">{{ $t('confirm_page_leave') }}</div>
        <div>{{ $t('unsaved_changes') }}</div>
      </v-card-text>
      <v-card-actions class="justify-center px-3 pb-3">
        <v-btn class="mr-4 px-4" outlined large tile @click="showUnsavedWarning = false">{{ $t('go_back') }}</v-btn>
        <v-btn class="ml-4 px-4" large tile depressed color="error" @click="ignoreUnsaved">{{ $t('ignore_changes') }}</v-btn>
      </v-card-actions>
    </v-card>
  </v-dialog>
</template>
 
 <script>
import events, { PAGE_LEAVE } from '@/events';

export default
{
  name: 'ConfirmPageLeave',
  props:
    {
      value:
        {
          // whether the monitored data has been modified
          type: Boolean,
          default: false
        }
    },
  data()
  {
    return {
      showUnsavedWarning: false,
      nextRoute: null,
    };
  },
  watch:
    {
      showUnsavedWarning(newVal)
      {
        if (!newVal)
        {
          this.nextRoute = null;
        }
      },
    },
  created()
  {
    events.$on(PAGE_LEAVE, this.discard);
    window.addEventListener('beforeunload', this.pageLeave);
  },
  beforeDestroy()
  {
    events.$off(PAGE_LEAVE, this.discard);
    window.removeEventListener('beforeunload', this.pageLeave);
  },
  methods:
    {
      discard(to, from, next)
      {
        if (this.value)
        {
          this.nextRoute = next;
          this.showUnsavedWarning = true;
        }
        else next();
      },
      pageLeave(e)
      {
        if (this.value)
        {
          const confirmationMsg = this.$t('leave_page');
          (e || window.event).returnValue = confirmationMsg;
          return confirmationMsg;
        }
      },
      ignoreUnsaved()
      {
        this.showUnsavedWarning = false;
        if (this.nextRoute) this.nextRoute();
      },
    }
};
</script>

<i18n>
  {
    "en": {
      "confirm_page_leave": "Unsaved changes",
      "unsaved_changes": "If you leave this page, any unsaved changes will be lost.",
      "ignore_changes": "Leave page",
      "go_back": "Cancel",
      "leave_page": "You're leaving the page but there are unsaved changes.nPress OK to ignore changes and leave the page or CANCEL to stay on the page."
    }
  }
</i18n>