I am building an e-commerce website using vue.js and an api as backend,
i have a root component 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 component Main.vue to trigger a function called getCartTotal()
hint Cart.vue is not a child component 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.com/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
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)
Related
I'm relatively new to Vue JS. So I have this case on Form Submit.
This is what I've submitted in Console Log:
{
amount: [
monday: "123",
tuesday: "97438"
],
day_of_week: "perday",
started_at: "2022-09-14"
}
And what it received in Laravel Backend when I dump the data is this:
^ array:3 [
"started_at" => "2022-09-14"
"day_of_week" => "perday"
"amount" => []
]
So My Question Is, what did I do wrong? How is the "amount" part not included on the backend? It already looks right on the console.log. I do appreciate help here. I reckon I did a mistake on how to write the model name in the array part inside HTML, but still, I do not know how to do it right.
THANK YOU IN ADVANCE ๐๐ผ๐๐ผ๐๐ผ
This is how I submit my form:
HTML Vue Page:
<div class="card-body p-9">
<div class="row mb-8">
<div class="col-xl-3">
<div class="fs-6 fw-semibold mt-2 mb-3">Type of Rate</div>
</div
<div class="col-xl-9 fv-row">
<VueMultiselect v-model="day_of_week" label="name" track-by="name" placeholder="Select Type" :options="type_of_day" :close-on-select="true">
<template slot="singleLabel" slot-scope="{ type_of_day }"><strong>{{ type_of_day.name }}</strong></template>
</VueMultiselect>
</div>
</div>
<div class="row mb-8">
<div class="col-xl-3">
<div class="fs-6 fw-semibold mt-2 mb-3">Started Date</div>
</div
<div class="col-xl-9 fv-row">
<div class="position-relative d-flex align-items-center">
<input v-model="ticketing.started_at" class="form-control form-control-solid ps-12" name="date" placeholder="Pick Start date" id="kt_datepicker_1" />
</div>
</div>
</div>
<div v-if="day_of_week">
<div v-if="day_of_week.value == 'allday'" class="row mb-8">
<div class="col-xl-3">
<div class="fs-6 fw-semibold mt-2 mb-3">Entrance Rate</div>
</div>
<div class="col-xl-9 fv-row">
<div class="input-group mb-5">
<span class="input-group-text">Rp.</span>
<input v-model="amount.allday" type="text" class="form-control" aria-label="Amount"/>
</div>
</div>
</div>
<div v-if="day_of_week.value == 'perday'" class="row mb-8">
<div class="col-12 mb-8">
<div class="fs-6 fw-bold mt-2 mb-3">Entrance Rate Per Day of Week</div>
</div>
<div class="col-xl-3">
<div class="fs-6 fw-semibold mt-2 mb-3">Monday</div>
</div>
<div class="col-xl-9 fv-row">
<div class="input-group mb-5">
<span class="input-group-text">Rp.</span>
<input v-model="amount.day.monday" type="text" class="form-control" aria-label="Amount"/>
</div>
</div>
<div class="col-xl-3">
<div class="fs-6 fw-semibold mt-2 mb-3">Tuesday</div>
</div>
<div class="col-xl-9 fv-row">
<div class="input-group mb-5">
<span class="input-group-text">Rp.</span>
<input v-model="amount.day.tuesday" type="text" class="form-control" aria-label="Amount"/>
</div>
</div>
</div>
</div>
</div>
<div class="card-footer d-flex justify-content-end pb-6 px-9">
<span #click="storeNewRate" class="btn btn-primary">Create New Rate</span>
</div>
</template>
This Is the script Part
<script>
import { onMounted, toRaw } from "vue";
import useTicketing from "../../../composable/ticketing";
export default {
data() {
const { storeTicketing, ticketing } = useTicketing()
const storeNewRate = async () => {
let type = this.day_of_week.value
let submittedAmount = []
type == 'allday' ? submittedAmount = this.amount.allday : submittedAmount = this.amount.day
this.ticketing.day_of_week = this.day_of_week.value
this.ticketing.amount = submittedAmount
console.log('submit: ', toRaw(ticketing))
await storeTicketing({...ticketing})
}
return {
type_of_day: [
{
"name": "All Days of Week",
"value": "allday"
},
{
"name": "Rate per Day",
"value": "perday"
},
],
started_at: '',
day_of_week: '',
amount: {
allday: '',
day: []
},
storeNewRate,
ticketing
}
}
}
</script>
And I have Separate file for handling the API:
export default function usePlan() {
const ticketing = reactive({});
const router = useRouter();
let toastr = Swal.mixin({
toast: true,
position: 'top-end',
showConfirmButton: false,
timer: 5000
})
const storeTicketing = async (data) => {
let errors = ''
try {
let response = await axios.post('/api/ticketing/create', data);
ticketing.value = response.data;
await toastr.fire("Success", "New Rate Has Been Created!", "success");
} catch (e) {
if(e.response.status === 422) {
for(const key in e.response.data.message) {
errors = e.response.data.message[key][0];
toastr.fire("Failed", errors, "error");
}
errors = ''
}
}
}
return {
storeTicketing,
ticketing
}
}
๐๐ผ
Component.html
<div class="bootstrap-wrapper" *ngIf="!isSubmit">
<div class="container-fluid">
<div class="row">
<div class="col-md-2">
<!-- instructions -->
<h2>Instructions</h2>
</div>
<div class="col-md-8">
<!-- questions -->
<ng-container *ngIf="questions">
<h2>{{questions[0].quiz.title}}</h2>
</ng-container>
<mat-card *ngFor="let q of questions, let i= index" class="mt20">
<mat-card-content>
<p> Q {{i+1}}) <span [innerHTML]="q.content"></span> </p>
<mat-divider></mat-divider>
<div class="row mt20" >
<div class="col-md-6">
<input type="radio" [value]="q.option1"
[name]="i"
// this is where i am getting error
[(ngModel)] ="q.givenAnswer"
/>
{{q.option1}}
{{i}}
</div>
<div class="col-md-6">
<input type="radio" [value]="q.option2"
[name]="i"
// this is where i am getting error
[(ngModel)] ="q.givenAnswer"
/>
{{q.option2}}
{{i}}
</div>
</div>
<div class="row mt20">
<div class="col-md-6">
<input type="radio" [value]="q.option3"
// this is where i am getting error
[name]="i"
[(ngModel)] ="q.givenAnswer"
/>
{{q.option3}}
{{i}}
</div>
<div class="col-md-6">
<input
type="radio"
[value]="q.option4"
// this is where i am getting error
[name]="i"
[(ngModel)] ="q.givenAnswer"
/>
{{q.option4}}
{{i}}
</div>
</div>
</mat-card-content>
</mat-card>
<div class="container text-center mt20">
<button (click)="submitQuiz()" mat-raised-button color="accent">Submit
Quiz</button>
</div>
</div>
<div class="col-md-2">
</div>
</div>
</div>
</div>
<!-- Show Result -->
<div class="bootstrap-wrapper" *ngIf="isSubmit">
<div class="row">
<div class="col-md-6 offset-md-3">
<mat-card>
<mat-card-header>
<mat-card-title>
<h1 class="text-center mall">Quiz Result</h1>
</mat-card-title>
</mat-card-header>
<mat-card-content>
<h1>Marks Obtained: {{marksGot}}</h1>
<h1>Correct Ansers: {{correctAnswers}}</h1>
<h1>Questions Attempted: {{attempted}}</h1>
</mat-card-content>
<mat-card-actions>
<button mat-raised-button color="accent">Print</button>
<button mat-raised-button color="accent" [routerLink]="'/user-
dashboard/0'">Home</button>
</mat-card-actions>
</mat-card>
</div>
</div>
</div>
Component.ts
import { LocationStrategy } from '#angular/common';
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { QuestionService } from 'src/app/services/question.service';
import Swal from 'sweetalert2';
#Component({
selector: 'app-start-quiz',
templateUrl: './start-quiz.component.html',
styleUrls: ['./start-quiz.component.css']
})
export class StartQuizComponent implements OnInit {
qid;
questions;
marksGot = 0;
correctAnswers = 0;
attempted = 0;
isSubmit = false;
constructor(private locationSt: LocationStrategy, private _route: ActivatedRoute, private
_question: QuestionService) { }
ngOnInit(): void {
this.preventBackButton();
this.qid = this._route.snapshot.params['qid'];
this.loadQuestions();
}
loadQuestions() {
this._question.getQuestionsOfQuizForTest(this.qid).subscribe(
(data: any) => {
this.questions = data;
this.questions.forEach((q) => {
q['givenAnswer'] = '';
});
console.log(data);
},
(error) => {
Swal.fire('Error', 'Error in loading questions of quiz', 'error');
}
);
}
preventBackButton() {
history.pushState(null, null, location.href);
this.locationSt.onPopState(() => {
history.pushState(null, null, location.href);
})
}
submitQuiz() {
Swal.fire({
title: 'Do you want to Submit quiz?',
showCancelButton: true,
confirmButtonText: 'Submit Quiz',
icon: 'info',
}).then((e) => {
if (e.isConfirmed) {
//calculation
this.isSubmit=true;
this.questions.forEach((q) => {
if (q.givenAnswer == q.answer) {
this.correctAnswers++;
let marksSingle = this.questions[0].quiz.maxMarks / this.questions.length;
this.marksGot += marksSingle;
}
if (q.givenAnswer.trim() != '') {
this.attempted++;
}
});
console.log("Correct Answers " + this.correctAnswers);
}
})
}
}
enter image description here
When i am name as [name] it is showing number is not assignable to type String and when i am using name it is compiling successfully but i have three questions in a quiz and while selecting an option of a particular question other options of other questions are getting deselected. what to do?
Thanks in Advance
[(ngModel)]="q.givenAnswer" type="radio" [value]="q.option1" name={{i}}
I am developing one page which is responsible from displaying books and the response is coming from backend API, DisplayBooks.vue component is imported inside a Dashboard.vue (parent-component).inside DisplayBooks.vue it contains books [] array , i want to pass this length to the Dashboard.vue component ,How to acheive this thing please help me to fix this thing..
Dashboard.vue
<template>
<div class="main">
<div class="navbar navbar-default navbar-fixed-top">
<div class="navbar-header">
<img src="../assets/education.png" alt="notFound" class="education-image" />
</div>
<ul class="nav navbar-nav">
<li>
<p class="brand">Bookstore</p>
</li>
</ul>
<div class="input-group">
<i #click="handlesubmit();" class="fas fa-search"></i>
<div class="form-outline">
<input type="search" v-model="name" class="form-control" placeholder='search...' />
</div>
</div>
<ul class="nav navbar-nav navbar-right" id="right-bar">
<li><a> <i class="far fa-user"></i></a></li>
<p class="profile-content">profile</p>
<li #click="shownComponent='Cart'"><a><i class="fas fa-cart-plus"></i></a></li>
<p class="cart-content" >cart <span class="length" v-if="booksCount">{{booksCount}}</span></p>
</ul>
</div>
<div class="mid-body">
<!-- i want to display books length here -->
<h6>Books<span class="items">({{books.length}})</span></h6>
<select class="options" #change="applyOption">
<option disabled value="">Sort by relevance</option>
<option value="HighToLow">price:High to Low</option>
<option value="LowToHigh">price:Low to High</option>
</select>
</div>
<div v-if="flam==false">
<h2>Hello</h2>
</div>
<DisplayBooks v-show="flag==='noOrder' && shownComponent==='DisplayBooks'" #update-books-count="(n)=>booksCount=n" />
<Cart v-show=" shownComponent==='Cart'" />
<sortBooksLowtoHigh v-show="flag==='lowToHigh'" #update-books-count="(n)=>booksCount=n" />
<sortBooksHightoLow v-show="flag==='highToLow'" #update-books-count="(n)=>booksCount=n" />
</div>
</template>
<script>
import sortBooksLowtoHigh from './sortBooksLowtoHigh.vue'
import sortBooksHightoLow from './sortBooksHightoLow.vue'
import DisplayBooks from './DisplayBooks.vue'
import Cart from './Cart.vue'
export default {
components: {
DisplayBooks,
sortBooksLowtoHigh,
sortBooksHightoLow,
Cart
},
data() {
return {
shownComponent:'DisplayBooks',
booksCount:0,
flag: 'noOrder',
brand: 'Bookstore',
name: '',
flam: true,
visible: true,
}
},
methods: {
flip() {
this.flam = !this.flam;
},
applyOption(evt) {
if (evt.target.value === "HighToLow") {
this.flag = 'highToLow';
} else this.flag = 'lowToHigh';
},
}
}
</script>
DisplayBooks.vue
<template>
<div class="carddisplay-section">
<!-- <div class="mid">
<h6>Books<span class="items">({{books.length}})</span></h6>
</div> -->
<div v-for="book in books" :key="book.id" class="card book" >
<div class="image-section" #mouseover="book.hover = true" #mouseleave="book.hover = false">
<div class="image-container">
<img v-bind:src="book.file" />
</div>
</div>
<div class="title-section" >
{{book.name}}
</div>
<div class="author-section">
by {{book.author}}
</div>
<div class="price-section">
Rs. {{book.price}}<label class="default">(2000)</label>
</div>
<div class="buttons">
<div class="button-groups" v-if="!addedBooks.includes(book.id)">
<button type="submit" #click="handleCart(book.id);toggle(book.id);addedBooks.push(book.id)" class="AddBag">Add to Bag</button>
<button class="wishlist">wishlist</button>
</div>
<div class="AddedBag" v-else>
<button class="big-btn" #click="removeFromCart(book.id);addedBooks=addedBooks.filter(id=>id!==book.id)">Added to Bag</button>
</div>
</div>
<div class="description" v-if="book.hover">
<p class="hovered-heading">BookDetails</p>
{{book.description}}
</div>
</div>
</div>
</template>
<script>
import service from '../service/User'
export default {
mounted() {
service.userDisplayBooks().then(response => {
let data = response.data;
data.map(function(obj) {
obj.hover = false;
return obj;
});
this.books.push(...data);
return response;
})
},
data() {
return {
isActive:true,
result: 0,
authorPrefix: 'by',
pricePrefix: 'Rs.',
defaultStrikePrice: '(2000)',
buttonValue: 'Add to Bag',
buttonWishlist:'wishlist',
buttonAddedBag:'Added to Bag',
flag: true,
state: true,
addedBooks:[],
clickedCard: '',
books: [] // i want to display this array length
}
},
watch:{
addedBooks:{
handler(val){
this.$emit('update-books-count',val.length)
},
deep:true
}
},
methods: {
toggleClass: function(event){
this.isActive = !this.isActive;
return event;
},
toggle(id) {
this.clickedCard = id;
console.log(this.clickedCard);
},
flip() {
this.state = !this.state;
},
Togglebtn() {
this.flag = !this.flag;
},
handleCart(bookId){
let userData={
id: bookId,
}
service.userUpdateCart(userData).then(response=>{
return response;
}).catch(error=>{
alert("error while displaying Books");
return error;
})
},
removeFromCart(bookId){
let userData={
id:bookId,
}
service.userRemoveFromCart(userData).then(response=>{
return response;
}).catch(error=>{
alert("error while removing from cart");
return error;
})
}
}
}
</script>
Using Vue(SPA) in Laravel-8
I am trying to use event and fetch some data from the component but the data is not being fetched and the error that I am getting is:- 404
exception "Symfony\Component\HttpKernel\Exception\NotFoundHttpException"
Here's the Contacts.vue that I am using to send DisplayMessages event request:-
<template>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">Contacts List</div>
<div class="card-body">
<ul v-for="contact in contacts" :key="contact.id" >
<li>
<button #click="displayMesages(contact.id)">
{{contact.name}}
</button>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data(){
return {
contacts: []
}
},
mounted() {
axios.get('/api/contacts-list')
.then(response => this.contacts = response.data)
},
methods: {
displayMesages(id){
console.log(id);
DisplayMessages.$emit('refresh', id);
}
}
}
</script>
Here's the DisplayMessages.vue where the request is being receieved:-
<template>
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">Display Messages</div>
<div class="card-body">
<h2 v-for="message in messages">{{message.message}}</h2>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
// import contacts from './ContactsList.vue';
export default {
data() {
return {
contacts: [],
messages: []
}
},
mounted() {
DisplayMessages.$on('refresh', (id)=>{
axios.get('/api/display-message/'+ id).then(response => this.messages = response.data)
});
}
}
</script>
Here's the route DisplayMessages is fetching:-
Route::get('/api/display-messages/{id}', [App\Http\Controllers\ChatsController::class, 'displayMessages']);
Here's the Controller:-
public function displayMessages($id)
{
return Chats::all();
}
There is a typing mistake in DisplayMessages.vue in axios route:-
mounted() {
DisplayMessages.$on('refresh', (id)=>{
axios.get('/api/display-message/'+ id).then(response => this.messages = response.data) //It should be '/api/display-messages/`
});
}
Does this route actually exist and return something?
/api/contacts-list
in news.twig
{% extends 'layouts.twig' %}
{% block content %}
<section class="ls section_padding_bottom_110">
<div id="cart" class="container">
<cart
v-bind:materials="news"
type="news"
test="materials"
></cart>
<modal></modal>
</div>
<script type="text/x-template" id="modal-template">
<transition name="modal">
<div class="modal-mask" v-if="active" #click="close()">
<div class="modal-wrapper">
<div class="modal-container">
<div class="modal-header">
<h3>${ item.name }</h3>
</div>
<div class="modal-body">
${ item.body }
<br>
modal #${ item.id }
</div>
<div class="modal-footer">
<button class="modal-default-button" #click="close()">
close
</button>
</div>
</div>
</div>
</div>
</transition>
</script>
</section>
{% endblock %}
I have 2 components and 1 Vue in my js.
var Hub = new Vue();
Vue.component(
'modal', {
template: '#modal-template',
delimiters: ['${', '}'],
data: function() {
return {
active: false,
item: {
id: '',
name: '',
body: ''
}
}
},
methods: {
open: function (item) {
this.active = true;
this.item = item;
},
close: function () {
this.active = false;
}
},
mounted: function() {
this.$nextTick(function () {
Hub.$on('open-modal', this.open);
Hub.$on('close-modal', this.close);
}.bind(this));
}
});
Vue.component('cart', {
props: {
materials: { type: Array, required: true},
type: { type: String, required: true}
},
computed: {
isPoints() {
return this.type == 'paymentPoints';
},
isNews() {
return this.type == 'news';
}
},
template : `
<div class="row masonry-layout isotope_container">
<div class="col-md-4 col-sm-6 isotope-item" v-for="item in materials">
<div class="vertical-item content-padding topmargin_80">
<div class="item-media">
<img v-bind:src="item.image" alt="">
<div class="media-links p-link">
<div class="links-wrap">
<i class="flaticon-arrows-2"></i>
</div>
<a v-if="!isNews" v-bind:href="item.image" class="abs-link"></a>
</div>
</div>
<button #click="openModal(item)" #keyup.esc="closeModal()">more</button>
<div class="item-content with_shadow with_bottom_border">
<span v-if="isNews" class="categories-links" style="font-size:20px;">
<a rel="category" href="#modal1" data-toggle="modal">
{{item.name}}
</a>
</span>
<p>{{item.body}}</p>
<div v-if="isPoints">
<hr class="light-divider full-content-divider bottommargin_10">
<div class="media small-icon-media topmargin_5">
<div class="media-left">
<i class="fa fa-map-marker grey fontsize_18"></i>
</div>
<div class="media-body">
{{item.adress}}
</div>
</div>
<div class="media small-icon-media topmargin_5">
<div class="media-left">
<i class="fa fa-phone grey fontsize_18"></i>
</div>
<div class="media-body">
{{item.telephone}}
</div>
</div>
</div>
<div v-if="isNews" class="text-center">
<hr class="light-divider full-content-divider bottommargin_10">
<span class="date">
<i class="flaticon-clock-1 grey"></i>
<time class="entry-date">
{{item.date}}
</time>
</span>
</div>
</div>
</div>
</div>
</div>
`
});
var vm = new Vue({
el: '#cart',
name: 'cart',
delimiters: ['${', '}'],
data: {
complated: [],
continuing: [],
planned: [],
points: [],
infoSlider: [],
news: []
},
methods: {
openModal: function (item) {
Hub.$emit('open-modal', item);
},
closeModal: function () {
Hub.$emit('close-modal');
}
},
created() {
axios.get(url).then(res => {
var proje = res.data.projects[0];
this.complated = proje.complated;
this.continuing = proje.continuing;
this.planned = proje.planned;
this.points = res.data.paymentPoints;
this.infoSlider = res.data.sliderฤฐnfos;
this.news = res.data.news;
})
.catch(e => {
console.log(e);
})
}
});
When I click openModal(item) button give me error ;
Property or method "openModal" 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://v2.vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.
I can not use the openModal function in component.
I can use the function in news.twig without any problems, but then I can not use the component. Can you help me?
You are using openModal in cart component, but that method is defined in root component.
According to Vue's documentation:
Everything in the parent template is compiled in parent scope;
everything in the child template is compiled in the child scope.
in my case need to define variable in vuejs
like this
<script>
export default {
name: "MegaMenu",
props: {
categories: Array,
},
}