I have a Vue component with an input and a button.
I would like the button to send off the input elsewhere and then clear the input. The latter part, is not happening.
Here is a rough runnable version of what I have:
new Vue({
el: '#root',
template: `
<div>
<input type='text' v-model='name'/>
<button #click='onClickMe'>Click me</button>
</div>
`,
methods: {
onClickMe () {
this.$emit('set-this-elsewhere', { name: this.name })
this.name = ''
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="root">
</div>
You see the error in the console?
Property or method "name" is not defined on the instance but referenced during render
You need to add
data() {
return {
name:''
};
},
new Vue({
el: '#root',
template: `
<div>
<input type='text' v-model='name'/>
<button #click='onClickMe'>Click me</button>
</div>
`,
data() {
return {
name: ''
};
},
methods: {
onClickMe() {
this.$emit('set-this-elsewhere', {
name: this.name
})
this.name = ''
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="root">
</div>
Related
I am struggling to update the interpolation of thing.
This simple function should change the value of the data but instead does nothing.
Chrome logs the change of thing in the console but the HTML does not update.
<template>
<div class="container-xl pb-5">
<button class="mb-2 btn-lg selected-dropdown" #click="dropSelect('B')">
{{ thing }}
</button>
</div>
</template>
<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({
data() {
return {
thing: "A",
};
},
methods: {
dropSelect(thing: any) {
console.log(thing);
return this.thing;
},
},
});
</script>
Try to update your data property instead of returning:
dropSelect(thing: any) {
this.thing = thing
},
One small observation : Use this.thing = thing instead of return this.thing
Demo :
new Vue({
el: '#app',
data: {
thing: "A"
},
methods: {
dropSelect(thing) {
this.thing = thing;
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button #click="dropSelect('B')">
{{ thing }}
</button>
</div>
I can solve this problem in jquery using the method $(document).on('click', '.newButton', function(){}); how can I solve the same thing at VUE
new Vue ({
el: '#app',
data: {
oldButton: function () {
$('#app').append('<button v-on:click="newButton">The New Button</button>');
},
newButton: function () {
console.log('Hello World!');
},
},
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app">
<button v-on:click="oldButton">The Old Button</button>
</div>
In vue.js, you normally don't manipulate DOM in javascript; If you want to conditionally show a component, you can use v-if or v-show directives; And also you should define your functions as methods instead of data:
new Vue ({
el: '#app',
data: {
showNewButton: false
},
methods: {
oldButton: function () {
this.showNewButton = true;
},
newButton: function () {
console.log('Hello World!');
},
},
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app">
<button v-on:click="oldButton">The Old Button</button>
<button v-if="showNewButton" v-on:click="newButton">The New Button</button>
</div>
If you want to have an array of buttons:
const app = new Vue({
el: '#app',
data: {
btns: []
},
methods: {
addBtn() {
this.btns.push({
name: 'Dynamic Button'
})
},
showMsg(index) {
console.log('Hello World!, from Button ' + index)
}
}
})
And:
<div id="app">
<ul>
<li v-for="(btn, index) in btns">
<button #click="showMsg(index)" type="text"> {{ btn.name}}</button>
</li>
</ul>
<button #click="addBtn">Add Button</button>
</div>
I wrote a basic vue js 2 basic example to test nested components.
Below is components and template structure.
Vue.component('form-com', {
template: '#form',
props: ['value'],
methods: {
onInput: function (event) {
this.$emit('input', event.target.value);
}
}
});
Vue.component('message-com', {
template: '#message',
data: function () {
return {
msg: 'Hello'
}
},
props: ['user']
});
Vue.component('welcome-com', {
template: '#welcome',
data: function () {
return {
user: 'ahmad'
}
}
});
new Vue({
el: '#container'
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.js"></script>
<!--Form Template-->
<template id="form">
<div>
<div class="form-control">
<label>Enter Your Name:</label>
<input type="text" v-bind:value="value" :input="onInput">
</div>
</div>
</template>
<!--Hello Message Template-->
<template id="message">
<div>
<h3>{{msg}} {{user}}</h3>
</div>
</template>
<template id="welcome">
<div>
<form-com :value="value"></form-com>
<br>
<message-com :user="user"></message-com>
</div>
</template>
<div id="container">
<welcome-com></welcome-com>
</div>
But when run app in browser this error is shown:
[Vue warn]: Property or method "value" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
found in
---> <WelcomeCom>
<Root>
what is problem?
Update:
I just Rewrite this Fiddle from one of chapters of Learning Vue.js 2. I just rename some parameters and component and templates names. but when I copy main fiddle to my code all things worked.
In your form-com component you can set up a v-model which binds the input value and set up a watcher to observer the changes in the input which in turn emits an custom-event which telss the parent comonent that a change has taken place.
Vue.component('form-com', {
template: '#form',
data(){
return{
myInput:''
}
},
watch: {
myInput: function (inputVal) {
this.$emit('input', inputVal);
}
}
});
Vue.component('message-com', {
template: '#message',
data: function () {
return {
msg: 'Hello'
}
},
props: ['user']
});
Vue.component('welcome-com', {
template: '#welcome',
data: function () {
return {
user: 'ahmad'
}
},
methods:{
updateUser(value){
this.user = value;
}
}
});
new Vue({
el: '#container'
})
You can listen to the events emitted from the child **form-com ** component using v-on:input or shorthand #input directly in the parent template (welcome component) where the child component is used.
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.js"></script>
<!--Form Template-->
<template id="form">
<div>
<div class="form-control">
<label>Enter Your Name:</label>
<input type="text" v-model="myInput">
</div>
</div>
</template>
<!--Hello Message Template-->
<template id="message">
<div>
<h3>{{msg}} {{user}}</h3>
</div>
</template>
<template id="welcome">
<div>
<form-com #input="updateUser($event)" ></form-com>
<br>
<message-com :user="user"></message-com>
</div>
</template>
<div id="container">
<welcome-com></welcome-com>
</div>
Here is the jsFiddle
If you don't want to use a watcher then you can do it using computed setter . Have a look at the fiddle which is using a computed-setter
You are missing in your Component 'welcome-com' the value object:
Vue.component('welcome-com', {
template: '#welcome',
data: function () {
return {
value: '',
user: 'ahmad'
}
}
});
Here's a problem that I want to solve:
I have a button that when is pressed must append my-component to a dom.
If it is pressed 2 times there must be 2 <p> tegs. How can I achieve this?
js:
<script>
Vue.component('my-component', {
template: "<p>hello</p>",
})
var vue = new Vue({
el: "#App",
data: {},
methods: {
append: function() {
// unknown code here
}
}
})
</script>
html:
<div id = "App">
<button #click="append" class="btn btn-primary">Spawn stuff!</button>
</div>
Here is one way you could do that. This code iterates over a counter using v-for to iterate over a range.
Vue.component('my-component', {
template: "<p>hello</p>",
})
var vue = new Vue({
el: "#App",
data: {
hellocount: 0
},
methods: {
append: function() {
// unknown code here
this.hellocount++
}
}
})
<script src="https://unpkg.com/vue#2.2.6/dist/vue.js"></script>
<div id="App">
<my-component v-for="n in hellocount" :key="n"></my-component>
<button #click="append" class="btn btn-primary">Spawn stuff!</button>
</div>
This is a little atypical; normally you will drive the components rendered from actual data, as #RoyJ suggests in your comments.
From your comment below, you could build a form something like this.
Vue.component('my-input', {
props:["value", "name"],
data(){
return {
internalValue: this.value
}
},
methods:{
onInput(){
this.$emit('input', this.internalValue)
}
},
template: `
<div>
{{name}}:<input type="text" v-model="internalValue" #input="onInput">
</div>
`,
})
var vue = new Vue({
el: "#App",
data: {
form:{
name: null,
email: null,
phone: null
}
},
methods:{
append(){
const el = prompt("What is the name of the new element?")
this.$set(this.form, el, null)
}
}
})
<script src="https://unpkg.com/vue#2.2.6/dist/vue.js"></script>
<div id="App">
<my-input v-for="(value, prop) in form"
:key="prop"
v-model="form[prop]"
:name="prop">
</my-input>
<button #click="append">Add New Form Element</button>
<div>
Form Values: {{form}}
</div>
</div>
The code defines a form object and iterates over the properties of the form to render inputs for each property.
This is obviously extremely naive, handles only input texts, etc. But hopefully you get the idea.
I m trying to make a game.
When a player submit to the game, normally the welcome message appears ! But Nothing appears.
<template>
<div>
<span v-html = "welcomeMessage" v-hide></span>
<form v-hide v-on:submit.prevent="setPlayer">
<input name="player" placeholder="Enter the player name" v-border:blue/>
<button type="submit" v-border:red>Play</button>
</form>
</div>
</template>
<script>
export default {
name: 'player',
data: function () {
return {
player: '',
welcomeMessage: ''
}
},
updated: function () {
this.welcomeMessage = `Hi <span class="player">${this.player}</span> ! `
},
methods: {
setPlayer: function (event) {
this.player = event.target[0].value
}
},
directives: {
border: function (el, binding) {
el.style.borderColor = binding.arg
},
hide: function (el, binding, vnode) {
let isForm = vnode.tag === 'form'
let player = vnode.context.player
if (isForm) {
el.style.display = player ? 'none' : 'block'
} else {
el.style.display = player ? 'block' : 'none'
}
}
}
}
</script>
<style scoped>
</style>
it seems that doesn't work, i don't know why ! the name of theplayer is updated in the hook updated but the template doesn't show it.
Any clue.
Thxs.
Use v-model to bind input to data, this create a two binding on inputs. This can reduce a couple to code lines.
<template>
<div>
// show message only if new player created
<span v-html="welcomeMessage" v-show="playerCreated"></span>
<form v-on:submit.prevent="setPlayer" v-if="!playerCreated">
<input name="player" v-model="player" placeholder="Enter the player name" v-border:blue/> // bind input to `player` data property.
<button type="submit" v-border:red>Play</button>
</form>
</div>
</template>
In the component logic:
data: function () {
return {
player: '',
playerCreated: false,
welcomeMessage: ''
}
},
methods: {
setPlayer: function () {
this.playerCreated = true;
this.welcomeMessage = `Hi <span class="player">${this.player}</span> !`
}
},
You can use this, its worked for me in modal, when loading data to body
JSFiddle
<template id="some-template">
<div class="header">
<slot name="header"></slot>
<slot name="body"></slot>
<slot name="footer"></slot>
</div>
</template>
<div id="app">
<some>
<div slot="header" v-html="rawHtmlheader"></div>
<div slot="body" v-html="rawHtmlbody"></div>
<div slot="footer" v-html="rawHtmlfooter"></div>
</some>
</div>
JS logic
Vue.component('some', {
template: '#some-template'
})
new Vue({
el: '#app',
data: {
rawHtmlheader: '<p style="color:red">RED HEADER</p>',
rawHtmlbody: '<p style="color:green">GREEN TEXT</p>',
rawHtmlfooter: '<p style="color:brown">BROWN FOOTER</p>',
}
})