Vue event handler on dynamically inserted string does not work - javascript

This is my code:
<template>
<div>
<div v-html="data"></div> <button v-on:click="replace">Click Me to replace div contents</button>
</div>
</template>
<script>
export default {
data() {
return {
data: "I will be replaced once you click on button"
}
},
methods: {
clickMe() {
alert("worked");
},
replace(){
this.data = "Why does click me not work? It is loaded from server via ajax <a href v-on:click.prevent='clickMe'>Click Me</a>";
}
}
};
</script>
Here if I click on Click Me to replace div contents the content is replaced but the event handler clickMe does not fire. This data would come from server and I need to compile this string and use it from within the Vue's context so Vue can handle events etc.
How can I have the dynamic string downloaded from server work? I am using Vue 2.

Since v-html isn't compiled you will have to create a mini component like this to get around the issue:
new Vue({
el: '#app',
data () {
return {
data: ``
}
},
computed: {
compiledData () {
return {
template: `<p>${this.data}</p>`
}
}
},
methods: {
replace () {
this.data = `Now click on me <a href='#' #click.prevent='alert("yo")'> here </a>`
}
}
})
<script src="https://unpkg.com/vue#2.5.3/dist/vue.min.js"></script>
<div id="app">
<component :is="compiledData" ></component>
<button v-on:click="replace">Click Me to replace div contents</button>
</div>
The above code compiles the string content and thus you can run/execute the function as intended

Other solution using Vue components (codepen):
<script src="https://unpkg.com/vue"></script>
<div id="app">
<div id="someId"></div> <button v-on:click="replace">Click Me to replace div contents</button>
<component :is="currentView"></component>
</div>
<script>
let app = new Vue({
el: '#app',
data: {
currentView: null
},
methods:{
replace: function(){
var templateFromServer = getTemplate();
var comp=Vue.component('template-from-server', {
template: templateFromServer,
methods:{
clickMe:function (){
console.log("click");
}
}
});
this.currentView = comp;
}
}
});
function getTemplate(){
return "<a href v-on:click.prevent='clickMe'>Click Me</a>"
}
</script>

v-html is not compiled as a Vue template. From the docs:
Note that the contents are inserted as plain HTML - they will not be compiled as Vue templates. If you find yourself trying to compose templates using v-html, try to rethink the solution by using components instead.
see: https://v2.vuejs.org/v2/api/#v-html

You can not render VueJS code from a html string.
You can solve this issue by using v-if
<div>
<div v-if="data">I will be replaced once you click on button</div>
<div v-else>Why does click me not work? It is loaded from server via ajax <a href #click.prevent='clickMe'>Click Me</a></div>
<button #click="replace">Click Me to replace div contents</button>
</div>
<script>
export default {
data() {
return {
data: true
}
},
methods: {
clickMe() {
alert("worked");
},
replace(){
this.data = !this.data;
}
}
};
You can call normal javascript function from string but not vuejs function so onclick event would also work.

Related

Default click on a button when component loads in vue js

