How to display many times the same component with vuex link inside - vue.js

I'm new to Vue and Vuex.
I made a component with radio button, and this component is two-way bind with the store.
Now that the component is working, i would like to use it for multiple item by calling it several time, and indicates to it which item of the vuex store it should be bound to. In this way, i'll be able to loop of the array containing all item on which apply the radio button modification.
In my store, I have this array :
state {
[...other data...]
arrayX {
item1: { name : 'a', value : 'nok'}
item2: { name : 'b', value : 'ok}},
[...other data...]
This array is bind with v-model inside the component :
<template>
<div class="radio">
<input type="radio" name="a" value="nok" v-model="arrayX.item1.value" id="nok"/>
<label for="nok"> Nok </label>
</div>
<div class="radio">
<input type="radio" name="a" value="" v-model="arrayX.item1.value" id="empty"/>
<label for="empty">None</label>
</div>
<div class="radio">
<input type="radio" name="a" value="ok_but" v-model="arrayX.item1.value" id="ok_but"/>
<label for="ok_but">Ok but</label>
</div>
<div class="radio">
<input type="radio" name="a" value="ok" v-model="arrayX.item1.value" id="ok"/>
<label for="ok">Ok</label>
</div>
</div>
</template>
<script>
import { mapFields } from 'vuex-map-fields';
export default {
name: 'SwitchColors',
computed:{
...mapFields(['arrayX' ])
}
}
</script>

I created an example scenario in my Vue 2 CLI sandbox app. Each radio button component is bound to the Vuex store array element through a computed property. I pass the store array index value as a prop from the parent to the radio component.
RadioButtonVuex.vue
<template>
<div class="radio-button-vuex">
<hr>
<div class="row">
<div class="col-md-6">
<input type="radio" value="One" v-model="picked">
<label>One</label>
<br>
<input type="radio" value="Two" v-model="picked">
<label>Two</label>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
pickedIndex: {
type: Number,
required: true
}
},
computed: {
picked: {
get() {
return this.$store.state.radioSelections[this.pickedIndex].picked;
},
set(newValue) {
this.$store.commit('updateSelection', { index: this.pickedIndex, newValue: newValue });
}
}
},
}
</script>
<style scoped>
label {
margin-left: 0.5rem;
}
</style>
Parent.vue
<template>
<div class="parent">
<h3>Parent.vue</h3>
<radio-button-vuex :pickedIndex="0" />
<radio-button-vuex :pickedIndex="1" />
</div>
</template>
<script>
import RadioButtonVuex from './RadioButtonVuex.vue'
export default {
components: {
RadioButtonVuex
}
}
</script>
store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
export default new Vuex.Store({
state: {
radioSelections: [
{
picked: 'One'
},
{
picked: 'Two'
}
]
},
mutations: {
updateSelection(state, payload) {
state.radioSelections[payload.index].picked = payload.newValue;
}
}
})

Related

Pass object to component - vue js

I've tried a few different things and now must ask for some help (thanks in advance).
I'm creating a simple Vue js app where a component form emits data as a object (this works) to its parent.
the parent then runs a v-for loop and passes the Object data to a component which displays the data (this does not work).
Vue Version
($ vue --version
#vue/cli 4.4.1)
Parent code:
<template>
<div class="MEDS">
<goalForm #goal_data="goal_create($event)"/>
<section>
<div v-for="(goalItem, index) in this.goals_list_container" v-bind:key="index">
<goalItem :goal="goalItem"/>
</div>
</section>
</div>
</template>
// # is an alias to /src
import goalForm from '#/components/Goal_form.vue'
import goalItem from '#/components/Goal_item.vue'
export default {
name: 'MEDS',
components: {
goalForm,
goalItem
},
data(){
return{
// Recievs goals from goalForm on submit, this is an array of objects
goals_list_container: [],
}
},
methods: {
goal_create(payload){
this.goals_list_container.push(payload);
console.log(this.goals_list_container)
}
}
}
GOAL ITEM (that should display the goal)
<template>
<div>
<h3>{{goal.title}}</h3>
</div>
</template>
export default {
prop: ['goal'],
data(){
return {
goal: {}
}
},
watch: {
}
}
Goal_form: (EDITED)
<div>
<form action="" class="goalForm" #submit.prevent="emit_Goal">
<input type="text" name="title" id="title" v-model="title">
<input type="text" name="description" id="description" v-model="des">
<input type="date" name="dueDate" id="dueDate" v-model="dueDate">
<input type="number" name="priority" id="priority" v-model="priority">
<input type="submit" class="submit">
</form>
</div>
</template>
<script>
export default {
data() {
return {
title: null,
des: null,
dueDate: null,
priority: null,
goal_data: {}
}
},
methods: {
push_goal(){
this.goal_data.title = this.title
this.goal_data.des = this.des
this.goal_data.dueDate = this.dueDate
this.goal_data.priority = this.priority
},
emit_Goal(){
// Move goal details into Object to be Emited
this.push_goal()
// Emit the form to the parent on submit
this.$emit('goal_data', this.goal_data)
}
}
}
</script>
<style lang="scss" scoped>
form {
display: flex;
flex-direction: column;
input {
&[name="title"]{
}
}
}
</style>
The for loop seems to work as each submit of the Goal_form creates a new instance of the Goal_item component.... But it does not populate with any data.
I Am either getting this totally wrong or have missed something small but any help would be greatly appreciated -
W

It's possible to pass an object which has some methods to the child component in the Vue 3 JS

Parent component has the method "startMethods" which just also has some other method "onDecrementStart ". OnDecrementStart method just only call Alert.
Under this line, added a code example but that code didn't work.
<template>
<div class="parent">
<div class="main">
<img alt="Vue logo" src="../assets/logo.png">
<SettingsBoard :max="maxValue" :start="startValue"
:startInc="startMethods"
/>
</div>
</div>
</template>
<script>
import SettingsBoard from "#/components/SettingsBoard";
export default {
name: "Main",
components: {
SettingsBoard
},
methods: {
startMethods: {
onDecrementStart () {
alert('Decrement')
},
onIncrementStart () {
alert('Incriment')
}
},
}
}
</script>
SettingsBoard component
<template>
<div class="container">
<label>
<button :#click="startInc.onDecrementStart()">-</button>
</label>
</div>
</template>
<script>
export default {
name: "SettingsBoard",
props: {
startInc: Object
},
}
</script>
I want to get like that if it's possible.
<template>
<div class="container">
<label>
<button :#click="startInc.onDecrementStart()">-</button>
<button :#click="startInc.onIncrementtStart()">+</button>
</label>
</div>
</template>
<script>
export default {
name: "SettingsBoard",
props: {
startInc: Object
},
}
</script>
To run a method in the parent component you should emit a custom event which has the parent method as handler :
<label>
<button #click="$emit('decrement')">-</button>
<button #click="$emit('increment')">+</button>
</label>
in parent component :
<div>
<SettingsBoard
#decrement="onDecrementStart"
#increment="onIncrementStart"
/>
...
methods: {
onDecrementStart() {
this.count--;
},
onIncrementStart() {
this.count++;
},
},
LIVE EXAMPLE

How to pass data between components when using Vue-Router

How do I pass data from one component to another when using Vue router?
I'm building a simple CRUD app that have different components.
My conponents are:
App.vue - where I render the router-view
Contacts.vue - where I have the array of objects of contacts
ContactItem.vue - handle how the contact is displayed (gets contact as a prop from contact.vue
AddContact.vue - add new contact
EditContact.vue - edit selected contact
On the AddContact component, I have a form the user fills and then clicks on the submit button to add the form to the main component in Contacts.vue but when I emit an event and call it on the Contacts.vue component, it doesn't work. I get no output but from devtools I can see the event was triggered from AddContact.vue component.
Here is the Github link
<!-- App.vue -->
<template>
<div>
<Navbar />
<div class="container">
<router-view #add-contact="addContact" />
</div>
</div>
</template>
<script>
import Navbar from "./components/layout/Navbar";
export default {
components: {
Navbar
}
};
</script>
<!-- Contacts.vue -->
<template>
<div>
<div v-for="(contact) in contacts" :key="contact.id">
<ContactItem :contact="contact" />
</div>
</div>
</template>
<script>
import ContactItem from "./ContactItem";
export default {
components: {
ContactItem
},
data() {
return {
contacts: [
{
id: 1,
name: "John Doe",
email: "jdoe#gmail.com",
phone: "55-55-55"
},
{
id: 2,
name: "Karen Smith",
email: "karen#gmail.com",
phone: "222-222-222"
},
{
id: 3,
name: "Henry Johnson",
email: "henry#gmail.com",
phone: "099-099-099"
}
]
};
},
methods: {
addContact(newContact) {
console.log(newContact);
this.contacts = [...this.contacts, newContacts];
}
}
};
</script>
<!-- AddContact.vue -->
<template>
<div>
<div class="card mb-3">
<div class="card-header">Add Contact</div>
<div class="card-body">
<form #submit.prevent="addContact">
<TextInputGroup
label="Name"
name="name"
placeholder="Enter your name..."
v-model="name"
for="name"
/>
<TextInputGroup
type="email"
label="Email"
name="email"
placeholder="Enter your email..."
v-model="email"
/>
<TextInputGroup
type="phone"
label="Phone"
name="phone"
placeholder="Enter your phone number..."
v-model="phone"
/>
<input type="submit" value="Add Contact" class="btn btn-block btn-light" />
</form>
</div>
</div>
</div>
</template>
<script>
import TextInputGroup from "../layout/TextInputGroup";
export default {
components: {
TextInputGroup
},
data() {
return {
name: "",
email: "",
phone: ""
};
},
methods: {
addContact() {
const newContact = {
name: this.name,
email: this.email,
phone: this.phone
};
this.$emit("add-contact", newContact);
}
}
};
</script>
Well there are more ways to send data from component to component:
You could create a new Vue instance in your main.js file and call it eventBus
export const eventBus = new Vue();
After that you can import this bus wherever you need it:
import { eventBus } from "main.js"
Then you can send events over this global bus:
eventBus.$emit("add-contact", newContact);
At the other component you need to import this bus again and listen to this event in your "created" lifecycle:
created(){
eventBus.$on("add-contact", function (value){
console.log(value)
})
}
The other way is to store it centralized in vuex state. With "created" you can call this data. Created gets executed after your vue instance is created

How to filter data from a parent component in a child component?

I have a component who show a list of data and I want to filter this list with different filters who are in a child component
I managed to do it with "computed" but only when I put everything in the parent component.
Test.vue
<template>
<div>
<filtres />
<garage v-for="g in garages" v-bind:gar="g" :key="g.id" />
</div>
</template>
<script>
export default {
name: 'test',
components: {
simplecomposant,
garage,
filtres
},
data(){
return{
garages: [],
}
},
mounted(){
var self = this;
axios.get('FETCHAPI').then(function (response)
{
self.garages = response.data.datas;
});
},
}
</script>
Filtres.vue
<template>
<div>
<label><input type="radio" v-model="selectedCity" value="All" /> All</label>
<label><input type="radio" v-model="selectedCity" value="1" /> City 1</label>
<label><input type="radio" v-model="selectedCity" value="2" />City 2</label>
</div>
</template>
<script>
export default {
name : 'filtres',
data(){
return{
selectedCity : "All"
}
},
}
</script>
garage.vue
<template>
<li class="aeris-simple-li">{{gar.name}}</span></li>
</template>
<script>
export default {
name : 'garage',
props: {
gar: {
type: Object
}
},
}
</script>
I want that when I select a filter (all, city 1, ...) it filters the datas "garages" who are in the parent component.

Access infromation of grandparent component from grandchild component in vue.js

I have a parent component in Vue called RecipeView, and it is an inline-component. inside it, i have these components:
comments. and inside comments, i have comment and NewCommentForm, has it shows in the picture below.
I am passing in the RecipeView component the id as a prop, and would like to access it in the NewCommentForm component in order to set an endpoint that i will post to and save the comment.
This is the RecipeView component:
<recipe-view :id="{{$recipe->id}}">
<comments :data="{{$recipe->comment}}"#added="commentsCount++"></comments>
</recipe-view>
and the script for it is this:
<script>
import Comments from '../components/Comments.vue';
export default {
props: ['initialCommentsCount','id'],
components: {Comments},
data(){
return {
commentsCount: this.initialCommentsCount,
recipe_id:this.id
};
}
}
</script>
The comments component looks like this:
<template>
<div>
<div v-for="comment in items">
<comment :data="comment"></comment>
</div>
<new-comment-form :endpoint="'/comments/**Here should go the id from parent RecipeView component**'" #created="add"></new-comment-form>
</div>
</template>
<script>
import Comment from './Comment.vue';
import NewCommentForm from './NewCommentForm.vue';
export default {
props: ['data'],
components: {Comment, NewCommentForm},
data() {
return {
items: this.data,
endpoint: ''
}
},
methods: {
add(comment) {
this.items.push(comment);
this.$emit('added');
}
}
}
</script>
and this is the NewCommentForm component:
<template>
<div>
<div class="field">
<p class="control">
<input class="input"
type = "text"
name="name"
placeholder="What is your name?"
required
v-model="name">
</p>
</div>
<div class="field">
<p class="control">
<textarea class="textarea"
name="body"
placeholder="Have your say here..."
required
v-model="body">
</textarea>
</p>
</div>
<button type="submit"
#click="addComment"
class="button is-medium is-success">send</button>
</div>
</template>
<script>
export default {
props:['endpoint'],
data(){
return {
body:'',
name:'',
}
},
methods:{
addComment(){
axios.post(this.endpoint, {
body:this.body,
name: this.name
}).then(({data}) => {
this.body = '';
this.name = '';
this.$emit('created', data);
});
}
}
}
</script>
Thanks for the help.