Trying to build a component that works with daepicker and using v-model to bind the input value. But the input event does not appear to be firing and I can’t seem to figure out why. Here’s my component:
<div id="app">
<datepicker v-model="date"></datepicker>
</div>
Vue.component('datepicker', {
template: '<input type="text" class="form-control pull-right" placeholder="dd/mm/aaaa" autocomplete="off">',
mounted: function() {
$(this.$el).datepicker({
autoclose: true,
startView: 'years',
}).on('changeDate', function(e) {
this.$emit('input', e.format('dd/mm/yyyy'));
});
},
destroyed: function () {
$(this.$el).datepicker('destroy');
}
});
var app = new Vue({
el: '#app',
data: {
date: '2018-03-01'
}
})
In addition, the following error appears in the console:
Uncaught TypeError: this.$emit is not a function
If you're mixing jQuery and Vue (just a guess from the code fragment), you're mixing up your contexts. One (of many) ways to fix:
mounted: function() {
const self = this;
$(this.$el).datepicker({
autoclose: true,
startView: 'years',
}).on('changeDate', function(e) {
self.$emit('input', e.format('dd/mm/yyyy'));
});
},
I failed with jacky's answer, but thanks to https://github.com/Xelia, problem sovled (even in Vue 1.0, using ready life cycle instead of mounted)
Manually update vue data in datepicker changeDate event listener, like this
var app = new Vue({
el: '#app',
data: {
startDate: '',
},
mounted() {
$("#startDate").datepicker().on(
"changeDate", () => {this.startDate = $('#startDate').val()}
);
},
})
https://jsfiddle.net/3a2055ub/
And by the way, if you are working on legacy company project using ES5 function instead of ES6 fat arrow function. Need to bind this, which is vue instance, into the function. For example:
mounted() {
var self = this; // the vue instance
$("#startDate").datepicker().on(
"changeDate", function() {self.startDate = $('#startDate').val()}
);
},
Of course there are other ways to reach this goal, as this blog written by Jason Arnold
shows.
Reference: https://github.com/vuejs/vue/issues/4231
Probable related question: v-model not working with bootstrap datepicker
Related
I am new to VueJs and I am working on a form that I want to enable the Save button only when a change occurs at the model.
My initial thought is to compute a dirty function comparing the initial model with the current.
Note: This code is not tested, it's here just for an example.
var app = new Vue({
el: '#app',
data: {a:0, b:'', c:{c1:null, c2:0, c3:'test'}},
initialData: null,
mounted():{ initialData = JSON.parse(JSON.stringify(data));},
computed: {
isDirty: function () {
return JSON.stringify(data) === JSON.stringify(initialData)
}
}
});
Is there a better way of doing this or is there any improvement you could suggest on the above-mentioned code?
You can use the deep option of watch as shown in the manual
var app = new Vue({
el: '#app',
data:
{
model:
{
a:0,
b:'',
c:
{
c1:null,
c2:0,
c3:'test'
}
},
dirty: false
},
watch:
{
model:
{
handler(newVal, oldVal)
{
this.dirty = true;
},
deep: true
}
}
});
Borrowing from -- > https://stackoverflow.com/a/48579303/4050261
You can bind single onchange event on the parent container and benefit from the fact that change events bubble:
<div class="container" #change="someThingChanged()">
<input v-model="foo">
<input v-model="bar">
... etc.
</div>
I want to update time without using setInterval function in Vue. Is there any way? It's work with setInterval like the code below. However, I want something inbuilt in vue or any other best/different way to do it.
new Vue({
el: '#app',
data: {
seconds: '',
},
mounted:function(){
setInterval( () => {
this.seconds= new Date().getSeconds();
}, 100);
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id="app">
Realtime Second = {{seconds}}
</div>
For things that need to change after a single update cycle we have this.$nextTick. This is, however, not really useful for a timer. That makes window.setInterval the next best thing.
Keep in mind that just like with manually added event handlers, timeouts and intervals must be manually cleared in a lifecycle hook. Your current code should be something like:
new Vue({
el: '#app',
data: {
seconds: (new Date).getSeconds(),
intervalRef: null
},
mounted () {
this.intervalRef = window.setInterval(() => {
this.seconds = (new Date).getSeconds();
});
},
beforeDestroy () {
if (this.intervalRef) {
window.clearInterval(this.intervalRef);
this.intervalRef = null;
}
}
});
I am pretty new to VueJs, so I am still figuring things out.
Since our templates are stored in the database, I want my templates to load async. For my components I now use the component-factory approach.
var c = Vue.component('my-async-component', function(resolve, reject){
setTimeout(function(){
resolve({
template: '<div class="loader">loaded asynchronous: {{ pageName }}</div>',
data() {
return {
pageName: 'my Page'
}
}
})
},2000)
})
But is it possible to have some kind of placeholder while loading it? I know I can do something with But in that case I need to have a parent component and I would like this to be independent.
On a Vue-instance you can do stuff in the render function end hook it up to mounted like:
var app = new Vue({
el: '#app',
data: {
finish: false,
template: null
},
render: function(createElement) {
if (!this.template) {
return createElement('div', 'loading...');
} else {
return this.template();
}
},
mounted() {
var self = this;
$.post('myUrl', {foo:'bar'}, function(response){
var tpl = response.data.template;
self.template = Vue.compile(tpl).render;
})
}
})
Is this possible in a component? And is this still working when I have some nested divs (see an other question of mine: here)
Ok, I figured it out. I just needed to reed de VUE guide a little bit bettter.
I just followed the advanced async example from the docs and now I have a working example.
So I have my template like this:
<div id="app">
<my-async-component></my-async-component>
</div>
Then in my JS I declared the template like:
var c = Vue.component('my-async-component', function(){
return {
component: new Promise(function(resolve, reject){
// setTimeout to simulate an asynchronous call
setTimeout(function(){
resolve({
template: '<div class="loader">loaded asynchronous</div>'
})
},3000)
}),
loading: Vue.component('loader', {
template: '<p>loading...</p>'
}),
error: Vue.component('load-error', {
template: '<p>error loading component</p>'
}),
delay: 200,
timeout: 10000
}
})
var vm = new Vue({
el: '#app'
});
The loading and error components could also be globally registered components, so it's easy to reuse.
Hopefully I could help someone with this answer to my own question.
How can I use a computed property in the data or emit it via bus?
I have the following vue instance, but myComputed is always undefined but computedData is working correctly.
var vm = new Vue({
data(){
return{
myComputed: this.computedData
}
},
computed: {
computedData(){
return 'Hello World'
}
}
})
Unfortunately, it is impossible to use computed property in data because of component creation timing: data evaluates Before computed properties.
To make things as simple as possible, just do the work in watcher, unless you want to emit the changes to different components or there're a lot of variables you want to notify, then you may have to use Vuex or the event bus:
var vm = new Vue({
data(){
return{
myComputed: '',
computedData: 'Hello World'
}
},
created() {
this.myComputed = this.computedData;
},
watch: {
computedData() {
this.myComputed = this.computedData;
}
}
});
Computed is already accessible in the template using {{ }}.
But you can use the
watch:{
//your function here
}
instead of computed
If you are using computed/reactive objects then it should be inside the computed and not inside the data.
Simply change your code to use computed instead of data
var vm = new Vue({
data(){
return{}
},
computed: {
computedData(){
return 'Hello World'
},
myComputed(){
return this.computedData
}
}
})
you are trying to use data as computed and this shall not be.
data doesn't act like computed object.
and it's not because of component creation timing. What if we changed the component creation timing ? this will not solve anything as data will take only the first computed value(only one) and will not update after.
you can work just with the computed function
var vm = new Vue({
data(){
return{
//is not necessary
}
},
computed: {
computedData(){
return 'Hello World'
}
}
})
and in your template
<template>
<div>{{ computedData }}</div>
</template>
You are over-coding it. Computed props are accessible in the same manner as data props in your template.
var vm = new Vue({
computed: {
myComputed(){
return 'Hello World'
}
}
})
In the template you have access to this just like you do to data:
<template>
<div>{{ myComputed }}</div>
</template>
https://v2.vuejs.org/v2/guide/computed.html
Try to convert the computed in a method
var vm = new Vue({
data(){
return{
myComputed: this.computedData
}
},
methods: {
computedData(){
return 'Hello World'
}
}
})
This is simple and it works (NOT reactive), but has a cost:
https://medium.com/notonlycss/the-difference-between-computed-and-methods-in-vue-js-9cb05c59ed98
computed is not available at the time data gets initialized.
If it should be a one-time thing (and NOT reactive), you could achieve this by setting the data at the moment where the computed property is available by using the created() hook:
export default {
data: () => ({
myDataBackend: '',
}),
computed: {
computedData () {
return 'Hello World'
}
},
created() {
this.$set(this, 'myDataBackend', this.computedData)
}
}
Futher reading: Vue Documentation on Lifecycle Hooks
In case you are trying to work with v-model:
You could also use :value and some event like #change or #keyup in the element instead.
:value is the value which the input-element initially works with
After writing some letter in the input field, the #keyup event changes the data.
Typically, events carry the updated form value in target.value
The changeMyData method sets the value
the computed property listens to the data change and the :value of the input field gets updated.
Note: I used data as a data store. But you could also use for example vuex instead.
<template>
<div>
<input
type="text"
:value="computedData"
#keyup="changeMyData"
/>
<p>{{myDataBackend}}</p>
</div>
</template>
<script>
export default {
data: () => ({
myDataBackend: 'Hello World'
}),
methods: {
changeMyData(evt) {
this.$set(this, 'myDataBackend', evt.target.value)
console.log('Changed the value to: ' + evt.target.value)
}
},
computed: {
computedData () {
return this.myDataBackend
}
}
}
</script>
How do can you compile a HTML string to template within a component method?
This was possible in Vue 1 like in this jsfiddle
new Vue({
el: '#app',
data: {
sampleElement: '<button v-on="click: test()">Test</button>'
},
methods:{
addNewElement: function(){
var element = $('#app').append(this.sampleElement);
/* compile the new content so that vue can read it */
this.$compile(element.get(0));
},
test: function(){
alert('Test');
}
}
});
How can you do this in Vue 2?
This is no longer possible, however, Vue 2 is data driven, so you shouldn't be trying to affect the DOM manually at all, instead you should bind elements to the underlying data in your view model. With that in mind your example will need to be re-written. Firstly, start by making your element a component:
Vue.component('my-button', {
template: '<button v-on:click="test()">{{text}}</button>',
props: ['text'],
methods: {
test() {
alert('Test');
}
}
});
Then you can create your main view model and add your button component using a v-for:
Markup:
<button v-on:click="addNewElement()">Add Element</button>
<my-button v-for="button in buttons" :text="button"></my-button>
View model:
new Vue({
el: '#app',
methods: {
addNewElement: function() {
this.buttons.push('Test');
}
},
data: {
buttons: []
}
});
In this example we are pushing buttons to an array that will then be displayed on the page, rather than manually appending them to the template.
Here's the JSFiddle: http://jsfiddle.net/10q9je5a/
If you want something more generic, then you can simply create an array of different components and use :is to let Vue know which component to render:
Markup:
<div id="app">
<button v-on:click="addNewElement()">Add Element</button>
<div :is="element.component" v-for="element in elements" v-bind="element.props"></div>
</div>
View Model:
new Vue({
el: '#app',
methods: {
addNewElement: function() {
this.elements.push({component: 'my-button', props: {text: 'Test'}});
}
},
data: {
elements: []
}
});
Here's the JSFiddle for that: http://jsfiddle.net/cxo5eto0/