Vuetify not showing when i use jQuery append - javascript

I'm having trouble using jquery append with vuetify. I can't write any vuetify code in my javascript. It wouldn't show me the vuetify code, but it should work. I think it has something to do with vuetify having to be restarted when you want to insert a new vuetify code.
new Vue({
el: '#app',
vuetify: new Vuetify(),
methods: {
createList() {
if (this.$refs.form.validate()) {
$('#lists').append(`
<v-card width="374" class="mx-10 my-12" elevation="5">
<v-system-bar color="${this.color.hexa}" dark><b>${this.name}</b></v-system-bar>
<v-form ref="form" lazy-validation>
<v-card-actions>
<v-text-field v-model="name" ref="name" class="mx-5" :rules="jobsTitleRules" label="Title" required></v-text-field>
<v-btn color="success" #click="addTitle">Add</v-btn>
</v-card-actions>
<ul style="padding: 0px;" id="jobs"></ul>
</v-form>
</v-card>`);
}
},
},
})
Searched everywhere and really can't find a solution. Anyone who can help?

As I mentioned in comments, jQuery append function was designed to add native HTML, native JS, or jQuery objects.
It is not possible to use it with Vue/Vuetify because you are trying to show virtual template components which are not native.
In your case you can use a list of Vue dynamic components.
First of all, you need to create your dynamic component (I simplified your template a little):
const externalForm = Vue.component('external-form', {
props: {
name: String,
color: String,
},
data() {
return {
internalName: this.name,
}
},
methods: {
addTitle() {
this.$emit("emit-title", this.internalName)
}
},
template: `
<v-card width="374" class="mx-10 my-12" elevation="5">
<v-system-bar :color="color" dark><b>{{name}}</b></v-system-bar>
<v-card-actions>
<v-text-field v-model="internalName" class="mx-5" label="Title"></v-text-field>
<v-btn color="success" #click="addTitle">Add</v-btn>
</v-card-actions>
</v-card>`,
});
When you need to pass some data to component, you should use props. For internal data you can use data section. And if you need to return some data, you can emit some event.
Now let's see how we can use it in your main template. Assume you already have some list of <v-list-item> components. You need to add some items into the tail of the list. This way, by example:
<v-btn #click="addItem" color="primary">Click here to add item</v-btn>
...
<v-list-item v-for="(item, key) in items" :key="key">
<component :is="item.component" v-bind="{...item.props}" #emit-title="pushToList"/>
</v-list-item>
And finally you need to define your main component:
new Vue({
el: '#app',
vuetify: new Vuetify(),
data() {
return {
items: [],
titles: []
}
},
methods: {
pushToList(val) {
this.titles.push(val)
},
addItem() {
this.items.push({ component: 'external-form', props: {
name: 'Some name',
color: '#' + Math.floor(Math.random() * 16777215).toString(16) //Just a random color HEX
}})
}
}
})
I hope now you can apply this method to your business logic.
CodePen playground is available here.

Related

Elegant way to use dynamic components with props in Vue

