i'm trying to do mini shop app in VueJS without backend, only adding items to shopping cart.
I have component One(Nmdgrey.vue), where i have e.g. boots and "Add to cart" button.
<button #click="addToCart">Add to cart</button>
And function in first component:
methods: {
addToCart() {
const boots = { name: 'adidas nmd' };
this.cart.push(boots);
}
}
And i have component Two, where i have shopping cart
<div v-for="item in cart">
{{item.name}}
</div>
And JS
import Nmdgrey from '#/components/Nmdgrey.vue';
export default {
name: 'Shoppingcart',
components:
Nmdgrey,
data() {
return {
cart: [
{ name: 'adidas adizero' },
]
}
},
};
How i can add boots from component One to list in component Two?
I have this.cart.push(boots); in component One but it didn't work
This is what i want but button didn't work: codesandbox
Use $emit, when child components need to communicate with parent.
Refer official doc:Listening-to-Child-Components-Events
Nmdgrey.vue
<template>
<div>
<!-- component 1 -->
<button #click="add">Add to cart</button>
</div>
</template>
<script>
export default {
name: "Numdgrey",
methods: {
add() {
const boots = { name: "adidas nmd" };
this.$emit("add", boots);
}
}
};
</script>
Shoppingcart.vue
<template>
<div>
<!-- component 2 -->
<nmdgrey #add="addCart"></nmdgrey>
<br>
<div v-for="(item, index) in cart" :key="index">{{item.name}}</div>
</div>
</template>
<script>
import Nmdgrey from "./Nmdgrey.vue";
export default {
name: "ShoppingCart",
components: {
Nmdgrey
},
data() {
return {
cart: [{ name: "adidas adizero" }]
};
},
methods: {
addCart(good) {
this.cart.push(good);
}
}
};
</script>
Codesandbox demo : https://codesandbox.io/s/z2qz6oy8yp
From your post I've deduced that what you want to do is to share data from two or more Vue components. For this purpose, you could use Vuex, which provides centralized state management.
This way you could use a vuex mutation to add items to the cart, which could be used from any component. You would also be able to retrieve the cart data from any of them by using vuex getters.
you need to create a post method where you take over everything you need from one page to another page. after that everything will get into the cart and you will be able to create your page.
Codesandbox demo : https://codesandbox.io/s/94l44j8j14
Use props to pass the values to cart component.
Nmdgrey.vue
<template>
<div>
<b>Component 1 : NmdGrey</b><br><br>
<button v-for="(product, index) in boots" :key="index"
#click="addToCart(product.name)" >Add {{product.name}} to Cart</button>
<br><br><hr><br>
<shoppingcart :cart="cart" />
</div>
</template>
<script>
import shoppingcart from './shoppingcart.vue';
export default {
name: 'Nmdgrey',
components:{shoppingcart},
data() {
return {
boots:[{name: 'adidas adizero'}, {name: 'puma walker'}, {name: 'nike shoe'}, {name: 'adidas plain'}],
cart:[],
}
},
methods: {
addToCart(boots) {
this.cart.push({ name: boots });
}
}
}
</script>
Shoppingcart.vue
<template>
<div>
<b>Component 2 : Shopping cart</b>
<br>
<br>
<div v-for="(product, index) in cart" :key="index">{{product.name}}</div>
</div>
</template>
<script>
export default {
name: "Shoppingcart",
props: ["cart"],
data() {
return {};
}
};
</script>
Well, If your application is small, then you can create Vue mixins for addToCart and call it whenever you'll require in your component.
Similar to the methods, you can share data across the components with the use of mixins.
Here is the mixins official docs
Here is the working JsFiddle
Hope this helps!
Related
HelloWorld.vue
<template>
<div>
<b>Vuejs dynamic routing</b>
<div v-for="item in items" :key="item.id">
<b>{{ item.id }}.</b>
<router-link :to="{ name: 'UserWithID', params: { id: item.id } }">
{{ item.kk }}
</router-link>
<router-link name="twoval"></router-link>
</div>
<br /><br /><br />
<User />
<Usertwo />
</div>
</template>
<script>
import User from "./User.vue";
import Usertwo from "./Usertwo.vue";
import { datalist } from "./datalist";
export default {
name: "HelloWorld",
components: {
User,
Usertwo,
},
data() {
return {
items: datalist,
};
},
};
</script>
User.vue
<template>
<div>
<div v-for="(item, key) in user" :key="key">
{{ item }}
</div>
</div>
</template>
<script>
import { datalist } from "./datalist";
export default {
name: "User",
data() {
return {
lists: datalist,
};
},
computed: {
user: function () {
return this.lists.find((item) => item.id === this.$route.params.id);
},
},
};
</script>
Usertwo.vue
<template>
<div>
<div v-for="usertwo in usertwos" :key="usertwo.mid">
{{ usertwo.doctor }}
</div>
</div>
</template>
<script>
import { datalisttwo } from "./datalisttwo";
export default {
name: "User",
data() {
return {
usertwos: datalisttwo,
};
},
};
</script>
main.js
import Vue from "vue";
import App from "./App.vue";
import VueRouter from "vue-router";
import HelloWorld from "./components/HelloWorld.vue";
Vue.use(VueRouter);
const router = new VueRouter({
routes: [
{ path: "/", name: "User", component: HelloWorld },
{ path: "/:id", name: "UserWithID", component: HelloWorld }
]
});
Vue.config.productionTip = false;
new Vue({
router,
render: (h) => h(App)
}).$mount("#app");
Logic trying to achieve, If i click on router-link id-1 from helloworld.vue component, then in User.vue and Usertwo.vue component. I need to show only array values list which is linked with id-1 only. from two arrray value list based on id.
Similarly on whatever the id, i click on from router-view from helloworld.vue. Same id value, i need to show inside the User and Usertwo.vue component.
Now only issue wit code is, Usertwo array value not loading correctly
I tried below code for that logic, But i couldn't make it.
This is my complete code:- https://codesandbox.io/s/pensive-williamson-n9bi6?file=/src/main.js:0-434
Something like this: https://codesandbox.io/s/white-bird-z6orf
Add props to the components
Send the id from the params to the components
Filter the list
I am developing a Vue app with pimcore and twig in the backend. I have to create a component that receives the slot (another component), and render it inside, but with dynamic props.
Here is root in viani.twig.html:
<div>
<viani-accordion>
<viani-filter-checkbox v-slot="{filterRows}"></viani-filter-checkbox>
</viani-accordion>
</div>
There is nothing special. viani-accordion is a parent component and the viani-filter-checkbox is a slot, which I have to render with appropriate props.
Here you can see the VianiAccordion.vue:
<template>
<div class="wrapper">
<AccordionTitle v-for="(item, index) in dataToRender" :item="item" :key="index">
/*I think this is incorrect, but I'm trying to prop data that I need in viani-filter-checkbox*/
<slot :filter-rows="item.items"></slot>
</AccordionTitle>
</div>
</template>
<script>
import AccordionTitle from './Accordion-Title';
export default {
name: "Viani-Accordion",
components: {AccordionTitle},
data() {
return {
dataToRender: [
{
name: 'first item',
items: [
{
name: 'oil olive',
quantity: 10,
},
{
name: 'subitem 2',
quantity: 11,
},
]
},
{
name: 'second item',
items: [
{
name: 'subitem 1',
quantity: 10,
},
{
name: 'subitem 2',
quantity: 11,
},
]
}
]
}
},
}
</script>
Then I have another deeper child component Accordion-Title.vue that is responsible for rendering the slot (so I have to pass the slot through the multiple components):
<template>
<div v-if="isOpen" class="child-wrapper">
/*I think this is incorrect, but I'm trying to prop data that I need in viani-filter-checkbox*/
<slot :filterRows="item.items"></slot>
</div>
</template>
<script>
export default {
name: "Accordion-Title",
props: {
item: {
type: Object,
default: null
}
}
}
</script>
and finally Viani-FiltersCheckbox.vue:
<template>
<div>
//child component which we don't need in this case
<FilterCheckboxRow v-for="(item, index) in filterRows" :item="item" :key="index"/>
</div>
</template>
<script>
import FilterCheckboxRow from './FilterCheckboxRow'
export default {
name: "VianiFilterCheckbox",
components: {
FilterCheckboxRow
},
props: {
//here I expect to get array to render, but got default value (empty array)
filterRows: {
type: Array,
default: function () {
return []
}
},
},
}
</script>
So I need to pass the props (filterRows) to the component (Viani-FiltersCheckbox.vue), which is rendered as a slot. I have read this and this, but still don't get where the mistake and how to get the props I need.
It looks like you're trying to access your props through props.XXX. That's typically only done in templates for functional components. Otherwise, the props would be accessed without the props. prefix (i.e., props.item.items should be item.items).
And to pass filterRows from the scope data to the child component, declare a <template>, and then move your child into that, binding filterRows there:
<viani-accordion>
<!-- BEFORE: -->
<!-- <viani-filter-checkbox v-slot="{filterRows}"></viani-filter-checkbox> -->
<template v-slot="{filterRows}">
<viani-filter-checkbox :filterRows="filterRows"></viani-filter-checkbox>
</template>
</viani-accordion>
I implemented a component that have a select element inside and it is like following more or less:
<!-- child component -->
<template>
<b-form-select
v-model="selectedProject"
:options="projects"
#change="changedValue">
<template v-slot:first>
<option :value="null" disabled>-- Please select a project --</option>
</template>
</b-form-select>
</template>
<script>
export default {
name: 'AllocationItem',
props: {
projects: {
type: Array,
default: () => [{ value: Number}, { text: String}]
}
},
data() {
return {
selectedProject: null,
}
},
methods: {
changedValue(value) {
this.selectedProject = value;
}
}
}
</script>
I use this component in a parent componenent where it can be possible add other AllocationItem clicking on a button.
To do that i have used an array where i push a new item every time that there is a click on add button (i don't know if it's the right way...)
Follow parent component code:
<!-- parent component -->
<template>
<b-button class="btnAction" #click="addItem()">Add</b-button>
<b-button class="btnAction" #click="sendAllocation()">Send</b-button>
<b-row v-for="allocation in resourceItem.allocations" v-bind:key="allocation.id">
<allocation-item v-bind:projects="projects"></allocation-item>
</b-row>
</template>
<script>
export default {
name: 'Management',
components: {
AllocationItem
},
data() {
return {
allocations: []
}
},
methods: {
addItem() {
this.allocations.push(AllocationItem);
},
sendAllocation() {
this.allocations.forEach((allocation) => {
// I WOULD LIKE TO HAVE ALL SELECTED VALUE HERE!!!
});
},
},
created() {
const dataProjects = this.getProjectsData();
dataProjects.then(response => {
this.projects = response.map((item) => {
return {value: item.id, text: item.name}
})
});
}
</script>
In my application i have another button, send button, that should be read values selected in all child component (allocation-item).
How can i do to have an array with that selected values?
Thank you in advance to all
This depends on the relationship from your 'Send Button'-component with your parent component.
You can either:
Use the $emit() method to propagate data up the component tree to the shared parent component. Then prop it down to the 'Send Button'-component.
Have a single source of truth by using Vuex. This is a store that keeps all your data in a centralised object.
Perhaps you can provide us with more information on the project structure?
First of all, ask yourself if this component is being used anywhere else but here. If you only use it once, build it into the parent component and your problems are solved.
Otherwise I'd go with #laurensvm's approach of using emit or Vuex.
After some researches on google i have founded a solution. I don't know if it the right way but it works fine and it seems clean.
My solution consists to use emit on child component and v-model on parent like show following example.
<!-- child component -->
<template>
<b-form-select
v-model="selectedProject"
:options="projects"
#change="changedValue">
<template v-slot:first>
<option :value="null" disabled>-- Please select a project --
</option>
</template>
</b-form-select>
</template>
<script>
export default {
name: 'AllocationItem',
props: {
projects: {
type: Array,
default: () => [{ value: Number}, { text: String}]
},
value: {
type: Number
}
},
data() {
return {
selectedProject: this.value
}
},
methods: {
changedValue(value) {
this.$emit('input', value);
}
}
}
</script>
And on parent using an array variable on v-model.
Something like this:
<!-- parent component -->
<template>
<b-container>
<b-row>
<b-button class="btnAction" variant="success" #click="addItem(index)">Add</b-button>
<b-button class="btnAction" #click="sendAllocation(index)">Send</b-button>
</b-row>
<b-row v-for="(allocation, index) in resourceItem.allocations" v-bind:key="allocation.id">
<allocation-item v-bind:projects="projects" v-model="allocationItemSelected[index]"></allocation-item>
</b-row>
</b-container>
</template>
See a runnable example clicking on codesandbox link below:
https://codesandbox.io/s/vue-template-4f9xj?autoresize=1&expanddevtools=1&fontsize=14&hidenavigation=1&moduleview=1
I have two pages routed:
Home > Results
Inside 'Home', I have a button/link which is not in <route-view/> and I want this button to redirect to Results.vue passing a parameter.
This parameter named activeTab, has to open the desired vue-tabs, which I can't accomplish, because it's getting nothing from variable:
code:
Home.vue
<div class="row">
<Notes refName="Relationship" />
<Notes refName="Support" />
</div>
...
<script>
import Notes from '#/components/Monthlynotes.vue'
export default {
name: 'home',
components: {
Notes
},
</script>
/components/Monthlynotes.vue
<b-card>
<p class="card-text">{{ refName }}</p>
<b-link class="right" :activeTab="refName" href="/results">More</b-link>
</b-card>
...
<script>
export default {
props: {
refName: String,
},
</script>
Results.vue
<vue-tabs type="pills" v-model="tabName">
<v-tab title="Relationship">
<RelDT msg="Results view"/>
</v-tab>
<v-tab title="Support">
<SupDT msg="Results view"/>
</v-tab>
</vue-tabs>
...
<script>
import RelDT from '#/components/DataTable.rel.vue'
import SupDT from '#/components/DataTable.sup.vue'
export default {
name: 'results',
props: {
activeTab: String
},
components:
{
RelDT,
SupDT,
},
data() {
return {
tabName: activeTab
}
}
}
</script>
App
<router-link :to="{name:'results', param:{activeTab}}">Results</router-link>
How can I make this <b-link> route if it was a <route-link />?
Even the b-link component supports the :to property. To be found here
The value of the property will be passed to router.push().
<b-link :to="{name:'results', param:{activeTab}}">Redirect me</b-link>
In my vue.js app, I need to display a list of items which the user can click.
When clicked, each of these items should then fire a modal, containing additional information about the item that the user just clicked.
What I have so far in my Items.vue component is:
<template>
<div id="content">
<li v-for="item in items" class="list-item pa2 ba">
<div class="f5 pt2 pb2">
<span>{{item.name}}</span>
</div>
</li>
</div>
</template>
<script>
import Items from '#/api/items';
export default {
name: 'items',
asyncComputed: {
items: {
async get() {
const items = await Items.getAll();
return items.data;
},
default: []
}
},
components: {}
}
</script>
Now, I could simply add a modal component to the v-for loop, thus creating one modal for each item, but this does not seem ideal if, for example, I have a list of thousands of items.
This led me to believe that the modal should probably be placed at the root of the app (in my case App.vue), like this:
<template>
<div id="app">
<modal></modal>
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'app'
}
</script>
and then somehow fired with custom data whenever I needed it.
However, I'm not sure how to proceed. How do I fire this modal with custom information from inside the v-for loop, which is in a child component relative to App.vue?
These two links helped me figure this out:
https://v2.vuejs.org/v2/examples/modal.html
https://laracasts.com/discuss/channels/vue/passing-data-form-v-for-loop-to-modal-component
#In your Parent Component
You don't have to create a modal for each item within the v-for loop, simply include the modal-component at the beginning of your parent and then work with v-if="..." and props.
<template>
<div>
<modal v-if="modalVisible" #close="modalVisible = false" :data="modalData"/>
<div v-for="item in items">
<button type="button" #click="openModal(item)">Open Modal</button>
</div>
</div>
</template>
and then in your script:
import modal from './Modal'
export default {
components: {
modal
},
data() {
return {
modalVisible: false,
modalData: null
}
},
methods: {
openModal(data) {
this.modalData = data
this.modalVisible = true
},
}
#In your child (modal) component
In your modal you can now do the following:
Template:
<template>
{{ data.foo }}
<button #click="$emit('close')">Cancel</button>
</template>
Script
<script>
export default {
props: ['user']
};
</script>
Hope that helps :-)