javascript - Vue.js How can i emit events between router-view components - Stack Overflow

I am building an e-merce website using vue.js and an api as backend,i have a root ponent called Main.v

I am building an e-merce website using vue.js and an api as backend, i have a root ponent called Main.vue which had a navigation of router-link and a body of router-view.

in a route called Cart.vue when a user updated quantity of some product i need Cart.vue to $emit an event to the root ponent Main.vue to trigger a function called getCartTotal()

hint Cart.vue is not a child ponent of Main.vue

Main.vue :

<template>
  <div>
    <div>
      <div v-on:getCartAgain="getCartTotal()" id="top-header" class="has-background-ishtari">
        <div class="container" style="padding:5px 0">
          <div class="is-hidden-mobile" style="font-size: 13px">
            <div
              class="has-text-white has-text-right"
              style="display: flex;align-items: center;justify-content: flex-end"
            >
              <i class="icon material-icons" style="margin-right: 0px;">attach_money</i>
              <span style="margin-right:15px">Best Deals</span>

              <i class="icon material-icons" style="margin-right: 5px;">low_priority</i>
              <span style="margin-right: 15px">Free & Easy Returns</span>

              <i class="icon material-icons" style="margin-right: 5px;">local_shipping</i>
              <span>Free Delivery (OVER $100)</span>
            </div>
          </div>
        </div>

        <div class="container" style="padding:10px 0">
          <div style="display: flex;justify-content: space-between;align-items: center;">
            <div id="header-logo" @click="openHomePage()">
              <img src="../assets/images/logo-ishtari.png" class width="140" />
            </div>
            <div style="flex-grow: 2;margin:0 40px">
              <p class="control is-expanded">
                <input
                  id="header-search"
                  class="input is-radiusless"
                  style="height: 35px;"
                  type="text"
                  placeholder="What are you looking for?"
                />
              </p>
            </div>
            <div
              class="has-text-white"
              style="display: flex;align-items: center;justify-content: space-between"
            >
              <div style="display: flex;align-items: center;padding-right:10px">
                <span>Login Or SignUp</span>
                <i class="icon material-icons">arrow_drop_down</i>
              </div>

              <div
                id="cart-container"
                style="display: flex;align-items: center;padding-left: 15px;border-left:1px solid rgba(255,255,255,0.5)"
                @click="openCartPage()"
              >
                <span style="margin-right:5px">Cart</span>
                <span>
                  <i class="icon material-icons">shopping_cart</i>
                  <span
                    class="has-background-ishtari-blue is-paddingless"
                    :class="this.cartCount.length == 0 ? 'button is-loading' : ''"
                    id="cart-total"
                  >{{this.cartCount}}</span>
                </span>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>

    <vue-page-transition name="fade-in-right">
      <router-view></router-view>
    </vue-page-transition>
  </div>
</template>
<script>
import VueCookies from "vue-cookies";

export default {

  data() {
    return {
      showNav: false,
      cartCount: "",
      readyToken: false
    };
  },
  created() {
    this.checkToken();
  },
  mounted() {
    this.getCartTotal();
  },

  methods: {
    openCartPage() {
      this.$router.push({ name: "cart" }).catch(err => {
        return err;
      });
    },
    openHomePage() {
      this.$router.push({ name: "home" }).catch(err => {
        return err;
      });
    },

    checkToken() {
      if (!VueCookies.isKey("token")) {
        let requestBody = {
          client_id: "shopping_oauth_client",
          client_secret: "shopping_oauth_secret"
        };
        let requestHeader = {
          "Content-Type": "application/x-www-form-urlencoded",
          Authorization:
            "Basic c2hvcHBpbmdfb2F1dGhfY2xpZW50OnNob3BwaW5nX29hdXRoX3NlY3JldA",
          "Access-Control-Allow-Origin": "*",
          "Cache-Control": null,
          "X-Requested-With": null
        };

        window.axios
          .post(window.main_urls["get-token"], requestBody, {
            headers: requestHeader
          })
          .then(response => {
            VueCookies.set("token", response.data.access_token);
          });
      } else {
        console.log(VueCookies.get("token"));
      }
    },

    getCartTotal() {
      console.log("here");
      let requestHeader = {
        Authorization: "Bearer " + VueCookies.get("token"),
        "Access-Control-Allow-Origin": "*",
        "Cache-Control": null,
        "X-Requested-With": null
      };
      window.axios
        .get(window.main_urls["get-cart"], { headers: requestHeader })
        .then(response => {
          if (response.data.error === "Cart is empty") {
            console.log(response.data);
            this.cartCount = 0;
          } else {
            this.cartCount = response.data.data.products.length.toString();
          }
        });
    }
  }
};
</script>
<style>
</style>

And Cart.vue :


<template>
  <div class="has-background-ishtari-grey">
    <div class="container">
      <div>
        <section v-if="this.loading || this.emptyCart" class="hero is-fullheight-with-navbar">
          <div
            v-if="this.loading"
            class="button is-loading hero is-fullheight-with-navbar has-background-ishtari-grey"
            style="border: none"
          >Please Wait</div>
          <div v-if="! this.loading && this.emptyCart" class="hero-body">
            <div class="container has-text-centered">
              <i class="material-icons has-text-grey" style="font-size: 80px">shopping_cart</i>
              <h1 class="title has-text-grey has-text-weight-bold is-4">Your Shopping Cart Is Empty</h1>
              <h2 class="subtitle title has-text-grey is-6">what are you waiting for</h2>
              <br />
              <button
                @click="goHome()"
                class="button is-ishtari-blue is-outlined has-text-weight-bold is-7"
              >START SHOPPING</button>
            </div>
          </div>
        </section>
      </div>

      <div class="section" v-if="! this.loading && !this.emptyCart">
        <div class="columns is-bordered" id="cart-products-container">
          <div class="column is-9" style="margin-right:20px">
            <h1
              class="subtitle has-text-weight-bold is-4"
            >My Cart ({{this.cartData.products.length}} items)</h1>
            <img
              src=".gif"
              style="margin-bottom:15px"
            />

            <div
              class="cart-product-row has-background-white"
              style="padding:10px 15px"
              v-for="product in cartData.products"
              :key="product.product_id"
            >
              <div
                class="columns padding-top:20px"
                :class="product.stock ? '' : 'has-background-danger'"
              >
                <div class="image is-128x128 column is-narrow">
                  <img :src="product.thumb" />
                </div>

                <div
                  class="column"
                  style="display:flex;flex-direction:column;justify-content:space-between"
                >
                  <p
                    class="has-text-grey subtitle is-7 is-marginless"
                    style="margin-bottom:10px !important"
                  >{{product.model}}</p>
                  <p class="has-text-weight-bold" style="font-size:14px">{{product.name}}</p>
                  <p>
                    <i @click="product.quantity = 0;updateCartQuantity(product.key,0)" class="material-icons has-text-grey" id="cart-delete">delete</i>
                  </p>
                </div>
                <div
                  class="column is-narrow"
                  style="padding-left:15px;display:flex;flex-direction:column;justify-content:center"
                >
                  <p class="has-text-weight-bold">{{product.price}}</p>
                </div>

                <div
                  class="column is-narrow"
                  style="display:flex;flex-direction:column;justify-content:center"
                >
                  <div class="field has-addons">
                    <p class="control">
                      <a
                        @click="product.quantity = Number(product.quantity) - 1;updateCartQuantity(product.key,product.quantity)"
                        class="button"
                      >-</a>
                    </p>
                    <p class="control">
                      <input
                        class="input has-text-centered"
                        type="text"
                        placeholder="0"
                        style="width:80px"
                        readonly
                        :value="product.quantity"
                        :ref="product.product_id"
                      />
                    </p>
                    <p class="control">
                      <a
                        class="button"
                        @click="product.quantity = Number(product.quantity) + 1;updateCartQuantity(product.key,product.quantity)"
                      >+</a>
                    </p>
                  </div>
                </div>
              </div>
            </div>
          </div>
          <div class="column is-narrow" style=" align-self:flex-start;margin-top:10px">
            <div style="border:1px solid #eee; background:#f7f9fe; padding:20px 15px">
              <div class="field has-addons">
                <p class="control">
                  <input class="input is-ishtari-green" type="text" placeholder="Coupon Code" />
                </p>
                <p class="control">
                  <a class="button is-ishtari-green">Apply</a>
                </p>
              </div>

              <div class="columns">
                <div class="column">
                  <p class="has-text-weight-bold">Order Summary</p>
                </div>
              </div>
              <div class="columns"></div>
              <div v-for="total in cartData.totals" :key="total.code">
                <div class="columns">
                  <div class="column">
                    <p>{{total.title}}</p>
                  </div>
                  <div class="column is-narrow">
                    <p>{{total.text}}</p>
                  </div>
                </div>
              </div>
            </div>
            <button
              class="button is-ishtari-blue has-text-weight-bold"
              style="display:block;width:100%;margin-top:10px"
            >CHECKOUT NOW</button>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import VueCookies from "vue-cookies";

