lipasite
@lipasite

Взаимодействие Vue компонентов: Property or method «updatebalance» is not defined?

Привет! Использую Laravel + Vue JS 2. Есть 2 vue компонента - кнопка заказа (Order.vue) и лейбл баланса (Balance_label.vue). Обмениваются событиями они через Event Bus (то есть друг другу они не подчиняются, как родитель-потомок, а находятся на одном уровне). Клик на кнопку Order инициирует событие updbalance, которое слушает компонент Balance_label.vue, и при его возникновении выполняет метод updatebalance. В результате после заказа списывается баланс и сразу же обновляется лейбл баланса без перезагрузки страницы (реализовано через axios).
В режиме npm run dev работают они как задумано: нажали кнопку - сразу обновился баланс, только есть проблема - в Консоли браузера возникают 2 предупреждения:

[Vue warn]: Property or method "updatebalance" 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. See: https://vuejs.org/v2/guide/reactivity.html#Declari... (found in )

[Vue warn]: Invalid handler for event "updbalance": got undefined found in ---> at resources/assets/js/components/Balance_label.vue

Можно было бы пройти мимо этих ворнингов, ведь все работает хорошо. Но в режиме npm run production веб-страница пустая (нет ни одного видимого компонента), а в Консоли браузера ошибка:

ReferenceError: updatebalance is not defined at dn.eval (eval at Ca (app.js:1), :3:465) at dn.t._render (app.js:1) at dn. (app.js:1) at Ie.get (app.js:1) at new Ie (app.js:1) at app.js:1 at dn.$mount (app.js:1) at dn.$mount (app.js:1) at dn.t._init (app.js:1) at new dn (app.js:1)

Подробности всех файлов с кодом ниже

Лейбл баланса "Balance label" рендерится так на html-страничке:
<balancelabel @updbalance="updatebalance"></balancelabel>


Кнопка заказа "Order":
<order
           :product={{ $product->id }}
           :ordered={{ $product->ordered() ? 'true' : 'false' }}
           productname= "<b>{{ $product->name }}</b>"
></order>


Balance_label.vue
<template> 
        <label id="balance_view" class="btn btn-default tp-icon chat-icon">
                    {{ balance }}  dollars     
        </label>
</template> 

<script>
    import { bus } from '../bootstrap';
    import 'vuejs-noty/dist/vuejs-noty.css'
    export default {

        data: function () {
            return {
                balance: 0
            }    
        },

        created(){
            bus.$on('updbalance', (data) => {
               this.updatebalance(); 
            });
        },
        mounted : function() {
            this.updatebalance();
        },

       methods: {

            updatebalance: function (){
            axios
                .get('/api/v1/balance')
                .then(response => {
                    this.balance = response.data.data[0][0]
                })        
                .catch(response => console.log(response.data));
             },
        }
    };
</script>


Order.vue
<template> 
        <span>
            <button v-if="isOrdered" 
                @click.prevent="unOrder(product)" 
                type="button" 
                class="btn btn-block btn-warning btn-xs" 
                name="order-button">
                    <i class="fa fa-heart"></i>&nbsp; Cancel Order
            </button>
            <button v-else-if="!isOrdered" 
                @click.prevent="order(product)" 
                type="button" 
                class="btn btn-block btn-success btn-xs" 
                name="order-button">
                    <i class="fa fa-heart-o"></i>&nbsp; Order
            </button>
       </span>    
 </template> 

<script>
    import { bus } from '../bootstrap';
    import 'vuejs-noty/dist/vuejs-noty.css'
    export default {
        props: ["product", "ordered", "productname", "resp"],

        data: function() {
            return { 
                isOrdered: '',
            }
        },

        mounted() {
            this.isOrdered = this.isOrder ? true : false;
        },
        computed: {
            isOrder() {
                return this.ordered;
            },
         },
        methods: {

            order(product) {
            axios
                .post('/order/' + product)
                .then(response => {this.isOrdered = true;
                                   this.$noty.success("The product " + this.productname + " is Ordered!");
                                   bus.$emit('updbalance'); //send update balance event
                                   })
               .catch(response => console.log(response.data));
            },

            unOrder(product) {
             axios
                .post('/unorder/' + product)
                .then(response => {this.isOrdered = false;
                                   this.$noty.warning("The product order " + this.productname + " cancelled"); 
                                   bus.$emit('updbalance'); //send update balance event
                                   })
                .catch(response => console.log(response.data));
            }
         }
    };
</script>


app.js
require('./bootstrap');
window.Vue = require('vue');

Vue.component('order', require('./components/Order.vue'));
Vue.component('balancelabel', require('./components/Balance_label.vue'));

app = new Vue({
    el: '#app',
});


bootstrap.js
window._ = require('lodash');
try {
      require('bootstrap-sass');
} catch (e) {}

window.axios = require('axios');
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

let token = document.head.querySelector('meta[name="csrf-token"]');

if (token) {
    window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
    console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-x-csrf-token');
}

window.Vue = require('vue');
window.events = new Vue();

import Vue from 'vue'
import VueNoty from 'vuejs-noty'

//connect event bus
export const bus = new Vue();

Vue.use(VueNoty)


package.json
{
  "private": true,
  "scripts": {
    "dev": "npm run development",
    "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
    "watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
    "watch-poll": "npm run watch -- --watch-poll",
    "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
    "prod": "npm run production",
    "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
  },
  "devDependencies": {
    "axios": "^0.17",
    "bootstrap-sass": "^3.3.7",
    "cross-env": "^5.2.0",
    "jquery": "^3.2",
    "laravel-mix": "^1.0",
    "lodash": "^4.17.4",
    "vue": "^2.5.16"
  },
  "dependencies": {
    "noty": "^3.2.0-beta",
    "sweetalert": "^2.1.0",
    "vuejs-noty": "^0.1.3"
  }
}


Гугление и чтение форумов Stackoverflow, vuejs forum ничего не дало.
Везде решают проблему в случае использования properties, а не methods.
Здесь пишут, что надо объявление метода в родительский компонент добавить, но у меня нет схемы "родитель-потомок", так как они общаются через шину событий (Event bus).
Предпринятые мной действия для самостоятельного решения проблемы, которые не помогли:
  • объявление метода "updatebalance" в корневом экземпляре Vue (файл app.js)
  • объявление метода "updatebalance" в Event bus экземпляре Vue (файл bootstrap.js)
  • объявление метода "updatebalance" в компоненте Order.vue
  • Вопрос задан
  • 805 просмотров
Решения вопроса 1
0xD34F
@0xD34F Куратор тега Vue.js
<balancelabel @updbalance="updatebalance"></balancelabel>

А зачем здесь обработчик события? Вы это событие обрабатываете внутри компонента, emit не делаете... Может его, ну не знаю, УБРАТЬ?!
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы