It's possible to pass an object which has some methods to the child component in the Vue 3 JS - vue.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

Related

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 display many times the same component with vuex link inside

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;
}
}
})

Vuejs passing function from parent to child

I have a beginner question about passing function from parent to child. In my example, I want to to use the child more times and sometimes it should to do someting else v-on:focus. How can i do that? There are options to pass it with prop but i don't know how and i think it's not good to do it ? Maybe with EventBus and if yes then how ? I want to know the right way how to do it in VueJs.
Here is the Parent Component:
import Child from "./child.js";
export default {
name: "app",
components: {
Child
},
template: `
<div>
<child></child>
<child></child>
<child></child>
</div>
`
};
And here is the child Component:
export default {
name: "test",
template: `
<div class="form-group">
<div class="input-group">
<input v-on:focus="functionFromChild">
</div>
</div>
`,
methods: {
functionFromChild() {
//run the function from parent
}
}
};
You can pass the function as any other prop
import Child from "./child.js";
export default {
name: "app",
components: {
Child
},
methods: {
calledFromChild(id){
console.log(id)
}
},
template: `
<div>
<child :callback="calledFromChild" id="1"></child>
<child :callback="calledFromChild" id="2"></child>
<child :callback="calledFromChild" id="3"></child>
</div>
`
};
And then in the child
export default {
name: "test",
props: ["callback", "id"],
template: `
<div class="form-group">
<div class="input-group">
<input v-on:focus="() => this.calledFromChild(this.id)">
</div>
</div>
`,
}
I'm also adding an id to the child so you know which child is making the call.
But this is not a good idea. You should use emit from your child to send an event, and listen to it from the parent.
In the child
export default {
name: "test",
template: `
<div class="form-group">
<div class="input-group">
<input v-on:focus="handleFocus">
</div>
</div>
`,
methods: {
handleFocus() {
this.$emit('focusEvent')
}
}
};
And in the parent
<child #focusEvent="handleFocusFromChild"></child>
A working example here
This should work:
const Child = {
template: `
<div class="form-group">
<div class="input-group">
<input v-on:focus="functionFromChild">
</div>
</div>
`,
props: {
functionFromParent: Function
},
methods: {
functionFromChild: function() {
this.functionFromParent();
}
},
data() {
return {
message: 'Oh hai from the component'
}
}
}
const App = {
template: `
<div>
<h1>Quick test</h1>
<p>{{ message }}</p>
<Child :functionFromParent="functionOnParent"/>
<Child :functionFromParent="functionOnParent"/>
<Child :functionFromParent="functionOnParent"/>
</div>
`,
components: {Child},
methods: {
functionOnParent: function(){
console.log("there we go");
}
},
data() {
return {
message: 'Hello'
}
}
}
new Vue({
render: h => h(App),
}).$mount('#app')
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
</div>
If you're trying to call a function in the parent from the child component, then try
this.$parent.parentMethod()
This will invoke the method in parent component.

Why can't you add a Vue.js reactive property directly to root model?

Here is some code that uses $set() to add a new reactive prop to the model. It works fine.
<template>
<div id="app">
<div>
Prop1: {{ x.prop1 }}
</div>
<div>
<input type="button" value="Go" #click="go()">
</div>
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
x: {}
};
},
methods: {
go() {
this.$set(this.x, 'prop1', 'yay');
}
}
};
</script>
Now, if I remove the x root property and try to add prop1 directly to the this it doesn't work.
<template>
<div id="app">
<div>
Prop1: {{ prop1 }}
</div>
<div>
<input type="button" value="Go" #click="go()">
</div>
</div>
</template>
<script>
export default {
name: 'app',
data() {
return {
};
},
methods: {
go() {
this.$set(this, 'prop1', 'yay');
}
}
};
</script>
I get that you should do this kind of thing, but I can't figure out why it doesn't work.
As stated in the docs:
The target object cannot be a Vue instance, or the root data object of a Vue instance.
It's a technical limitation.

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.