首页 > 解决方案 > 来自 php 脚本的响应数据在 ROOT Vue 实例中不可用

问题描述

我有用于模式窗口的 vuejs 组件。当用户在我想要的字段中填写所有数据时:

  1. 向服务器发送数据。
  2. 设置超时以可视化用户正在发送数据(将 fa-spin 类应用于按钮)。
  3. 关闭模态窗口。
  4. 从服务器接收答案(带有成功消息的 php 控制器)。
  5. 向用户显示已发送消息的警报块。

现在我遇到了第 2、4 和 5 点的问题。超时不起作用,模态窗口立即关闭。来自 php 脚本的响应数据在 ROOT Vue 实例中不可用。我只能在组件内部看到服务器响应,但我不知道如何将它传递给 ROOT。

视图\layout.blade.php

                <div v-if="FreeZamerSent" class="alert alert-dismissible fade show" :class="class" role="alert">
                  @{{ message }}
                  <button type="button" class="close" data-dismiss="alert" aria-label="Close">
                    <span aria-hidden="true">&times;</span>
                  </button>
                </div>


<freezamermodal v-if="showFreeZamerModal" @close="showFreeZamerModal = false" @sent="onFreeZamerSent(response)"></freezamermodal>

App\Http\Controllers\MainController.php

    public function freezamer(Request $request) {

        request()->validate([
            'customer_name' => 'required',
            'customer_email' => 'required|email',
            'customer_phone' => 'required'
        ]);

        Mail::to( env('MAIL_TO_ADDRESS') )->send(new FreeZamer());

        return [
            'message' => 'Ваша заявка отправлена! В ближайшее время мы свяжемся с вами.', 
            'alertclass' => 'alert-success', 
                ];
    }

资源\js\components\FreeZamerModal.vue

<template>

<div class="modal is-active" id="FreezamerModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <h4 class="modal-title" id="myModalLabel">Заказать бесплатный замер</h4>
        <button type="button" class="close" data-dismiss="modal" aria-label="Close" @click="$emit('close')">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
        <form method="POST" action="/freezamer" @submit.prevent="onSubmit">
          <input type="hidden" name="_token" :value="csrf">
          <div class="modal-body">
              <input type="hidden" class="form-control" name="formname" :value="formname">
              <input type="hidden" class="form-control" name="currentUrl" :value="currentUrl">
              <div class="error" v-if="!$v.customer_name.required">Введите имя</div>
              <div class="error" v-if="!$v.customer_name.minLength">Имя должно содержать минимум {{$v.customer_name.$params.minLength.min}} буквы.</div>
              <div class="error" v-if="!$v.customer_name.cyrillic">Имя должно состоять только из русских букв</div>
              <input type="text" class="form-control" name="customer_name" placeholder="Ваше имя" :class="{ 'form-control--error': $v.customer_name.$error }" v-model.trim="$v.customer_name.$model">

              <div class="error" v-if="!$v.customer_email.required">Введите email</div>
              <div class="error" v-if="!$v.customer_email.email">Введите существующий email</div>
              <input type="text" class="form-control" name="customer_email" placeholder="Ваш e-mail" :class="{ 'form-control--error': $v.customer_email.$error }" v-model.trim="$v.customer_email.$model">

              <div class="error" v-if="!$v.customer_phone.required">Введите номер телефона</div>
              <div class="error" v-if="!$v.customer_phone.minLength">Телефон должен содержать 11 цифр.</div>
              <input type="tel" class="form-control" name="customer_phone" placeholder="Ваш телефон" :class="{ 'form-control--error': $v.customer_phone.$error }" v-model.trim="$v.customer_phone.$model" v-mask="'+# (###) ###-##-##'">
          </div>
          <div class="modal-footer">

            <button class="button button_fill" type="submit" :disabled="$v.$invalid">
              <svg v-if="!success"><use xlink:href="#plan"></use></svg>
              <i class="fa fa-spinner fa-spin" v-if="success"></i>
              <span>Бесплатный замер</span>
            </button>

          </div>
        </form>
    </div>
  </div>