export default {
  data() {
    return {
      cartData: [],
      emptyCart: false,
      loading: true
    };
  },

  created() {
    this.getCartContent();
  },
  methods: {
    goHome() {
      this.$router.push({ name: "home" }).catch(err => {
        return err;
      });
    },

    getCartContent() {
      let requestHeader = {
        Authorization: "Bearer " + VueCookies.get("token")
      };
      window.axios
        .get(window.main_urls["get-cart"], { headers: requestHeader })
        .then(response => {
          if (response.data.error === "Cart is empty") {
            this.emptyCart = true;
            this.loading = false;
          } else {
            this.loading = false;
            this.cartData = response.data.data;
          }
        });
    },

    updateCartQuantity(pkey, pquan) {
      this.loading = true;
      let requestHeader = {
        Authorization: "Bearer " + VueCookies.get("token")
      };
      let requestBody = {
        key: pkey.toString(),
        quantity: pquan.toString()
      };

      window.axios
        .put(window.main_urls["get-cart"], requestBody, {
          headers: requestHeader
        })
        .then(response => {
          if (response.data.success == "1") {
            this.getCartContent();
            this.$emit("getCartAgain");
          }

        });
    },
  }
};
</script>
<style scoped la>
.cart-product-row {
  border-bottom: 1px solid #eee;
  padding: 10px 0;
}
.cart-product-row:last-of-type {
  border-bottom: none;
}
#cart-delete {
  font-size: 20px;
  cursor: pointer;
}
#cart-delete:hover{
  transform: scale(1.05);
}
</style>


I need the function called updateCartQuantity() in Cart.vue to trigger the function called getCartTotal() in Main.vue

I am building an e-merce website using vue.js and an api as backend, i have a root ponent called Main.vue which had a navigation of router-link and a body of router-view.

in a route called Cart.vue when a user updated quantity of some product i need Cart.vue to $emit an event to the root ponent Main.vue to trigger a function called getCartTotal()

hint Cart.vue is not a child ponent of Main.vue

Main.vue :

<template>
  <div>
    <div>
      <div v-on:getCartAgain="getCartTotal()" id="top-header" class="has-background-ishtari">
        <div class="container" style="padding:5px 0">
          <div class="is-hidden-mobile" style="font-size: 13px">
            <div
              class="has-text-white has-text-right"
              style="display: flex;align-items: center;justify-content: flex-end"
            >
              <i class="icon material-icons" style="margin-right: 0px;">attach_money</i>
              <span style="margin-right:15px">Best Deals</span>

              <i class="icon material-icons" style="margin-right: 5px;">low_priority</i>
              <span style="margin-right: 15px">Free & Easy Returns</span>

              <i class="icon material-icons" style="margin-right: 5px;">local_shipping</i>
              <span>Free Delivery (OVER $100)</span>
            </div>
          </div>
        </div>

        <div class="container" style="padding:10px 0">
          <div style="display: flex;justify-content: space-between;align-items: center;">
            <div id="header-logo" @click="openHomePage()">
              <img src="../assets/images/logo-ishtari.png" class width="140" />
            </div>
            <div style="flex-grow: 2;margin:0 40px">
              <p class="control is-expanded">
                <input
                  id="header-search"
                  class="input is-radiusless"
                  style="height: 35px;"
                  type="text"
                  placeholder="What are you looking for?"
                />
              </p>
            </div>
            <div
              class="has-text-white"
              style="display: flex;align-items: center;justify-content: space-between"
            >
              <div style="display: flex;align-items: center;padding-right:10px">
                <span>Login Or SignUp</span>
                <i class="icon material-icons">arrow_drop_down</i>
              </div>

              <div
                id="cart-container"
                style="display: flex;align-items: center;padding-left: 15px;border-left:1px solid rgba(255,255,255,0.5)"
                @click="openCartPage()"
              >
                <span style="margin-right:5px">Cart</span>
                <span>
                  <i class="icon material-icons">shopping_cart</i>
                  <span
                    class="has-background-ishtari-blue is-paddingless"
                    :class="this.cartCount.length == 0 ? 'button is-loading' : ''"
                    id="cart-total"
                  >{{this.cartCount}}</span>
                </span>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>

    <vue-page-transition name="fade-in-right">
      <router-view></router-view>
    </vue-page-transition>
  </div>
</template>
<script>
import VueCookies from "vue-cookies";

