I have an HTML page updated with ajax contents.
I'm using Vue.js for some dynamic front-end events.
Dynamically added elements don't interact with the Vue.js instance, even if I try to forceUpdate.
How could I do that?
<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.2.0/vue.js"></script>
<div id="app">
<button v-on:click="click()">click will console something</button>
</div>
<script>
var app = new Vue({
el: '#app',
methods: {
click: function() {
console.log('clicked')
},
}
});
setTimeout(function() {
$('#app').append('<button v-on:click="click()">click here wont do anything</button>');
app.$forceUpdate();
}, 1000);
</script>
That is not how you add elements in Vue. Your contract with Vue is that it will control the DOM and you will provide it a model that describes how things should appear and work.
Let go of jQuery. If you want a button to conditionally appear, have a variable that controls its appearance, and set that variable to true.
If you really, really have to deal with DOM being added outside of Vue, then you will need to call new Vue on the newly added elements to put them under Vue's control.
Related
I'm fairly new to Vue.JS and currently having an issue listening to a click event on a component.
JS:
Vue.component('photo-option', {
data: function () {
return {
count: 0
}
},
props: ['src'],
template: `
<img :src=src v-on:click="$emit('my-event')" />
`
});
HTML:
<photo-option :src=uri v-for='uri in aboutUsPhotos' v-on:my-event="foo" />
...where foo is a method on my main Vue instance.
The above is based on the Vue.JS docs for handling component events, and I can't see what I'm doing wrong. The alert doesn't fire, and there's no errors in the console.
Before I found those docs, I also tried simply adding v-on:click='...' to both the JS (i.e. the template) and the HTML, each with no success.
What am I doing wrong?
[EDIT]
This is happening because the code is picked up by a lightbox script and has its DOM position changed. So presumably the binding/event attachment is being lost.
Does Vue have any way of allowing for this, perhaps by 'reinitialising' itself on an element, or something?
Summary of my application scenario: I have created a Vuejs application which can work with different web applications. Vuejs application script included in any web page will replace a div element on integrating web page and on certain actions of user on the page, the Vuejs application renders response to hidden input fields and web application should listen to any change events on those hidden fields to determine any changes to response
Problem:
Native DOM events triggered inside Vuejs application are not captured by listeners outside Vuejs Instance
I have native DOM change events on input field triggered in Vuejs application and also triggered them on elements using { this.$refs } to refer the actual DOM element and then added listeners on the integrating web page for any change events but Listeners are not firing in IE and firefox though everything is working as expected in chrome.
Can someone suggest me any solution for addressing this issue ?
I am aware of listening using " vm.$on " outside Vue instance but I want to keep the client integration seam less to framework and use the native dom listeners to identify any change to input fields.
Here is code sample code to explain
Related issues found but didn't work for this case:
Is it possible to trigger events using Vue.js?
App component:- Added a hidden text field and a button that changes the value of the text field using javascript which in turn will trigger change event on the hidden field.
Web page: So when I am trying to listen to change events on a web page, listeners do not fire though change events are triggered inside Vuejs application via native events using javascript
<!DOCTYPE html>
<html>
<head title = "TestPage">
<script>
document.addEventListener('DOMContentLoaded',
function (event) {
var element = document.getElementById("test-field");
element.addEventListener('change', function () {
console.log("Change event captured")
})
})
</script>
</head>
<body>
<input id = "divelementidtoreplace" type = "text" >
</body>
</html>
<template>
<div id="app">
<input type="hidden" id = "test" v-model = 'value' ref="inputFieldToListen">
<button #click= "changeHiddenInputFieldvalue()" > click to change the text </button >
</div>
</template>
<script>
export default {
name: 'App',
data: {
value: ''
}
methods: {
changeHiddenInputFieldvalue: function () {
this.value = 'This assigned can put random value to test the trigger';
},
triggerChangeEvent: function (element) {
if (element) {
if ('createEvent' in document) {
var evt = document.createEvent('HTMLEvents')
evt.initEvent('input', true, true)
element.dispatchEvent(evt)
}
}
}
},
watch: {
value: function (NewValue, oldValue) {
this.triggerChangeEvent(this.$refs.inputFieldToListen)
},
}
}
</script>
main.js: -
import Vue from 'vue'
import App from './App.vue'
var vueInstance = new Vue({
el: '#app',
data: {
},
template: '<App />',
})
I'm trying to fit Vue.js inside an existing project that doesn't use Vue. But I could not find any resource or documentation on how I can write a Vue component that have an API that can be used by code outside of the Vue world. Everything is focused on apps built with Vue from top to bottom.
var MyV = Vue.extend({
template: `
<div>
<h4>{{ message }}</h4>
<ul>
<li v-for="item in items">
{{item}}
<button v-on:click="remove(item)">-</button>
</li>
</ul>
<button v-on:click="add">add</button>
</div>
`,
data() {
return {
message: 'Hello Vue.js!',
items: ['foo', 'bar'],
};
},
methods: {
add() {
this.items.push(Math.random());
},
remove(item) {
this.items.splice(this.items.indexOf(item), 1);
}
}
});
var v = new MyV({
el: c1
});
<script src="https://unpkg.com/vue#2.5.8/dist/vue.js"></script>
<div id="c1"></div>
What I already figured out just by playing around:
From outside of Vue, I can just mutate the v instance and the view changes according.
// everything works
v.message = "Hi there!";
v.items.push('hello');
v.items.add();
How I'm I supposed to listen to a "onchange" event? I can se a few ways of implementing this, but there's an "official" way?
Setting stuff is pretty straightforward, I can just set any property, or initialize it while instantiating, but how do I can get data back from this view?
Let's say I'm building a dropdown menu with Vue, I can populate this "component" just by setting a property.
If there's like a "submit" button inside this view, how I can notify the code outside of the view the user clicked on it?
If the view provides UI for the user modify its internal state (this.data) how code outside can be notified that the data has been modified, and it can then get the content of it again?
In the same way that you can call v.add() from outside the Vue code, you can watch value members from outside:
v.$watch('items', (newValue) => { console.log("Items changed!", newValue); });
You could also use v.$emit and v.$on to send and receive custom events, since every Vue instance is an event bus.
I am trying to use an external JS library that generates a drawing canvas (atrament.js) within a vue.js app.
I am not sure what is the right way of doing this. Right now I am just doing:
<script type="text/javascript">
var main = new Vue({
el: '#vueapp'
});
</script>
<script type="text/javascript">var atr = atrament("canvas", 500, 500);</script>
And with that the canvas is generated wherever I put the <canvas></canvas> tags.
However, this does not seem a very elegant option, and the atr var is not accessible for the vue app, for example for clearing the canvas. So, which is the right way of doing this?
Since Vue is a component-based UI library, you should try to think in components.
Consider wrapping that canvas in a component and your parent element will talk to it via props and events. Vue has a very clean documentation for that: https://v2.vuejs.org/v2/guide/components.html
You should have atr variable as a vue data variable, like following:
<script type="text/javascript">
var main = new Vue({
el: '#vueapp',
data: {
atr : atrament("canvas", 500, 500)
}
});
</script>
Now atr will be available in the vue app via this.atr and you can do required operations on it.
I have very small web page with emberjs, where I want to show some item list and openlayers map for them, and another map for selected item.
Something like that:
<script type="text/x-handlebars" id="list">
<div class="list">
<div id="list_map"></div>
</div>
{{outlet}}
</script>
<script type="text/x-handlebars" id="list/item" >
<div class="item">
<div id="item_map"></div>
</div>
</script>
<script>
function showListMap() {
listMap = new ol.Map({target:'list_map'});
}
function showItemMap() {
itemMap = new ol.Map({target:'item_map'});
}
</script>
There is no problem to display map for list:
model: function(params) {
var content = [];
var url = 'http://localhost:8080/app/list';
$.ajax({
url: url,
success: function(surveys) {
content.pushObjects(surveys);
showListMap();
}
});
return content;
}
and I have action in item controller that is executed, when opening selected item, but if I try to create item map there (in controllers action) it fails (afaik because there is no such div at that moment in DOM).
So if I could execute action or my function after div is already add to DOM, it should work.
And question would be, how to execute something after template is added to DOM, or that's completely wrong way to do such stuff (than what would be correct ember way)?
I can't say much with seeing full code. But to execute some code after the DOM is rendered you schedule a function on the the run loops afterRender queue.
Ember.run.scheduleOnce('afterRender', this, function() {
//The div should be available now.
});
But if you really need to touch the DOM I recommend you wrap your map code in a component. A component gets a didInsertElement where you can write your maps initialization code.
var component = Em.Component.extend({
setup: function() {
//Do you map init here.
}.on('didInsertElement')
});
There unfortunately isn't a really good route or controller hook that fires off after a page has already rendered. I believe the reason for this is that the developers of Ember think it is an anti-pattern to directly talk to the DOM.
That being said, I think it sometimes is quite handy for complex UI on otherwise static web pages. If you want to do some sort of jquery or use the DOM API after a route has rendered, you can do the following in your route file (as #Dainius correctly points out)
routeName.js
import Route from '#ember/routing/route';
import jQuery from 'jquery';
export default class myRouteFile extends Route {
manipulateDom = function() {
$("#myDiv").css( "color", "red" );
}
init() {
this._super(...arguments)
Ember.run.scheduleOnce('afterRender', this, this.manipulateDom)
}
}