I have a button in vue component within template as follow:
<a href="#" #click="openTab" class="border-red px-8" id="activeSlide" data-target-quote="#yvoefrance">
<img :src="inactive_logo[0]" class="logo" alt="yvoefrance logo" />
</a>
I want it to be clicked by default when components loads after refreshing the page, how can I achieve this? I tried following but didn't work for me.
I thought the right place is created. Can anyone help? Thank you in advance.
export default {
name: "component.showcase",
components: {
// ...
},
data() {
return {
// data here....
};
},
created() {
document.querySelector("#activeSlide").click();
},
mounted() {},
beforeDestroy() {},
computed: {},
methods: {
openTab: function(e) {
e.preventDefault();
const target_tab = e.target.parentElement.dataset.targetQuote;
document.querySelector(target_tab).classList.add("active");
e.target.src = require(`#/assets/img/testimonials/${target_img}_active.png`);
}
}
};
The button should call a method when clicked:
<button #click="someMethod">Show Content</button>
Then you can just call that method programmatically from a lifecycle hook instead of trying to manually trigger a click on the button:
methods: {
someMethod() {
console.log('someMethod called');
}
},
created() {
this.someMethod(); // Run the button's method when created
}
EDIT to match your edit:
You are using DOM manipulation but should manipulate data instead and let Vue handle the DOM. Here is a basic example of how you can do what you want:
new Vue({
el: "#app",
data() {
return {
logos: [
{
urlInactive: 'https://via.placeholder.com/150/000000/FFFFFF',
urlActive: 'https://via.placeholder.com/150/FFFFFF/000000',
isActive: false
},
{
urlInactive: 'https://via.placeholder.com/150/666666/FFFFFF',
urlActive: 'https://via.placeholder.com/150/999999/000000',
isActive: false
}
]
}
},
methods: {
toggleActive(logo) {
logo.isActive = !logo.isActive;
}
},
});
<div id="app">
<a v-for="logo in logos" #click="toggleActive(logo)">
<img :src="logo.isActive ? logo.urlActive : logo.urlInactive" />
</a>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

onclick event not firing in vue.js

I have the following Vue.js template :
<script type="text/x-template" id="ti-page-inquire">
<div>
<h3 class="mdc-typography--headline3">{{page.name}}</h3>
<ti-button v-bind:button="page.button" v-on:click="onSubmit"></ti-button>
</div>
</script>
<script type="text/x-template" id="ti-button">
<button class="mdc-button mdc-button--raised" v-bind:title="button.name">{{button.name}}</button>
</script>
script
Vue.component('ti-page-inquire', {
props: ['page'],
template: '#ti-page-inquire',
methods : {
onSubmit : function() {
alert(1);
}
}
});
Vue.component('ti-button', {
props: ['button'],
template: '#ti-button',
mounted: function () {
// ripple on button
mdc.ripple.MDCRipple.attachTo(this.$el);
}
});
when I click on my custom button, nothing happens. I think it's because its looking for onSubmit in the ti-button component, but how do I get it to look in the ti-page-inquire component?
Components are black boxes you should catch all events inside it and emit them to the outer world.
Fiddle example
Vue.component('ti-button', {
props: ['button'],
template: '#ti-button',
mounted: function () {
// ripple on button
mdc.ripple.MDCRipple.attachTo(this.$el);
},
methods: {
buttonClicked: function() {
this.$emit('button-clicked');
}
}
});
<script type="text/x-template" id="ti-page-inquire">
<div>
<h3 class="mdc-typography--headline3">{{page.name}}</h3>
<ti-button v-bind:button="page.button" v-on:button-clicked="onSubmit"></ti-button>
</div>
</script>
<script type="text/x-template" id="ti-button">
<button class="mdc-button mdc-button--raised" v-bind:title="button.name" #clicked="buttonClicked">{{button.name}}</button>
</script>
This might be because you need to listen for a native click event. So you need to use the .native modifier ..
<ti-button v-bind:button="page.button" v-on:click.native="onSubmit"></ti-button>
This will only work if the button is the root element of your ti-button component. Otherwise you'll have to pass your event listeners to your button in the ti-button component like this ..
<button v-on="$listeners" ...> ... </button>
Try to emit an event from ti-button component to the parent one by using this.$emit function :
Vue.component('ti-button', {
props: ['name'],
template: '#vButton',
data: {
name: 'hi'
},
methods: {
submit() {
this.$emit('submit')
}
}
});
<template id="vButton">
<button v-bind:title="name" #click="submit">{{name}}</button>
</template>
the emitted event submit it called in the parent component like v-on:submit="onSubmit" and handled using onSubmit method:
<script type="text/x-template" id="ti-page-inquire">
<div>
<h3 class="mdc-typography--headline3">{{page.name}}</h3>
<ti-button v-bind:button="page.button" v-on:submit="onSubmit"></ti-button>
</div>
</script>
Vue.component('ti-page-inquire', {
props: ['page'],
template: '#ti-page-inquire',
methods : {
onSubmit : function() {
alert(1);
}
}
});
Sometimes you need also to emit some parameters, so you could do it like :
this.$emit('submit',params)
params could be of any type

