Vue and Fullcalendar customizing day cell - vue.js

I need to add a button into every day cell. I know that I can do something similar to this:
dayCellContent: {
html: `<button> button text </button>`,
},
But i don't really wanna do that and I would much rather use my button component. Also like this I would have to add the click listeners without Vue which I also don't like very much.
So is there a better way to do this? Preferably just pass in the Vue component?

I solved this by using a component and initializing it using the Vue constructor like so.
const el = document.createElement("a");
const calendarDayCellContentContainer = new Vue({
el,
render: (h) =>
h(CalendarDayCellContent, {
props: {
buttonText: "whatever",
},
}),
});
and passing the button.$el as DOM node.
domNodes: [button.$el],

Related

Vuetify v-text-field not updating value when it is changed. Even on $forceUpdate()

Vuetify v-text-field not updating value when it is changed. Even on $forceUpdate() and when using $set
I have tried to use it without $forceUpdate and $set but even with both of these methods it does not work
Example what DOES work WITHOUT vuetify:
https://jsfiddle.net/gu273qy0/3/
Example of what DOES NOT work WITH Vuetify:
https://jsfiddle.net/gu273qy0/5/
This javascript can be used in both examples
new Vue({
el: "#app",
data: () => ({
todos: ['']
}),
methods: {
setText(index, todo){
if (todo.toString().match(/^((([A-Z]{3})[UJZ](\d{0,7}))|[A-Z]{0,3})$/)) {
this.$set(this.todos, index, todo)
}
this.$forceUpdate()
},
addText() {
console.log(this.todos)
this.todos.push('')
}
}
})
I expect that the exact same implementation works with vuetify and without vuetify. Unfortunately this is not the case. Only the non vuetify example works.
An example of a valid value or todo is AAAU0101201.
An example of a invalid value is AAAA
I cannot really explain why you have not the same results with or without Vuetify, but in fact, with Vuetify example, if you enter a wrong value, then the todos array won't be modified, and so the model won't be updated
I think you should for example handle #blur event:
https://jsfiddle.net/v6m2tyrs/1/

Vue loses prototypes and imported files when creating new component

When extending a Vue component to a modal, a new Vue instance is generated. This leads to losing all pre-imported helpers/ Vuex-stores etc.
Is there a way how to copy all data to the new extended Vue component?
To make it clear, have a look at this jsfiddle.
It confronts you with an alert. When you click on the button, the modal is opened. But if you look at my code, another alert should be fired. But $hi() is not available in the other Vue instance.
I create a component like this:
const createComponent = (props) => {
const wrapper = document.createElement('div');
const Component = Vue.extend(MyComponent);
return new Component({
propsData: {
props,
},
}).$mount(wrapper);
};
I'm using SweetAlert as a modal.
So I add this like:
swal({
content: createComponent(props).$el,
})
If you move the line
Vue.prototype.$hi = () => alert('hi');
above
let component = new TestComponent().$mount(wrapper)
it will work. Your first component references $hi() before it's defined.

Kendo grid details template and VueJS

