In VueJS after append() an element the click method not works - javascript

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>

Related

When does an element become available in dom after using vue v-if

When does an element become available in the dom after using a vue v-if? I would have thought you could do a query selector on the element after the v-if evaluates to true?
In the below code I need to get the .test element once the popout is shown but it shows as null - how do I get it?
new Vue({
el: "#app",
data() {
return {
showPopout: false,
};
},
methods: {
buttonClick(day) {
this.showPopout = true;
console.log(document.querySelector('.test'));
},
},
});
<script src="https://unpkg.com/vue#2.6.14/dist/vue.js"></script>
<div id='app'>
<span #click="buttonClick">show popout</span>
<div v-if="showPopout"><span class="test">test</span></div>
</div>
It will be there after nextTick
new Vue({
el: "#app",
data() {
return {
showPopout: false,
};
},
methods: {
buttonClick(day) {
this.showPopout = true;
this.$nextTick(() => {
console.log(document.querySelector('.test'));
});
},
},
});
<script src="https://unpkg.com/vue#2.6.14/dist/vue.js"></script>
<div id='app'>
<span #click="buttonClick">show popout</span>
<div v-if="showPopout"><span class="test">test</span></div>
</div>

Clear vue input fields after emitting event

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>

How to clone vue instance to another dom node

How can i clone a vue application to another dom node?
I have made a simple vue app to work out my problem, i tried cloning using js clone function and re-initializing the application, it will mount but it wont workout the events. Here's my code:
const initMyApp = (el) => {
return new Vue({
el,
data: {
message: 'HEY'
},
methods: {
sayHello: function () {
this.message = 'HELLO!!!' + new Date().getMilliseconds()
}
},
mounted: function () {
console.log('mounted')
}
})
}
initMyApp('#app')
function moveApp() {
var appNode = document.getElementById("app")
var cloned = appNode.cloneNode(true);
document.getElementById("appContainer").innerHTML = ''
document.getElementById("appContainer").appendChild(cloned);
initMyApp('#appContainer')
}
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
<h2>Main</h2>
<button #click="sayHello">Hello</button>
<p>{{message}}</p>
</div>
<button onclick="moveApp()">DO</button>
<div>
<h2>Container</h2>
<div id="appContainer">
</div>
</div>
Any suggestion is really appreciated
If you want to mount your Vue app on different DOM elements, then you should be using mount() instead of the el:
const initMyApp = (el) => {
return new Vue({
data: {
message: 'HEY'
},
methods: {
sayHello: function() {
this.message = 'HELLO!!!' + new Date().getMilliseconds()
}
},
mounted: function() {
console.log('mounted')
}
})
}
function moveAppFirst() {
const vue1 = initMyApp()
vue1.$mount('#app')
}
function moveAppSecond() {
const vue2 = initMyApp()
vue2.$mount('#appContainer')
}
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<button onclick="moveAppFirst()">DO FIRST</button>
<div id="app">
<h2>Main</h2>
<button #click="sayHello">Hello</button>
<p>{{message}}</p>
</div>
<button onclick="moveAppSecond()">DO SECOND</button>
<div>
<h2>Container</h2>
<div id="appContainer">
<button #click="sayHello">Hello</button>
<p>{{message}}</p>
</div>
</div>
More information on mount(): https://v2.vuejs.org/v2/api/#vm-mount

Cannot render multiple components in Vue

I'm just starting with Vue and I have defined 2 components, however, I cannot render them in the same 'Vue' instance.
Here are my 2 components:
Vue.component('mat-example', {
data: function () {
return {
count: 0
}
},
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
Vue.component('button-counter', {
data: function () {
return {
count: 0
}
},
template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
Next, I do have the 'Vue' entry point defined:
var app = new Vue({
el: '#app'
});
And in my HTML, I do have the following code:
<div id="app">
<button-counter />
<mat-example />
</div>
The 'Vue' development tools does only show the 'button-counter' component:
If I remove the 'button-counter', the 'mat-example' is showed up in the Vue Developer Tools.
How does it come that I cannot render those 2 components in my Vue entry point?
this is going to work:
<div id="app">
<button-counter></button-counter>
<mat-example></mat-example>
</div>
demo:
Vue.component('mat-example', {
data: function() {
return {
count: 0
}
},
template:
'<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})
Vue.component('button-counter', {
data: function() {
return {
count: 0
}
},
template:
'<button v-on:click="count++">You clicked me {{ count }} times</button>'
})
var app = new Vue({
el: '#app'
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<button-counter></button-counter>
<mat-example></mat-example>
</div>
Use this tag instead of using self closing tag
<div id="app">
<button-counter></button-counter>
<mat-example></mat-example>
</div>

Is it possible to append a component on a button press?

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.

Categories

Resources