Passing custom emited events as props to a new created component in VueJs

My app consists of:
A component named
<consl :output="output" #submit-to-vue><consl>
which contains an input that calls a submit() method when enter key is pressed.
<div>
<output v-html="output"></output>
<div id="input-line" class="input-line">
<div class="prompt">{{ prompt }}</div>
<div>
<input class="cmdline" autofocus
v-model.trim="command"
#keyup.enter="submit"
:readonly="submited" />
</div>
</div>
Then the method submit() emits an event #submit-to-vue to parent method submitv() that create an instance of the same component and adds it to the DOM.
//........
methods: {
submit: function () {
this.$emit('submit-to-vue')
this.submited = true
}
},
and
//......
methods: {
submitv: function () {
var ComponentClass = Vue.extend(consl)
var instance = new ComponentClass({
propsData: { output: this.output }
})
instance.$mount() // pass nothing
this.$refs.container.appendChild(instance.$el)
What I want to accomplish ?
I want to create a new consl component and add it to the DOM every time the old one is submited. (I want my app to emulate a terminal)
The problem
When submitted the new created component does not contain the #submit-to-vue event listener, which make it unable to recall the submitv() method.
Questions
How can I solve this problem ?
Is this the proper way to do things in VueJs or is there a more elegent way ?
In parent component, declare one data property=childs, it will includes all childs already created.
So once parent component receives the event=submit-to-vue, then add one new child to this.childs
Finally uses v-for to render these child components.
The trick: always consider the data-driven way, doesn't manipulate dom directly as possible.
below is one simple demo :
Vue.config.productionTip = false
Vue.component('child', {
template: `
<div>
<div>Label:<span>{{output}}</span></div>
<div>Value:<span>{{command}}</span></div>
<div id="input-line" class="input-line">
<div class="prompt">{{ prompt }}</div>
<div>
<input class="cmdline" autofocus
v-model.trim="command"
#keyup.enter="submit"
:readonly="submited" />
</div>
</div>
</div>`,
props: ['output'],
data() {
return {
submited: false,
command: ''
}
},
computed: {
prompt: function () {
return this.submited ? 'Already submitted, input is ready-only now' : ''
}
},
methods: {
submit: function () {
this.$emit('submit-to-vue')
this.submited = true
}
}
})
app = new Vue({
el: "#app",
data: {
childs: [{'output':'default:'}]
},
methods: {
addChild: function () {
this.childs.push({'output': this.childs.length})
}
}
})
<script src="https://unpkg.com/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<div>
<ul>
<li v-for="(child, index) in childs" :key="index">
<child :output="child.output" #submit-to-vue="addChild()"></child>
</li>
</ul>
</div>
</div>

Vue 2.0 - How passing function to child component?

I have one issue. I want to pass function link to the child component. It's working but in HTML I get that code. It's correct how improve it?
I have Vue instance
app = new Vue({
... some code
data: {
onAppClose: null,
onAppSend: null
}
})
I want to add from global window any function. Or register function in Vue instance
app.onSend = () => console.log('data')
And pass this function to child
<div id="app">
<dynamsoft-component v-if="displayComponent"
:docs="docs"
:onAppSend="onSend"
:onAppClose="onClose"
></dynamsoft-component>
</div>
But I get this HTML template in console
<div id="app">
<div onappsend="()=>{}" onappclose="function (data) {
console.warn('dwdawad')
console.log('data')
}"></div>
</div>
You example code is not making a lot of sense - do you want to add a listener not a div or pass a function to a child component?`
I assume the latter. Vue has custom events for that .
Parent template:
<div v-on:appsend="someMethod" v-on:appclose="someOtherMethod"></div>
Parent component methods:
methods: {
someOtherMethod: function (data) {
console.warn('dwdawad')
console.log('data')
},
// ...
}
And then emit form the child:
this.$emit('appclose', {id: 'whatever'} /*pass data here*/)
Edit:
I still don't see how those functions would end up directly in the template, but the real problem is: HTML is not case-sensitive. so :onAppSend becomes :onappsend. You have to use kebap-case: :on-app-send. Vue will convert it to onAppSend in the component.
I have never used Vue.js before now..
But having a look at the how to on their site, this seems to work
In Vue style guide have recommendations about props naming
https://v2.vuejs.org/v2/style-guide/#Prop-name-casing-strongly-recommended
Vue.component('dynamsoft-component', {
props: ['onAppSend'],
template: '<button v-on:click="buttonclick">click me</button>',
methods: {
buttonclick(e){
// Check if onAppSend is defined.
if(Boolean(this.onAppSend)){
this.onAppSend();
}
}
}
})
new Vue({
el: '#app',
methods: {
onSend: function(){
console.log('child clicked');
}
}
});
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app">
<dynamsoft-component :on-app-send="onSend"></dynamsoft-component>
</div>

How to keep data in component when using v-with in Vue js

So here is my problem:
I want to make a component that takes it's values from v-with="values" and add them to my component model after some modification, then display those modified properties.
But from what I understand, when I set values with "v-with", component data are erased so the binding between my component data (not v-with one) and my directives are lost.
I'm really new to this framework, I don't see any solution, so I guess it was time to ask my first question here !
Here is the HTML:
<script type="text/x-template" id="my-template">
<p v-on="click:reloadParentMsg">Msg parent : {{ParentMsg}}</p>
<p v-on="click:reloadChildMsg">Msg child : {{ChildMsg}}</p>
</script>
<div id="myVue">
<my-component v-with="ParentData" ></my-component>
</div>
And here is the Javascript:
Vue.component('my-component', {
template: '#my-template',
data: function () {
return {
ChildMsg: "wololo"
}
},
methods:{
reloadParentMsg : function(){
this.ParentMsg="Parent";
console.log(this.ParentMsg);
},
reloadChildMsg : function(){
this.ChildMsg="Child";
console.log(this.ChildMsg);
}
}
})
var myVue = new Vue({
el: '#myVue',
data: {
ParentData:{ParentMsg: "gloubiboulga"}
}
})
And the js fiddle http://jsfiddle.net/KwakawK/hfj1tv4n/3/
I'm not totally clear on what you're trying to do, but I believe it can be solved by using the second form of v-with, which is v-with="childProp: parentProp". Rather than the parent property overriding all of the child data, this will replace only the property on the left of the colon.
So I think your code can be fixed by changing the v-with to this:
<my-component v-with="ParentMsg: ParentData.ParentMsg" ></my-component>
Here's the updated code as a snippet:
// register the grid component
Vue.component('my-component', {
template: '#my-template',
data: function () {
return {
ChildMsg: "wololo"
}
},
methods:{
reloadParentMsg : function(){
this.ParentMsg="Parent";
console.log(this.ParentMsg);
},
reloadChildMsg : function(){
this.ChildMsg="Child";
console.log(this.ChildMsg);
}
}
})
// bootstrap the demo
var myVue = new Vue({
el: '#myVue',
data: {
ParentData:{ParentMsg: "gloubiboulga"}
}
})
<script src="http://cdnjs.cloudflare.com/ajax/libs/vue/0.11.4/vue.min.js"></script>
<script type="text/x-template" id="my-template">
<p v-on="click:reloadParentMsg">Msg parent : {{ParentMsg}}</p>
<p v-on="click:reloadChildMsg">Msg child : {{ChildMsg}}</p>
</script>
<div id="myVue">
<my-component v-with="ParentMsg: ParentData.ParentMsg" ></my-component>
</div>
See the Vue guide for more information.

Categories

Resources