export default {

  data() {
    return {
      showNav: false,
      cartCount: "",
      readyToken: false
    };
  },
  created() {
    this.checkToken();
  },
  mounted() {
    this.getCartTotal();
  },

  methods: {
    openCartPage() {
      this.$router.push({ name: "cart" }).catch(err => {
        return err;
      });
    },
    openHomePage() {
      this.$router.push({ name: "home" }).catch(err => {
        return err;
      });
    },

    checkToken() {
      if (!VueCookies.isKey("token")) {
        let requestBody = {
          client_id: "shopping_oauth_client",
          client_secret: "shopping_oauth_secret"
        };
        let requestHeader = {
          "Content-Type": "application/x-www-form-urlencoded",
          Authorization:
            "Basic c2hvcHBpbmdfb2F1dGhfY2xpZW50OnNob3BwaW5nX29hdXRoX3NlY3JldA",
          "Access-Control-Allow-Origin": "*",
          "Cache-Control": null,
          "X-Requested-With": null
        };

        window.axios
          .post(window.main_urls["get-token"], requestBody, {
            headers: requestHeader
          })
          .then(response => {
            VueCookies.set("token", response.data.access_token);
          });
      } else {
        console.log(VueCookies.get("token"));
      }
    },

    getCartTotal() {
      console.log("here");
      let requestHeader = {
        Authorization: "Bearer " + VueCookies.get("token"),
        "Access-Control-Allow-Origin": "*",
        "Cache-Control": null,
        "X-Requested-With": null
      };
      window.axios
        .get(window.main_urls["get-cart"], { headers: requestHeader })
        .then(response => {
          if (response.data.error === "Cart is empty") {
            console.log(response.data);
            this.cartCount = 0;
          } else {
            this.cartCount = response.data.data.products.length.toString();
          }
        });
    }
  }
};
</script>
<style>
</style>

And Cart.vue :


<template>
  <div class="has-background-ishtari-grey">
    <div class="container">
      <div>
        <section v-if="this.loading || this.emptyCart" class="hero is-fullheight-with-navbar">
          <div
            v-if="this.loading"
            class="button is-loading hero is-fullheight-with-navbar has-background-ishtari-grey"
            style="border: none"
          >Please Wait</div>
          <div v-if="! this.loading && this.emptyCart" class="hero-body">
            <div class="container has-text-centered">
              <i class="material-icons has-text-grey" style="font-size: 80px">shopping_cart</i>
              <h1 class="title has-text-grey has-text-weight-bold is-4">Your Shopping Cart Is Empty</h1>
              <h2 class="subtitle title has-text-grey is-6">what are you waiting for</h2>
              <br />
              <button
                @click="goHome()"
                class="button is-ishtari-blue is-outlined has-text-weight-bold is-7"
              >START SHOPPING</button>
            </div>
          </div>
        </section>
      </div>

      <div class="section" v-if="! this.loading && !this.emptyCart">
        <div class="columns is-bordered" id="cart-products-container">
          <div class="column is-9" style="margin-right:20px">
            <h1
              class="subtitle has-text-weight-bold is-4"
            >My Cart ({{this.cartData.products.length}} items)</h1>
            <img
              src="https://storage.googleapis./noon-cdn-res/rn/banners/en_disclaimer-cart-desktop.gif"
              style="margin-bottom:15px"
            />

            <div
              class="cart-product-row has-background-white"
              style="padding:10px 15px"
              v-for="product in cartData.products"
              :key="product.product_id"
            >
              <div
                class="columns padding-top:20px"
                :class="product.stock ? '' : 'has-background-danger'"
              >
                <div class="image is-128x128 column is-narrow">
                  <img :src="product.thumb" />
                </div>

                <div
                  class="column"
                  style="display:flex;flex-direction:column;justify-content:space-between"
                >
                  <p
                    class="has-text-grey subtitle is-7 is-marginless"
                    style="margin-bottom:10px !important"
                  >{{product.model}}</p>
                  <p class="has-text-weight-bold" style="font-size:14px">{{product.name}}</p>
                  <p>
                    <i @click="product.quantity = 0;updateCartQuantity(product.key,0)" class="material-icons has-text-grey" id="cart-delete">delete</i>
                  </p>
                </div>
                <div
                  class="column is-narrow"
                  style="padding-left:15px;display:flex;flex-direction:column;justify-content:center"
                >
                  <p class="has-text-weight-bold">{{product.price}}</p>
                </div>

                <div
                  class="column is-narrow"
                  style="display:flex;flex-direction:column;justify-content:center"
                >
                  <div class="field has-addons">
                    <p class="control">
                      <a
                        @click="product.quantity = Number(product.quantity) - 1;updateCartQuantity(product.key,product.quantity)"
                        class="button"
                      >-</a>
                    </p>
                    <p class="control">
                      <input
                        class="input has-text-centered"
                        type="text"
                        placeholder="0"
                        style="width:80px"
                        readonly
                        :value="product.quantity"
                        :ref="product.product_id"
                      />
                    </p>
                    <p class="control">
                      <a
                        class="button"
                        @click="product.quantity = Number(product.quantity) + 1;updateCartQuantity(product.key,product.quantity)"
                      >+</a>
                    </p>
                  </div>
                </div>
              </div>
            </div>
          </div>
          <div class="column is-narrow" style=" align-self:flex-start;margin-top:10px">
            <div style="border:1px solid #eee; background:#f7f9fe; padding:20px 15px">
              <div class="field has-addons">
                <p class="control">
                  <input class="input is-ishtari-green" type="text" placeholder="Coupon Code" />
                </p>
                <p class="control">
                  <a class="button is-ishtari-green">Apply</a>
                </p>
              </div>

              <div class="columns">
                <div class="column">
                  <p class="has-text-weight-bold">Order Summary</p>
                </div>
              </div>
              <div class="columns"></div>
              <div v-for="total in cartData.totals" :key="total.code">
                <div class="columns">
                  <div class="column">
                    <p>{{total.title}}</p>
                  </div>
                  <div class="column is-narrow">
                    <p>{{total.text}}</p>
                  </div>
                </div>
              </div>
            </div>
            <button
              class="button is-ishtari-blue has-text-weight-bold"
              style="display:block;width:100%;margin-top:10px"
            >CHECKOUT NOW</button>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import VueCookies from "vue-cookies";

