How to filter data from a parent component in a child component? - vue.js

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.

Related

The Value of Variables does not update inside components

When I put the input field inside a component, it doesn't update the value by any means. Is there something I've missed? Btw, I use Vue 3.
Child Component (input-form.vue)
<template>
<input
type="text"
:value="searchField"
#input="$emit('update:searchField', $event.target.value)"
/>
<p>Text Inside: {{ searchField }}</p>
</template>
<script>
export default {
emits: ["update:searchField"],
props: {
searchField: {
type: String,
},
},
};
</script>
Parent
<template>
<div>
<input-form :searchField="searchField" />
<p>Text Outside: {{ searchField }}</p>
</div>
</template>
<script>
import inputForm from "components/input-form.vue";
export default {
data() {
return {
searchField: "",
};
},
components: {
inputForm,
},
};
</script>
You are not listening for the update:searchField event in the Parent.
<input-form :searchField="searchField" #update:searchField="searchField = $event" />
or
<input-form v-model:searchField="searchField" />

Why is component shown just for a fraction of a second?

As a beginner in Vue, I am struggling with a problem that - even this should not be too hard - I can't solve. The principle is as follows:
I want to create a survey that consists of different topics. The user should be able to choose between these topics (component A and component B). This works fine.
But: When I click on the button "Show Component C", this component is only displayed for a fraction of a second. Why is this, what mistake have I made and how can I solve the problem?
Thanks a lot for your help!
App.vue
<button #click="setSelectedComponent('ComponentA')">Component A</button>
<button #click="setSelectedComponent('ComponentB')">Component B</button>
<component-b
v-if="selectedComponent === 'ComponentB'"
> </component-b>
<component-a
v-if="selectedComponent === 'ComponentA'"
></component-a>
<start
v-if="selectedComponent === 'form-empty'"
></start>
</template>
<script>
import ComponentB from './components/ComponentB.vue';
import ComponentA from './components/ComponentA.vue';
import Start from './components/Start.vue';
export default {
components: {
ComponentB,
ComponentA,
Start,
},
data() {
return {
selectedComponent: 'form-empty',
}
},
methods: {
setSelectedComponent(cmp) {
this.selectedComponent = cmp;
},
}
}
</script>
Start.vue
<template>
<form>
<div>
<h1>Which Component Do You Want To Select?</h1>
</div>
</form>
</template>
**
Component A
<template>
<form>
<h1>Component A</h1>
<div class="form-control">
<input type="range" min ="0" max="100" v-model=value>
</div>
<div>
<button #click="evaluateForm">Save Data</button>
</div>
<h4>Value: {{value}}</h4>
</form>
<component-c v-if="varia === 'yes'"></component-c>
</template>
<script>
import ComponentC from './ComponentC.vue';
export default {
components: {
ComponentC
},
methods: {
evaluateForm() {
this.varia='yes'
}
},
computed: {
result() {
return parseInt(this.abc) + parseInt(this.cde)
}
},
data() {
return {
value: '',
varia: ''
}
}
}
</script>
Component B
<template>
<form>
<h1>Component B</h1>
<div>
<input type="range" min ="0" max="100" v-model=value>
</div>
<div>
<button #click="evaluateForm">Save Data</button>
</div>
<h4>Value: {{value}}</h4>
</form>
<component-c v-if="varia === 'yes'"></component-c>
</template>
<script>
import ComponentC from './ComponentC.vue';
export default {
components: {
ComponentC
},
methods: {
evaluateForm() {
this.varia='yes'
}
},
computed: {
result() {
return parseInt(this.abc) + parseInt(this.cde)
}
},
data() {
return {
value: '',
varia: ''
}
}
}
</script>
Component C
<template>
<form>
<div class="form-control">
<h1>The value is: </h1>
</div>
</form>
</template>
I tested this out locally and the problem comes from the fact that you are using <form> in ComponentA and ComponentB. If you switch those to <div> or <form #submit.prevent> you'll see that it works as you expected.
Here is some documentation on the <form> element to learn more about how it works: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form
Component A
<template>
<div>
<h1>Component A</h1>
<div>
<input type="range" min ="0" max="100" v-model=value>
</div>
<div>
<button #click="evaluateForm">Save Data</button>
</div>
<h4>Value: {{value}}</h4>
</div>
</template>
<script>
export default {
methods: {
evaluateForm() {
this.$emit('xxx');
}
},
computed: {
result() {
return parseInt(this.abc) + parseInt(this.cde)
}
},
data() {
return {
value: '',
}
}
}
</script>
App.Vue
<template>
<button #click="setSelectedComponent('ComponentA')">Component A</button>
<button #click="setSelectedComponent('ComponentB')">Component B</button>
<component
:is="selectedComponent"
></component>
<component-a
#xxx="startComponentC"
v-if="varia === 'yes'"
></component-a>
<component-c
v-if="varia === 'yes'"
></component-c>
</template>
<script>
import ComponentB from "./components/ComponentB.vue";
import ComponentA from "./components/ComponentA.vue";
import Start from "./components/Start.vue";
export default {
components: {
ComponentB,
ComponentA,
Start,
},
data() {
return {
selectedComponent: "start",
varia:""
};
},
methods: {
setSelectedComponent(cmp) {
this.selectedComponent = cmp;
},
startComponentC() {
this.varia="yes"
this.selectedComponent="stopConditionforComponentAandB"
}
},
};
</script>

How to change parent data from a child component?

After doing some digging, it seems that from child to parent you should update data by emitting events (not by v-model). This is my attempt of doing that (to no avail).
App.vue
<template>
<div>
<HelloWorld :count="count" #update:count="count= $event" />
</div>
</template>
<script>
import HelloWorld from "./components/HelloWorld.vue";
export default {
name: "App",
data() {
return {
count: 10
};
},
components: {
HelloWorld
}
};
</script>
HelloWorld.vue
<template>
<div class="hello">
<input
type="number"
min="0"
:value="count"
#input="$emit('input', $event.target.value)"
style="width:6em"
/>
</div>
</template>
<script>
export default {
name: "HelloWorld",
props: {
count: Number
}
};
</script>
Where am I going wrong? Ideally, I would like that changes in the input field of the child component would change the deposit field of the parent. Thanks!
I think that doesn't work because you're missing emits: ['update:count'] option in child component, But I recommend to name the prop as modelValue in child component and use v-model directive in parent instead of #update:count event :
<template>
<div>
<HelloWorld v-model="count" />
</div>
</template>
<script>
import HelloWorld from "./components/HelloWorld.vue";
export default {
name: "App",
data() {
return {
count: 10
};
},
components: {
HelloWorld
}
};
</script>
HelloWorld.vue :
<template>
<div class="hello">
<input
type="number"
min="0"
:value="count"
#input="$emit('update:modelValue', $event.target.value)"
style="width:6em"
/>
</div>
</template>
<script>
export default {
name: "HelloWorld",
props: {
modelValue: Number
},
emits: ['update:modelValue'],
};
</script>
This allows you to create a custom input

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

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.