Following case: I have an API outputting all the content for my Page, as well as its structure and so on (for better understanding, imaging an CMS which includes kind of a page builder, where an author can place components by drag and drop to generate pages content, which is delivered to the front-end by that api).
The structure of the api output would look something like:
{content: [
{component: hero, content: {...} },
{component: form, content: {...} },
...
]}
So to generate related content I would think of using dynamic components like:
<template v-for="item in content">
<component :is="item.component" />
</template>
However, doing so I would face the problem, that I have to add properties data onto my components somehow, which (as far as I could see) isn't described within the Vue documentation. So now I wonder how to pass props onto dynamic components, which have entirely different props (hero might have an image, form could have input-placeholders, and so on) - any ideas???
Take a look at v-bind https://v2.vuejs.org/v2/guide/components-props.html#Passing-the-Properties-of-an-Object (same as Vue 3).
Assuming your API includes a props property for each component, then you'd do this:
<component v-for="item in content" :is="item.component" v-bind="item.props"></component>
Nowadays it's necessary to state what version of Vue do you work with.
With Vue 2 you could do it like this:
Vue.component('FirstComponent', {
props: ['title', 'prop1_1'],
template: `
<div>
{{ title }}<br />
{{ prop1_1 }}
</div>
`
})
Vue.component('SecondComponent', {
props: ['title', 'prop2_1', 'prop2_2'],
template: `
<div>
{{ title }}<br />
{{ prop2_1 }}<br />
{{ prop2_1 }}
</div>
`
})
new Vue({
el: "#app",
data() {
return {
items: [
{
component: 'FirstComponent',
props: {
title: 'First component title',
prop1_1: 'this is prop 1_1'
},
},
{
component: 'SecondComponent',
props: {
title: 'Second component title',
prop2_1: 'this is prop 2_1',
prop2_2: 'this is prop 2_2',
},
},
]
}
},
template: `
<div>
<component
v-for="(item, idx) in items"
:key="idx"
:is="item.component"
v-bind="item.props"
></component>
</div>
`
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app"></div>

Passing dynamically a default date for v-date-picker from parent component and returning the changed date from child component

I have a custom component using the v-date-picker which is used in a lot of places. I want to be able to dynamically set the "default" date picked from the parent component, while being able to modify the date from the child component.
Here's the code in the parent component:
<template>
<DatePickerMenu #selectedDate="selectedExpirationDate" :selectedDate="this.date"></DatePickerMenu>
</template>
<script>
data() {
return {
date: '2021-04-29', //used for testing, will eventually come from a certain calculation inside this parent component
}
},
methods: {
selectedExpirationDate(value) {
this.expiration_date = value;
},
},
</script>
In the child component:
<template>
<v-menu
ref="datePickerMenu"
v-model="datePickerMenu"
:close-on-content-click="false"
:return-value.sync="selectedDate"
transition="scale-transition"
offset-y
min-width="auto"
>
<template v-slot:activator="{ on, attrs }">
<v-text-field
class="form"
v-model="selectedDate"
label="Expiration date *"
hint="Minimum expiration date: one week from today"
prepend-icon="mdi-calendar"
readonly
v-bind="attrs"
v-on="on"
:rules="requiredRules"
></v-text-field>
</template>
<v-date-picker
v-model="selectedDate"
no-title
scrollable
color="primary"
>
<v-spacer></v-spacer>
<v-btn
text
color="primary"
#click="datePickerMenu = false"
>
Cancel
</v-btn>
<v-btn
text
color="primary"
#click="$refs.datePickerMenu.save(selectedDate)"
>
OK
</v-btn>
</v-date-picker>
</v-menu>
</template>
<script>
export default {
name: "DatePickerMenu",
data () {
return {
datePickerMenu: false,
//selectedDate: this.setSelectedDate, and changing the 'selectedDate' props to setSelectedDate
}
},
props: ['selectedDate'],
watch: {
selectedDate: function() {
this.$emit('selectedDate', this.selectedDate);
},
},
}
When I do this, the date-picker shows the correct date passed from the parent component, but when I change the selected date, the following message appears:
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "selectedDate"
So as you can see, I tried setting a local data with the passed props //selectedDate: this.setSelectedDate, but when I do so, the default selected date works the first time, but when it's changed in the parent component, it won't update in the child's.
Hopefully my problem is clearly explained.. Any solution ideas?
Thanks in advance.
You can use the .sync modifier like so:
in your parent:
<DatePickerMenu
:selectedDate.sync="this.date"
#selectedDate="selectedExpirationDate"
/>
and in your child component, create a computed like so:
<v-date-picker
v-model="selectedDateComputed"
no-title
scrollable
color="primary"
>
computed: {
selectedDateComputed: {
get(): {
return this.selectedDate;
}
set(newDate): {
this.$emit('update:selectedDate', newDate);
}
}
}
of course you need to use this for v-text-field as well.
you can see the vue sync modifier docs for more information.
Seems to be working like a charm, big thanks!
FYI, it was the perfect approach, but there were some syntax errors:
computed: {
selectedDateComputed: {
get: function () {
return this.selectedDate;
},
set: function(newDate) {
this.$emit('update:selectedDate', newDate);
}
},

Passing dynamic boolean props to a VueJS component

How can I attach dynamic properties to a VueJS Component using VuetifyJS?
I have the following VuetifyJS code example that creates a select field element:
<div id="app">
<v-app id="inspire" style="padding: 10px; ">
<v-select
v-model="selectField"
:items="items"
multiple attach chips>
</v-select>
</v-app>
</div>
new Vue({
el: '#app',
vuetify: new Vuetify(),
data () {
return {
selectField: '',
items: [
'item1',
'item2',
'item3'
],
booleanProperties: [
'multiple',
'attach',
'chips'
]
}
},
})
This creates a functional VuetifyJS select component, however I want to know how to pass the boolean props multiple, attach, chips to the select element as data properties so they do not have to be specified explicitly in the component declaration.
For example: I want to add the props: multiple, attach, chips defined within the data array element booleanProperties while still being able to define the component without having them specified. This way it works dynamically for any prop I pass.
Something similar to the following pseudocode example.
<v-select
v-model="selectField"
:items="items"
v-for="(booleanProperties) as boolProp">
</v-select>
How can I pass/specify the data props: multiple, attach, chips dynamically for the v-select element?
Here is a code example of what I am referring to.
https://codepen.io/deftonez4me/pen/NWRLWKE
You can simply use v-bind without specifying the key/property, and then passing an object into it, i.e. v-bind="additionalProps". As per VueJS v2 documentation on v-bind:
When used without an argument, can be used to bind an object containing attribute name-value pairs.
You can also merge your items binding into the object returned by additionalProps then, for brevity. Example based on your code.
new Vue({
el: '#app',
vuetify: new Vuetify(),
data () {
return {
selectField: '',
additionalProps: {
items: [
'item1',
'item2',
'item3'
],
multiple: true,
attach: true,
chips: true
}
}
}
});
<link href="https://cdnjs.cloudflare.com/ajax/libs/vuetify/2.3.16/vuetify.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuetify/2.3.16/vuetify.min.js"></script>
<div id="app">
<v-app id="inspire" style="padding: 10px; ">
<v-select
v-model="selectField"
v-bind="additionalProps">
</v-select>
</v-app>
</div>
You can go simply as this:
<div id="app">
<v-app id="inspire" style="padding: 10px; ">
<v-select
v-model="selectField"
:items="items"
:multiple="multiple"
:attach="attach"
:chips="chips">
</v-select>
</v-app>
</div>
Then you can declare the props as before:
props: [
'multiple',
'attach',
'chips'
]
Or a bit more specific like this:
props: {
multiple: {
type: Boolean
},
attach: {
type: Boolean
},
chips: {
type: Boolean
}
}

How to creat a node for activator attribute in Vuetify?

Vuetify give props activator in many components such as v-menu or v-dialog,but there are no more details about how to creat a node to work properly.
The document describe like this
Designate a custom activator when the activator slot is not used. String can be any valid querySelector and Object can be any valid Node.
But i use querySelector to pick a simple element and it didn't work,any additional property should i add?
As far as I know, there is no additional props. The activator prop accept 3 different types.
With Selector:
<v-app>
<v-btn class="my-btn">Dropdown</v-btn>
<v-menu activator=".my-btn">
<v-list>
...
</v-list>
</v-menu>
</v-app>
Example
With Component:
<v-app>
<v-btn ref="myBtn">Dropdown</v-btn>
<v-menu :activator="myBtnRef">
<v-list>
...
</v-list>
</v-menu>
</v-app>
new Vue({
data: () => ({
myBtnRef: null,
...
}),
mounted() {
this.myBtnRef = this.$refs.myBtn
}
}).$mount('#app')
Example
With HTMLElement:
<v-app>
<v-btn>Dropdown</v-btn>
<v-menu :activator="myBtn">
<v-list>
...
</v-list>
</v-menu>
</v-app>
new Vue({
data: () => ({
myBtn: null,
...
}),
mounted() {
let button = document.createElement('button')
button.textContent = 'Dropdown'
document.body.insertBefore(button, document.body.firstChild)
this.myBtn = button
}
}).$mount('#app')
Example

How to commit mutations in a Vuex store, from a Vuetify list, VueJS

This follows on from my previous question : How to get states from a Vuex store from within a Vuetify list, VueJs
I understand how to get states from the store, but I am struggling commiting mutations from within a list.
Here is my list :
export default{
name: 'menuList',
data() {
return {
show: true,
items: [{
title: 'Do something',
icon: 'web',
event: function () {
this.$store.commit('UI/DoSomething', {
argument1: "argument1",
rootStore: this.$store
})
}
}
]
}
}
}
I have to have the event in a function as, if I don't, the commit will run straight away. I only want it to run when I click the button.
Here is my list creation :
<template>
<v-navigation-drawer v-model="show" right clipped app fixed hide-overlay permanent="true">
<v-list two-line>
<template v-for="(item, index) in items">
<v-list-tile :key="item.title" #click="item.event()">
<v-list-tile-action>
<v-icon>{{item.icon}}</v-icon>
</v-list-tile-action>
<v-list-tile-content>
<v-list-tile-title v-html="item.title"></v-list-tile-title>
</v-list-tile-content>
</v-list-tile>
<v-divider :key="index"></v-divider>
</template>
</v-list>
</v-navigation-drawer>
</template>
When I wrap the commit with a function (so it doesn't execute automatically), the keyword 'this' refers to the object, not the Vue.
I'm sure I'm missing something simple here, any ideas ?
Why do you want to store a function to commit a mutation in the data option?You can just store the information needed to commit a mutation.
export default{
name: 'menuList',
data() {
return {
show: true,
items: [{
title: 'Do something',
icon: 'web',
mutation: {
type: 'UI/DoSomething',
arguments:{
argument1: "argument1",
rootStore: this.$store
}
}
}
}
]
}
}
}
Then create a method as follows
commitMutation(mutation) {
this.$store.commit(mutation.type, mutation.arguments)
}
Then add a click listener with item.mutation as an argument to commitMutation method.
#click="commitMutation(itrm.mutation)"

Categories