export default {
  data() {
    return {
      cartData: [],
      emptyCart: false,
      loading: true
    };
  },

  created() {
    this.getCartContent();
  },
  methods: {
    goHome() {
      this.$router.push({ name: "home" }).catch(err => {
        return err;
      });
    },

    getCartContent() {
      let requestHeader = {
        Authorization: "Bearer " + VueCookies.get("token")
      };
      window.axios
        .get(window.main_urls["get-cart"], { headers: requestHeader })
        .then(response => {
          if (response.data.error === "Cart is empty") {
            this.emptyCart = true;
            this.loading = false;
          } else {
            this.loading = false;
            this.cartData = response.data.data;
          }
        });
    },

    updateCartQuantity(pkey, pquan) {
      this.loading = true;
      let requestHeader = {
        Authorization: "Bearer " + VueCookies.get("token")
      };
      let requestBody = {
        key: pkey.toString(),
        quantity: pquan.toString()
      };

      window.axios
        .put(window.main_urls["get-cart"], requestBody, {
          headers: requestHeader
        })
        .then(response => {
          if (response.data.success == "1") {
            this.getCartContent();
            this.$emit("getCartAgain");
          }

        });
    },
  }
};
</script>
<style scoped la>
.cart-product-row {
  border-bottom: 1px solid #eee;
  padding: 10px 0;
}
.cart-product-row:last-of-type {
  border-bottom: none;
}
#cart-delete {
  font-size: 20px;
  cursor: pointer;
}
#cart-delete:hover{
  transform: scale(1.05);
}
</style>


I need the function called updateCartQuantity() in Cart.vue to trigger the function called getCartTotal() in Main.vue

Share Improve this question asked Apr 4, 2020 at 20:55 user13223749user13223749 1
  • 3 This is a job for Vuex. – Sølve Commented Apr 4, 2020 at 20:58
Add a ment  | 

1 Answer 1

Reset to default 4

Vuex may be a bit more than you need here. You can just use the pub/sub technique by creating a simple file that exports a new vue instance, you can then listen to events emitted on that Vue instance:

//bus.js

import Vue from 'vue'

export default new Vue()

Then in your Main.vue you can import that:

import EventBus from '@/path/to/bus'

And in your created hook you can set up a listener:

created() {
    EventBus.$on('refresh_cart_total', this.getCartTotal)
}

And you can $emit that event from your Cart.vue when you need to. Once again, import the bus:

import EventBus from '@/path/to/bus'

Then in your Cart.vue call it whenever you need:

EventBus.$emit('refresh_cart_total')

Now you have a bi-directional pub/sub system in place and you don't need to introduce Vuex for a simple task such as this.

Bonus

To keep things DRY you could also implement constants within your bus.js, such as:

export const REFRESH_CART_TOTAL = 'refresh_cart_total'

And now you can use import * as CART_CONSTANTS from '/path/to/bus' and use this method:

EventBus.$on(CART_CONSTANTS.REFRESH_CART_TOTAL, this.getCartTotal)

and:

EventBus.$emit(CART_CONSTANTS.REFRESH_CART_TOTAL)

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745427692a4627254.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信