Emit additional parameters to function from child component [duplicate] - vue.js

i'm trying to emit function with parameters like that.
template: `
<div class="searchDropDown">
<div class="dropdown is-active">
<div class="dropdown-trigger">
<button class="button" aria-haspopup="true" aria-controls="dropdown-menu">
<span>{{selectedItem}}</span>
</button>
</div>
<div class="dropdown-menu" id="dropdown-menu" role="menu">
<div class="dropdown-content">
<a class="dropdown-item" v-for="item in drop" #click="$emit('select-menu-item($event)')">
{{item.name}}
</a>
</div>
</div>
</div>
</div>
`
here is the i'm trying to pass item to method like a parameter.
here is my component which i try to emit function:
<search-component v-bind="searchProps" #select-menu-item="selectedITem($event)"></search-component>
and here is my method:
selectedITem(arg1) {
console.log("cl")
console.log(arg1)
}
here is if i'm not trying to pass parameter everything well work so my method selectedITem is working. When i try to pass parameter like that nothing happened and i'm not getting some error.

The following argument(s) in $emit() are the arguments in your emitted function.
$emit('select-menu-item', $event, 1, 2, 3, 4, "cupcakes")
and in your component method.
selectMenuItem: function(evt, num1, num2, num3, num4, food){
}
And in your actual component markup, you don't need to add arguments, just write the reference to the method without parenthesis.
<search-component v-bind="searchProps" #select-menu-item="selectMenuItem">
SAMPLE
window.onload = function(){
const component = function() {
return {
template: `
<div>
<button #click="$emit('click-me', 'foobar')">
Click Me
</button>
</div>
`,
props: ["data"]
}
}
new Vue({
el: container,
data: {},
components: { "my-component": component(), },
methods: {
clickMe: function(str){
console.log(str);
}
}
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="container">
<my-component :data=$data #click-me="clickMe"></my-component>
</div>

just adding more answer from #Albana Clara.
You can merge your parameter along with the event passed.
EXAMPLE:
<search-component v-bind="searchProps" #select-menu-item="selectMenuItem('test', ...arguments)">
selectMenuItem: function(a, b) {
console.log(a + " " + b);
// test foobar
}

If anyone searching how emits work in Vue3 with Composition API:
In the child component:
<script setup>
const emit = defineEmits(["updateItem", "updateCount"])
function itemizedLot() {
emit("updateItem", param1, param2)
}
function countedLot() {
emit("updateCount", param3, param4)
}
</script>
<template>
<button #click="itemizedLot"> Btn1 </button>
<button #click="countedLot"> Btn2 </button>
</template>
In the parent component:
<script setup>
// access the emitted methods and their parameters
function updateMenuItem(param1, param2) {
...
}
function updateMenuCount(param3, param4) {
...
}
</script>
<template>
<MyButton #update-item="updateMenuItem" #update-count="updateMenuCount"/>
</template>

Related

Problem with Recursive Components in Vue.js

I'm a beginner in Vue.js and I'm getting stuck on using recursive components.
When I try to recall the GroupFilter component in the GroupFilter component, the test message "New filter group" is displayed fine, but not the new GroupFilter component.
I have tried using an import and using the name given to the component, but in both cases it does not work.
Does anyone know how to make this work ? Thank you in advance for your help !
<template>
<div class="groupFilter">
<p>OR</p>
<div class='groupFilterForms'>
<div class="selectButton">
<button type="button" class="btn btn-primary" v-on:click="addNewFilter">+ Add Filter</button>
<button type="button" class="btn btn-success" v-on:click="addNewGroupFliter">+ Add Filter Group</button>
</div>
<div v-for="(child, index) in this.$store.state.storeData[this.index].$all" :index="index" :key="index">
<div v-if="child.condition">
<FilterTemplate :index='index'/>
</div>
<div v-else-if="child.$all">
<h3>New filter group</h3>
<GroupFilter/>
</div>
</div>
</div>
</div>
</template>
<script>
import FilterTemplate from'./FilterTemplate';
import GroupFilter from './GroupFilterTemplate'
export default {
name: 'GroupFilter',
components: {
FilterTemplate,
GroupFilter
},
data: function (){
return {
storeData: [],
props: this.index,
toto: Array
}
},
props: ['index'],
mounted: function(){
this.storeData = this.$store.state.storeData
},
methods: {
addNewFilter(){
console.log("new filter TEST")
this.storeData[this.props].$all.push(
{condition:{
"attr":"group_id",
"ope":"eq",
"value":106
}}
);
this.$store.commit('setStoreData', this.storeData);
console.log(this.storeData[index])
},
addNewGroupFliter(){
console.log("new group filter TEST")
this.storeData[this.props].$all.push({
$all:[
{condition:{
"attr":"group_id",
"ope":"eq",
"value":106
}}
]
})
this.$store.commit('setStoreData', this.storeData);
}
}
}
</script>

Vue Eventbus: handler.apply is not a function

I want to reload one of my components when an other component changes (for example I send a put with axios)
I tried it with eventbus but I got this error:
handler.apply is not a function
In the component where I want to trigger:
EventBus.$emit('compose-reload', Math.random()*100);
Where I want to be triggered:
<template>
<div class="">
<div class="">
<div class="columns">
<div class="column is-one-fifth">
<div class="box">
<Menu></Menu>
</div>
</div>
<div class="column is-four-fifth">
<div class="box">
<router-view :key="key"></router-view>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import Menu from './includes/Menu'
import EventBus from '../../../event-bus';
export default {
components: {
Menu,
},
data() {
return {
key: null
}
},
mounted(){
EventBus.$on('compose-reload', this.key);
},
created(){
this.key = Math.random()*100
}
}
</script>
EventBus.$on expects a handler function as a second argument but the variable this.key is passed in, hence the error.
You should change this :
mounted(){
EventBus.$on('compose-reload', this.key);
}
To this :
mounted(){
EventBus.$on('compose-reload', key => {
this.key = key;
});
}

VuesJS components template

I'm a VueJS beginner and i'm struggling to understand some component logic.
If i have my component (simplified for clarity) :
Vue.component('nav-bar', {
template: '<nav [some code] ></nav>'
}
This component represent the whole navigation bar of my page.
In my HTML file, how can i insert code inside the component?
Something like:
<nav-bar>
<button></button>
...
</nav-bar>
Could you please tell me if it is the right way to do it?
There are at least three options I can think of:
Using ref, or
Slot props with scoped slots, or
provide/inject.
1. Example with ref
Vue.component('NavBar', {
template: `
<nav>
<slot></slot>
</nav>
`,
methods: {
run() {
console.log('Parent\'s method invoked.');
}
}
});
new Vue().$mount('#app');
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<div id="app">
<nav-bar ref="navbar">
<button #click="$refs.navbar.run()">Run with refs</button>
</nav-bar>
</div>
2. With Scoped <slot>
Vue.component('NavBar', {
template: `
<nav>
<slot v-bind="$options.methods"></slot>
</nav>
`,
methods: {
run() {
console.log('Parent\'s method invoked.');
}
}
});
new Vue().$mount('#app');
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<div id="app">
<nav-bar>
<template #default="methods">
<button #click="methods.run">Run with slot props</button>
</template>
</nav-bar>
</div>
3. With provide and inject
Vue.component('NavBar', {
template: `
<nav>
<slot></slot>
</nav>
`,
provide() {
const props = {
...this.$options.methods,
// The rest of props you'd like passed down to the child components.
};
return props;
},
methods: {
run() {
console.log('Parent\'s method invoked.');
}
}
});
// In order to "receive" or `inject` the parent props,
// the child(ren) needs to be a component itself.
Vue.component('Child', {
template: `
<button #click="run">
<slot></slot>
</button>
`,
// Inject anything `provided` by the direct parent
// This could also be `data` or `props`, etc.
inject: ['run']
});
new Vue().$mount('#app');
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<div id="app">
<nav-bar>
<template>
<child>Run with injected method</child>
</template>
</nav-bar>
</div>

Vue component not showing no errors

I am trying to do a very simple vue example and it won't display. I've done similar things before, but this won't work.
It is an extremely simple task list. It is an input with a submit button that adds an item to a list. For some reason the component does not render at all. I am very lost am supposed to give a presentation on vue. I was hoping to use this as an example.
I'm really not sure what else to say about this, but stack overflow won't let me submit this without typing more information about the issue.
<div id="app">
<task-list></task-list>
</div>
Vue.component('task-list-item', {
props: ["task"],
template: '#task-list-item-template'
})
Vue.component('task-list', {
data: function () {
return {
taskList: [],
newTask: ''
}
},
methods: {
addTask: function () {
var self = this;
if (self.newTask !== ""
&& self.newTask !== null
&& typeof self.newTask !== "undefined") {
this.taskList.push(self.newTask);
this.newTask = "";
}
}
},
template: '#task-list-template'
})
new Vue({
el: '#app',
data: function () {
return {
}
}
})
<script id="task-list-template" type="text/x-template">
<input v-model="newTask" />
<button v-on:click="addTask()">Add Task</button>
<ul>
<task-list-item v-for="taskItem in taskList"
v-bind:task="taskItem">
</task-list-item>
</ul>
</script>
<script id="task-list-item-template" type="text/x-template">
<li>{{task}}</li>
</script>
I am getting no error messages of any kind.
I think the problem is there should be only 1 child under <script id="task-list-template" type="text/x-template"></script>. In task-list-template, you have multiple children. Try to wrap them in 1 div
<script id="task-list-template" type="text/x-template">
<div>
<input v-model="newTask" />
<button v-on:click="addTask()">Add Task</button>
<ul>
<task-list-item v-for="taskItem in taskList"
v-bind:task="taskItem">
</task-list-item>
</ul>
</div>
</script>
Demo on codepen
According to A Single Root Element
Every component must have a single root element
To fix you can do some thing like:
<script id="task-list-template" type="text/x-template">
<div>
<input v-model="newTask" />
<button v-on:click="addTask()">Add Task</button>
<ul>
<task-list-item v-for="taskItem in taskList" v-bind:task="taskItem">
</task-list-item>
</ul>
</div>
</script>

Vuejs How to use $emit function properly?

I have two components SingleTaskItem and ControlArea. ControlArea has a collapse button and when that button is clicked I want to call a function in SingleTaskItem. Here is my code so far. Can you please tell me what am I doing wrong?
SingleTaskItem:
<template>
<div class="SingleTaskItem">
<ControlArea v-bind:collapsed="collapsed"
v-bind:onClickCollapse="onClickCollapse"/>
</div>
</template>
<script>
export default {
name: "SingleTaskItem",
data() {
return {
collapsed: false
};
},
methods: {
onClickCollapse(value) {
console.log("on Click Collapse called");
this.collapsed = value;
}
}
};
</script>
ControlArea:
<template>
<div class="ControlArea">
<div class="action-btn edit">
<i class="fas fa-ellipsis-h"></i>
</div>
<div class="action-btn collapsible">
<i v-if="collapsed" v-on:click="uncollapse" class="fas fa-chevron-down"></i>
<i v-else v-on:click="collapse" class="fas fa-chevron-up"></i>
</div>
</div>
</template>
<script>
export default {
name: "ControlArea",
props: {
collapsed: Boolean
},
methods: {
collapse(event) {
console.log("collapse function is called");
this.$emit("onClickCollapse", "true");
},
uncollapse(event) {
this.$emit("onClickCollapse", "false");
}
}
};
</script>
Instead of v-bind:onClickCollapse="onClickCollapse" you should use v-on:onClickCollapse. This is kind of easy to miss because you used the word 'on' in your event name - it might be clearer to remove that.
Also, to pass that true/false string you need to pass $event into your function call: v-on:onClickCollapse($event). To clean this up you should probably also pass true/false booleans rather than strings.