</div>

</template>

<script>
    import { alpha, email, helpers, maxLength, minLength, numeric, required } from 'vuelidate/lib/validators';
    import {mask} from 'vue-the-mask';
    export const cyrillic = helpers.regex('cyrillic', /^[А-Яа-яёЁ\s]+$/);

    export default {
        name: "FreeZamerModal",

        data: function () {
          return {
            currentUrl: window.location.pathname,
            csrf: document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
            formname: 'freezamer',
            customer_name: '',
            customer_email: '',
            customer_phone: '',
            success: false,
            errors: []
          }
        },

        directives: {mask},

        methods: {
          onSubmit () {
            axios.post('/freezamer', this.$data)
            .then(this.onSuccess)
            .catch(error => this.errors = error.response.data);
          },

          onSuccess (response) {
            this.success = true;
            setTimeout(5000);
            this.$emit('sent', response);
          },

        },

        mounted() {
           console.log('Freezamer component mounted.')
        },

        validations: {
          customer_name: {
            required,
            cyrillic,
            minLength: minLength(3)
          },
          customer_email: {
            required,
            email
          },
          customer_phone: {
            required,
            minLength: minLength(18),
            maxLength: maxLength(18)
          }

        }

    }
</script>

资源\js\app.js

Vue.component('freezamermodal', require('./components/FreeZamerModal.vue').default);


const app = new Vue({
    el: '#app',

    data: {
        showFreeZamerModal: false,
        FreeZamerSent: false,
        message: '',
        alertclass : '',
    },

    methods: {
        onFreeZamerSent (response) {
            this.showFreeZamerModal = false,
            this.FreeZamerSent = true,
            this.message = this.response.message,
            this.alertclass = this.response.alertclass
        },

    },



});

相反,我在控制台中有错误

[Vue warn]: Property or method "response" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property.

[Vue warn]: Error in v-on handler: "TypeError: Cannot read property 'message' of undefined"

found in

---> <FreeZamerModal> at resources/js/components/FreeZamerModal.vue
       <Root>

标签: phplaravelvue.jsaxiosresponse

解决方案


一步一步我使用 vue-devtools 发现了问题。以下代码this.$emit('sent', response)将大响应对象传递给 ROOT 实例并访问 response.data 工作解决方案。唯一的缺点是我不明白为什么 setTimeout 不起作用。动作this.$emit('sent', response)会在 5 秒后立即触发。5 秒后,我在控制台中看到错误。如果删除超时一切正常。

资源\js\components\FreeZamerModal.vue

          onSuccess (response) {
            this.success = true;
            setTimeout(this.$emit('sent', response), 5000);
          },

资源\js\app.js

    data: {
        showFreeZamerModal: false,
        FreeZamerSent: false,
        message: '',
        alertclass: ''
    },
    methods: {
        onFreeZamerSent (response) {
            this.showFreeZamerModal = false,
            this.FreeZamerSent = true,
            this.message = response.data.message,
            this.alertclass = response.data.alertclass
        },

刀片.php

<freezamermodal v-if="showFreeZamerModal" @close="showFreeZamerModal = false" @sent="onFreeZamerSent"></freezamermodal>

控制台错误,5 秒后出现

VM27172:1 Uncaught SyntaxError: Unexpected identifier
setTimeout (async)
onSuccess @ FreeZamerModal.vue?0b2e:81
Promise.then (async)
onSubmit @ FreeZamerModal.vue?0b2e:75
submit @ FreeZamerModal.vue?b9fb:63
invokeWithErrorHandling @ vue.common.dev.js?4650:1859
invoker @ vue.common.dev.js?4650:2184
original._wrapper @ vue.common.dev.js?4650:7543


推荐阅读