I need to render a sub grid using Kendo UI. Here is an example from their site.
In their example, basically they construct the sub grid using jQuery, so I will lost the connection with my Vue component. In particular, that sub grid has to have some buttons to trigger other actions, like showing a modal and then removing some data.
I have a way to do this, but I want to know if there is any more "elegant" solution to my problem:
<script>
var myComponent = {
name: 'myComponent'
data() {
...
},
methods: {
test: function (data) {
// here I do something with the data
}
}
}
export default myComponent
</script>
by defining myComponent as a variable first, I can access it from my kendo commands:
$('<div />').appendTo(e.detailCell).kendoGrid({
dataSource: e.data.someArray,
columns: [
{
command: [
{
name: 'edit',
click: function (e) {
var data = this.dataItem($(e.target).closest('tr'))
templatesManager.methods.test(data) ---> HERE
}
}
]
}
]
I don't like the approach of having a reference to my component, and as I don't have much experience with Vue, I want to know if this is the right way to communicate between the grid and my component. Unfortunately, we cannot get rid of Kendo Grid. Here I found an example on how to connect between my vue component and AngularJS via events... is that a better option? this solution of having a reference to the component could cause any potential issues?

Difference between .$mount() and el [Vue JS]

What's the difference between this code:
new Vue({
data () {
return {
text: 'Hello, World'
};
}
}).$mount('#app')
and this one:
new Vue({
el: '#app',
data () {
return {
text: 'Hello, World'
};
}
})
I mean what's the benefit in using .$mount() instead of el or vice versa?
$mount allows you to explicitly mount the Vue instance when you need to. This means that you can delay the mounting of your vue instance until a particular element exists in your page or some async process has finished, which can be particularly useful when adding vue to legacy apps which inject elements into the DOM, I've also used this frequently in testing (See Here) when I've wanted to use the same vue instance across multiple tests:
// Create the vue instance but don't mount it
const vm = new Vue({
template: '<div>I\'m mounted</div>',
created(){
console.log('Created');
},
mounted(){
console.log('Mounted');
}
});
// Some async task that creates a new element on the page which we can mount our instance to.
setTimeout(() => {
// Inject Div into DOM
var div = document.createElement('div');
div.id = 'async-div';
document.body.appendChild(div);
vm.$mount('#async-div');
},1000)
Here's the JSFiddle: https://jsfiddle.net/79206osr/
According to the Vue.js API docs on vm.$mount(), the two are functionally the same, except that $mount can (optionally) be called without an element selector, which causes the Vue model to be rendered separate from the document (so it can be appended later). This example is from the docs:
var MyComponent = Vue.extend({
template: '<div>Hello!</div>'
})
// create and mount to #app (will replace #app)
new MyComponent().$mount('#app')
// the above is the same as:
new MyComponent({ el: '#app' })
// or, render off-document and append afterwards:
var component = new MyComponent().$mount()
document.getElementById('app').appendChild(component.$el)
In the example you provide, I don't believe there is really much difference or benefit. However, in other situations there may be a benefit. (I have never encountered situations like the following).
With $mount() you have more flexibility what element it will be
mounted on if that were to ever be necessary.
Similarly you if for some reason you need to instantiate the
instance before you actually know what element it will be mounted on
(maybe an element that is created dynamically) then you could mount
it later using vm.$mount()
Following along with the above you could use also use mount when you
need to make a decision before hand which element to mount to
assuming that there may be two or more possibilities.
Something like...
if(document.getElementById('some-element') != null){
// perform mount here
}
Top answer is good enough. just left a comment here as I don't have enough reputation points. Alternativley:
setTimeout(() => {
const element = document.createElement('div');
document.body.appendChild(element);
vm.$mount(element);
}, 0)
Besides all the great answers here that say using $mount is better, another thing I would like to add is about the correctness of your code for when it gets evaluated by clean-code-tools (like SonarQube etc).
In short, the code is cleaner when using $mount separately. Because otherwise the clean-code-tools complains:
"Objects should not be created to be dropped immediately without being used"
So in this example the clean-code-tool complains:
new Vue({
el: '#some-id',
...
})
The solution is:
let instance = new Vue({
...
})
instance.$mount('#some-id')
And so everybody is happy again

Vue 2 hook ready

Vue 2, Is there a lifecycle hook that actually refers to "finished rendering"? I want to put a loading screen when entering a page and it fades away and show the page content once Vue is finished loading everything, but I have tried most of the lifecycle hook but not working. Here is something I would try to do if updated refers to "finished rendering":
updated(){
this.loaded()
},
methods:{
loaded(){
var vm = this;
this.loading = false;
}
}
If there is not such a lifecycle hook, what would you suggest me to do instead? Thanks
Probably the method you're looking for is mounted(), this is fired when the vue component is ready. You can check the Vue life-cycle documentation here
so probably your Vue instance would look something like this:
var app = new Vue({
el: '#app',
/*
* When the instance is loaded up
*/
mounted: function () {
this.loaded()
},
methods: {
loaded: function () {
var vm = this
this.loading = false
}
}
})
To make sure that all child components have also been mounted, use vm.$nextTick - much cleaner than a setTimeout:
mounted: function () {
this.$nextTick(function () {
// Code that will run only after the
// entire view has been rendered
})
}
Source: https://v2.vuejs.org/v2/api/#Options-Lifecycle-Hooks
As I cannot find that there is a specific lifecycle hook or other Vue-specified method to hook the point where everything is finished rendering, I used the basic js code as well as estimation of time needed for loading all the things with codes as following:
mounted(){
this.loaded()
},
methods:{
loaded(){
var vm = this;
setTimeout(function(){
vm.loading = false
}, 3000);
}
}
Hope there will be someone who is strong at Vue and provide a new answer to hook it more accurately.
Actually, your answer to your question is not what you have asked originally, the Vue instance is rendered in less than a second and it's not possible to use any hooks only to create userfriendly loader.
The case in using setTimeout function is the only solution for this. But the component is ready in DOM a way earlier than 3 seconds.
The best approach for this purpose is to use loader component, where you will have setTimeout in created hook and after this time it will emit event to the parent.
So inside app you need only to import the loader component itself and when the event fires, change internal boolean data property to render another component v-if