如何在使用Vue的表单提交时禁用window.onbeforeunload?

时间:2018-03-31 15:56:20

标签: javascript laravel vue.js vuejs2

我有一个名为countdowntimer.vue的组件,它显然只是一个倒数计时器,我用它来进行在线检查页面,我想在窗口对象上应用onbeforeunload事件,但我也希望计时器能够在没有被窗口事件中断的情况下自动提交,我尝试将代码放在vuejs组件中,但它没有响应,它或者不允许我在不中断的情况下提交,或者它根本不起作用,让任何事件都能在不中断的情况下离开页面。

这里是倒数计时器的代码:

<template>
    <div>
        <div v-if="finished" v-text="expiredText"></div>

        <div v-else>
            <span>{{ remaining.minutes }} Minutes, </span>
            <span>{{ remaining.seconds }} Seconds</span>
            left...
        </div>
    </div>
</template>

<script>
    import moment from 'moment';
    export default {
        props: {
            until: { default: 600000},
            expiredText: { default: 'TIME IS UP' }
        },
        data () {
            return { limiter: this.until * 10000};
        },
        created () {
            this.refreshEverySecond();
            document.addEventListener('beforeunload', this.redirect());
        },
        computed: {
            finished () {
                return this.remaining.total <= 0;
            },
            remaining () {
                let remaining = moment.duration(this.limiter);
                if (remaining <= 0) this.$emit('finished');
                return {
                    total: remaining,
                    minutes: remaining.minutes(),
                    seconds: remaining.seconds()
                };
            },
        },
        methods: {
            refreshEverySecond () {
                let interval = setInterval(() => this.limiter = this.limiter - 1000, 1000);
                this.$on('finished', () => clearInterval(interval));
                this.$on('finished', () => this.timeUp());
            },

            timeUp() {
                const form = document.querySelector('[data-form-submit]');
                const radios = document.querySelectorAll('input[type=radio]');
                radios.forEach(radio => radio.style.display = 'none');
                form.submit(function(e) {
                    console.log(e);
                });
            },
            redirect () {
                // if(this.$on('finished')) {
                    // console.log(this.finished)
                    // window.onbeforeunload = function() {
                    //     return "Do you really want to leave our brilliant application?";
                    // };
                // }
                // console.log(this.finished())
                // return;
            }
        },
    }
</script>

我已尝试将该方法设置为计算属性,并将其设置为具有不同if语句的观察者,但它只是不像我上面提到的那样工作。

这是我在

中使用的刀片模板
@extends('layouts.app')

@section('content')
    <div class="container">
        <div class="row">
            <div class="col-md-6 col-md-offset-3">
                <form method="POST" onsubmit="clicked()" data-form-submit>
                    {{ csrf_field() }}
                    <div class="panel panel-danger">
                        <div class="panel-heading">
                            {{ $quiz->quiz_name }}
                        </div>
                        <div class="panel-body">
                            <ol>
                                @foreach($quiz->questions as $index => $question)
                                    <li><h4>{{ $question->title }} ? </h4></li>
                                    <ul>
                                        <div class="flex-align-baseline">
                                            <li>{{$question->option_1}}</li>
                                            <input type="radio" name="{{$index}}" value="{{ $question->option_1 }}">
                                            <hr>
                                        </div>
                                        <div class="flex-align-baseline">
                                            <li>{{$question->option_2}}</li>
                                            <input type="radio" name="{{$index}}" value="{{ $question->option_2 }}">
                                            <hr>
                                        </div>
                                        <div class="flex-align-baseline">
                                            <li>{{$question->option_3}}</li>
                                            <input type="radio" name="{{$index}}" value="{{ $question->option_3 }}">
                                            <hr>
                                        </div>
                                        <div class="flex-align-baseline">
                                            <li>{{$question->option_4}}</li>
                                            <input type="radio" name="{{$index}}" value="{{ $question->option_4 }}">
                                            <hr>
                                        </div>
                                    </ul>
                                    <hr>
                                @endforeach
                            </ol>
                            <input type="submit" class="btn btn-success" onclick="clicked()" value="Submit">
                        </div>
                    </div>
                </form>
            </div>
            <timer :until="{{ count($quiz->questions) }}" class="countdown col-md-3"></timer>
        </div>
    </div>

@endsection

<script>

let submitForm = false;
function clicked() {
    submitForm = true;
}
window.onbeforeunload = function(e) {
    if (submitForm) {
        return null;
    }
    return "Do you really want to leave our brilliant application?";
};
</script>

正如您所看到的,我在@endsection之外有一个脚本标记,我所了解的是您可能无法做到这一点,它无法连接到任何元素。刀片模板本身,我试图抓住表单对象,就像我在vue组件中做的那样,但它返回null或undefined我无法记住,并且你不能将事件监听器附加到undefined,但如果我在浏览器的控制台中按预期运行相同的逻辑,我在表单上的onsubmit=""事件由于某种原因没有到达底部的脚本标记,submitForm的值{1}}变量没有变化,但奇怪的是,如果我手动点击提交按钮它会触发函数clicked(),所以我在这里很困惑,我不知道我是否可以只用vue实现这一点,如果不是,我不知道为什么onsubmit=""事件不起作用,当然我不能在@section内移动脚本标签因为vue会嘎嘎叫,如果你知道我应该怎么做我要感激一点代码。

1 个答案:

答案 0 :(得分:1)

首先,您应该将方法引用传递给beforeunload,而不是调用该方法的结果。因此,请删除()

created () {
    this.refreshEverySecond();
    document.addEventListener('beforeunload', this.redirect); // not this.redirect()
},

现在,启用/禁用处理程序的简单解决方案是只添加一个标志:

    data () {
        return {
            limiter: this.until * 10000
            preventSubmit: true
        };
    },

并且,在您的方法中,更新/使用该标志:

    methods: {
        // ...
        timeUp() {
            this.preventSubmit = false; // ALLOW redirect now

            const form = document.querySelector('[data-form-submit]');
            const radios = document.querySelectorAll('input[type=radio]');
            radios.forEach(radio => radio.style.display = 'none');
            form.submit(function(e) {
                console.log(e);
            });
        },
        redirect () {
            if (this.preventSubmit) {
               // do your thing to prevent submit
            }
        }
    },

替代

或者,你可以删除监听器:

created () {
    this.refreshEverySecond();
    document.addEventListener('beforeunload', this.redirect); // not this.redirect()
},

methods: {
    // ...
    timeUp() {
        document.removeEventListener('beforeunload', this.redirect);
        // ...

但我认为标志替代方案更安全。


正确处理unbeforeunload

根据评论,我正在添加一个如何工作的演示。

请参阅下面的JSFiddle DEMO here或演示。

new Vue({
  el: '#app',
  data: {
    preventSubmit : true
  },
  mounted () {
    window.addEventListener("beforeunload", this.redirect);
	},
  methods: {
  	redirect(event) {
    	if (this.preventSubmit) {
		  	var confirmationMessage = "\o/";
  			event.returnValue = confirmationMessage;     // Gecko, Trident, Chrome 34+
  			return confirmationMessage;              // Gecko, WebKit, Chrome <34
      }
    }
  }
})
<script src="https://unpkg.com/vue"></script>

<div id="app">
  <p>preventSubmit ? {{ preventSubmit  }}</p>
  <button @click="preventSubmit = !preventSubmit ">Toggle preventSubmit </button>
</div>
<br>
<a href="/somewhere-else">click to try to navigate away</a>