I'm just tring to do simple onclick methods but I'm not sure why the methods are not triggered.
My buttons:
<template>
<div class="button">
<a
long
class="cancel-button"
#click="cancelOnClick"
>Cancel</a>
<a
long
class="ok-button"
#click="okOnClick"
>OK</a>
</div>
</template>
My method:
export default {
methods: {
cancelOnClick(){ console.log('Cancel clicked') }
okOnClick() { console.log('OK clicked') }
}
}
But when I change the
#click="cancelOnClick"
to
:click="this.cancelOnClick()"
then the function will keep triggered without clicking it. I'm not sure what's happening. Any help suggestions ?
#click="cancelOnClick" is working...
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script type="text/x-template" id="test-template">
<div class="button">
<a
long
class="cancel-button"
#click="cancelOnClick"
>Cancel</a>
<a
long
class="ok-button"
#click="okOnClick"
>OK</a>
</div>
</script>
<div id="app"></div>
<script>
var app = new Vue({
el: '#app',
template: '#test-template',
methods: {
cancelOnClick(){ console.log('Cancel clicked') },
okOnClick() { console.log('OK clicked') }
}
})
</script>
Related
I'm new to vue (less than a week) and I'm rewriting a hobby project to get up and going. Decided to try out components and ran into and issue. One of my sub-components emits just fine, but the other one is not received by the parent. The vue-devtools chrome extension tells me that sidenavselect is indeed emitting the close event, but the sidenav v-on:close is not being triggered.
<sidenav v-on:close="sidebar_show = false" v-show="sidebar_show">
<sidenavbutton #click="draw_board">Start Round</sidenavbutton>
<sidenavselect v-model="location" :datafield="locations" title="Location"></sidenavselect>
</sidenav>
Vue.component('sidenav', {
props: ['method'],
template: `
<div class="sidenav" v-on:close="handle_close" #click.self="handle_close">
<div class="contents" v-on:close="handle_close">
<span class="closebtn" v-on:click="handle_close">×</span>
<slot></slot>
</div>
</div>
`,
methods: {
handle_close: function() {
this.$emit('close');
}
}
});
Vue.component('sidenavbutton', {
template: `
<button tabindex="-1" #click="handle_click"><slot></slot></button>
`,
methods: {
handle_click: function() {
this.$emit('click');
this.$emit('close');
}
}
});
Vue.component('sidenavselect', {
props: ['datafield', 'title', 'value'],
template: `
<div class="sidenav-box">
{{title}}<br>
<select tabindex="-1" v-bind:value="value" #input="handle_close">
<option v-for="this_data in datafield" :value="this_data.value">{{this_data.label}}</option>
</select>
</div>
`,
methods: {
handle_close: function(event) {
this.$emit('input', event.target.value);
this.$emit('close');
}
}
});
Vue.component("sidenav", {
template: `
<div class="sidenav" #close="handle_close" #click.self="handle_close">
<div class="contents" #close="handle_close">
<span class="closebtn" #click="handle_close">×</span>
<slot></slot>
</div>
</div>
`,
methods: {
handle_close: function() {
this.$emit("close");
}
}
});
Vue.component("sidenavbutton", {
template: `
<button #click="handle_click"><slot></slot></button>
`,
methods: {
handle_click: function() {
this.$emit("click");
this.$emit("close");
}
}
});
Vue.component("sidenavselect", {
props: ["value"],
template: `
<div class="sidenav-box">
<select :value="value" #input="handle_close">
<option value="1">A</option>
<option value="2">B</option>
<option value="3">C</option>
</select>
</div>
`,
methods: {
handle_close: function(event) {
this.$emit("input", event.target.value);
this.$emit("close");
}
}
});
new Vue({
data() {
return {
sidebar_show: true,
location: 0
};
},
methods: {
handle_close: function(event) {
this.sidebar_show = false;
}
}
}).$mount("#app");
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<sidenav #close="handle_close" v-show="sidebar_show">
<sidenavbutton #close="handle_close">Start Round</sidenavbutton>
<sidenavselect #close="handle_close" v-model="location"></sidenavselect>
</sidenav>
</div>
You want to do too much at once. Take smaller steps. It's easier to catch mistakes.
The answer to my issue was that the methods attached to the buttons were still closing the sidenav from before I was working with components.
User skirtle was correct in that each component I used needed to have #close on them.
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>
I just created an event bus in the main.js file like this:
main.js
Vue.prototype.$bus = new Vue()
After that, I just wrote some code to test the event bus like this:
TestComponent
<template>
<div>
<div class="account-modal_form">
<form action="" #submit.prevent="formSubmit">
<div class="account-modal_form__group" :class="{ warning: errors.has('password') }">
<div class="account-modal_form__input">
<input name="password" :type="passwordType" placeholder="" class="width-316" v-validate="'required'" v-model="password">
<i class="account-modal_form__viewpass" #click="togglePassword"></i>
</div>
<span class="account-modal_form__warning" v-show="errors.has('password')">
{{ errors.first('password') }}
</span>
</div>
{{ errors }}
<div class="account-modal_form__group">
<button type="submit" class="btn btn--primary btn--large">next</button>
<button type="button" class="btn btn--default" #click="cancelAction">cancel</button>
</div>
</form>
</div>
</div>
</template>
<script>
import { API } from '#/api'
export default {
data() {
return {
passwordType: 'password',
password: ''
}
},
methods: {
created() {
this.$bus.$on('test', () => console.log('test'));
},
nextStep() {
this.$bus.$emit('test');
},
formSubmit() {
this.nextStep();
}
}
}
</script>
When I click submit button I want to submit form first and call nextstep to emit an event, but the $on event output nothing.
You're running $emit before $on, so when you fire the event there are no listeners at that point, and it's better to register your listeners on the component created life cycle event, otherwise whenever you run your test method you'll register a new listener:
Vue.prototype.$bus = new Vue();
Vue.component('spy-component', {
template: '<p>{{this.text}}</p>',
data() {
return {
text: '',
}
},
created() {
this.$bus.$on('sendOriginPassword', (text) => {
this.text = text;
});
}
})
Vue.component('test-component', {
template: '<button #click="test">Click me</button>',
created() {
this.$bus.$on('sendOriginPassword', () => {
console.log('I am listening event')
});
},
methods: {
test() {
this.$bus.$emit('sendOriginPassword', 'Can you hear me?');
}
}
});
new Vue({
el: "#app",
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
<div id="app">
<spy-component></spy-component>
<test-component></test-component>
</div>
I understand how this works from the VueJS Documentation:
<div id="components-demo">
<button-counter></button-counter>
</div>
And also this:
<div id="app">
<a v-on:click="loadElement(request, etc)">Load</a>
</div>
Is there any way to write the equivalent from a function and have Vue pick it up? For example as this:
<div id="app">
{{ writeComponent('component-name', etc) }}
or
{{ writeElement('loadElement') }} <!-- which then writes the component above -->
</div>
The reason for this is that in this context quite a few components might need to be written and writing it out in HTML would be cumbersome.
You could use the render function like below:
Vue.component('button-counter', {
template: '<span class="bc">bc</span>'
})
new Vue({
el: '#app',
data: {
request: 'req!',
etc: 'etc!',
buttonCounters: []
},
methods: {
loadElement(r, e) {
console.log('loadElement', r, e);
this.buttonCounters.push(r);
}
},
render(h) {
let bcs = this.buttonCounters.map(bc => h('button-counter'));
let loadLink = h('a', {on: {"click": ($event) => { this.loadElement(this.request, this.etc)}}}, ["Load"]);
return h('div', {attrs: {"id": "app"}}, [loadLink, " ", ...bcs])
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app"></div>
Though the same can be achieved via regular template and v-for:
Vue.component('button-counter', {
template: '<span class="bc">bc</span>'
})
new Vue({
el: '#app',
data: {
request: 'req!',
etc: 'etc!',
buttonCounters: []
},
methods: {
loadElement(r, e) {
console.log('loadElement', r, e);
this.buttonCounters.push(r);
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<a v-on:click="loadElement(request, etc)">Load</a>
<button-counter v-for="(bc, index) in buttonCounters" :key="index"></button-counter>
</div>
Problem
loginForm.recaptcha is always blank. Am I missing anything?
Component
<template>
<div>
<vue-recaptcha v-model="loginForm.recaptcha"
sitekey="My key">
</vue-recaptcha>
</div>
</template>
<script>
export default {
data() {
return {
loginForm: {
recaptcha: ''
}
}
}
}
</script>
app.js
import VueRecaptcha from 'vue-recaptcha';
Vue.use(VeeValidate);
Vue.component('vue-recaptcha', VueRecaptcha);
I can confirm that the captcha is rendering successfully.
vue-recaptcha as no value/v-model property. You can use the verify event:
See demo JSFiddle here.
Code:
Vue.component('vue-recaptcha', VueRecaptcha);
new Vue({
el: '#app',
data: {
loginForm: {
recaptcha: ''
}
},
methods: {
recaptchaVerified(response) {
this.loginForm.recaptcha = response;
}
}
})
<script src="https://www.google.com/recaptcha/api.js?onload=vueRecaptchaApiLoaded&render=explicit" async defer>
</script>
<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/vue-recaptcha#latest/dist/vue-recaptcha.js"></script>
<div id="app">
<div>
<vue-recaptcha #verify="recaptchaVerified"
sitekey="your key">
</vue-recaptcha>
</div>
loginForm: {{ loginForm }}
</div>