VueJS dynamic adding form components - vue.js

I'm trying to make dynamic form, so if i want to add another text or some other field i can do it through web and save that to some json file.
Here is my code so far:
<template>
<div class="q-pa-md" style="max-width: 800px">
<div class="col-2">
<q-btn color="primary" label="Add Field" #click="addField = true"/>
</div>
<q-dialog v-model="addField">
<q-card>
<q-card-section>
<div class="text-h6">Add Field</div>
</q-card-section>
<q-separator />
<q-card-section style="max-height: 50vh" class="scroll">
test
</q-card-section>
<q-separator />
<q-card-actions align="right">
<q-btn flat label="Cancel" color="primary" v-close-popup />
<q-btn flat label="Add" color="primary" v-close-popup />
</q-card-actions>
</q-card>
</q-dialog>
<q-separator spaced inset />
<q-list>
<q-item>
<q-item-section>
<s-input v-model="data.email" label="Email" required />
<q-item-label caption lines="2">Email field.</q-item-label>
</q-item-section>
<q-item-section side top>
<div>
<q-toggle
v-model="data.active"
checked-icon="check"
color="blue"
unchecked-icon="clear"
/>
<q-icon name="keyboard_arrow_up" color="blue" size="md"/>
<q-icon name="keyboard_arrow_down" color="blue" size="md"/>
</div>
</q-item-section>
</q-item>
<q-separator spaced inset />
<q-item>
<q-item-section>
<s-input v-model="data.username" label="Username" required />
<q-item-label caption lines="2">Username field.</q-item-label>
</q-item-section>
<q-item-section side top>
<div>
<q-toggle
v-model="data.active"
checked-icon="check"
color="blue"
unchecked-icon="clear"
/>
<q-icon name="keyboard_arrow_up" color="blue" size="md"/>
<q-icon name="keyboard_arrow_down" color="blue" size="md"/>
</div>
</q-item-section>
</q-item>
<q-separator spaced inset />
<q-item>
<q-item-section>
<s-input v-model="data.password" label="Password" type="password" required />
<q-item-label caption lines="2">Password field.</q-item-label>
</q-item-section>
<q-item-section side top>
<div>
<q-toggle
v-model="data.active"
checked-icon="check"
color="blue"
unchecked-icon="clear"
/>
<q-icon name="keyboard_arrow_up" color="blue" size="md"/>
<q-icon name="keyboard_arrow_down" color="blue" size="md"/>
</div>
</q-item-section>
</q-item>
</q-list>
</div>
</template>
<script>
export default {
data () {
return {
newItem: true,
titleAction: null,
title: null,
titleHideEvent: false,
addField: false,
data: {
active: true
}
}
}
}
</script>
these are all field i have. Now if user wants to add another checkbox he should open dialog with button Add Field and choose that type of component and after he confirm that new chekcbox should be added in form. But i don't know how to make it dynamic. This 3 field should be mandatory and show in all forms (email, username and password).
Can anyone please help with some advice about this?

That's a huge template you got ^^"
Think that you can success by adding counters in your component's data.
Each time the User want to add a checkbox, you increment one of your counter depending on his choices. If the User wants to add a field for name for example, increment counterOfNameFields. In your template you can loop over these counters to display the right amount of fields.
Also, make a function that will push the content of these fields in arrays for example. You should have the same amount of arrays as counters.
Hope this answer your problem, tell me if it's not the case :)

You can have a list for added fields
data: function {
return {
...
addedItems:[],
...
}
}
And in your template
<q-item v-for="item in addedItems>
some html, which depends on how you define your items in your code
</q-item>
When you add an item to the page you just push it into
addedItems
collection and when you remove it from the page you remove it from collection.

I think this may be helpful https://v2.vuejs.org/v2/guide/components.html#Dynamic-Components
You can use v-bind:is="'yourComponentName'"
And you can store list of dynamic components and their options in some array.
Something like this (of course it's only example)
<component v-for="component in components" v-bind:is="component.name" v-bind:someoptions="component.options">
</component>

Related

Handling button in multiple Cards

I've product listing page where I use multiple ProductCard component. I need to add Spinner to Add to cart button when clicking on it, but if I just add Spinner to v-else I've Spinners in every ProductCard. How to control only this ProductCard where I click Add to cart button?
Parent's component:
<ProductCard
v-for="product in products"
:key="product.id"
:product="product"
/>
Child's component:
<template>
<div id="productCart">
<div>
<NuxtLink :to="`/product/${item.slug}`" />
<NuxtLink v-if="item.name" :to="`/product/${item.slug}`">
<h5
id="productName"
v-text="item.name"
/>
</NuxtLink>
<Button
v-if="!loading"
#click="addToBuyCart({ product: item })"
v-text="$t('labels.buy')"
/>
<Spinner v-else />
</div>
</div>
</template>
loading is defined in composable, and it's updating in addToBuyCart function by making it true, then adding to cart (api), then making it false
As pointed by IVO, in such situations you should check against a value specific for each item in the loop, item ID for example, instead of a boolean which lacks specificity and therefore cannot be used for such scenarios.
<template>
<div id="productCart">
<div>
<NuxtLink :to="`/product/${item.slug}`" />
<NuxtLink v-if="item.name" :to="`/product/${item.slug}`">
<h5
id="productName"
v-text="item.name"
/>
</NuxtLink>
<Button
v-if="loading !== item.id"
#click="addToBuyCart({ product: item })"
v-text="$t('labels.buy')"
/>
<Spinner v-else />
</div>
</div>
</template>
const addToBuyCart = ({product}) => {
loading = product.id
// some logic or request
loading = null
}

How to add icon beside the lable using Vue Bootstrap

I have this vue bootstrap input field:
<b-form-group label="Name" label-for="name">
<validation-provider
#default="{ errors }"
name="Mapping Name"
rules="required"
>
<b-form-input
v-model="mappingData.mapping_name"
:state="errors.length > 0 ? false : null"
id="mappingName"
placeholder="Enter a maping name"
/>
<small class="text-danger">{{ errors[0] }}</small>
</validation-provider>
</b-form-group>
The output is look like this :
Now, beside ( Right side of the name ) the Name label I want to add a icon which is:
<feather-icon
icon="AlertCircleIcon"
class="mr-50 my-icon"
v-b-tooltip.hover.top="'Some tooltip text'"
/>
So for that I have added this after the : <b-form-group label="Name" label-for="name">. Now it look like this:
But its should be beside the name label.
Is there any way to do this?
Yes. You can use the template slot label of b-form-group like this:
<b-form-group label-for="name">
<validation-provider
#default="{ errors }"
name="Mapping Name"
rules="required"
>
<template slot="label">
Name
<feather-icon
v-b-tooltip.hover.top="'Some tooltip text'"
icon="AlertCircleIcon"
class="mr-50 my-icon"
/>
</template>
<b-form-input
v-model="mappingData.mapping_name"
:state="errors.length > 0 ? false : null"
id="mappingName"
placeholder="Enter a maping name"
/>
<small class="text-danger">{{ errors[0] }}</small>
</validation-provider>
</b-form-group>

custom navigation for q-carousel not working

I am struggling solving my navigation q-carousel that I want, it is does not work.
It just shows default navigation only but not the custom one that I grab the code from the Quasar website.
My template:
<q-card-section class="q-pa-none" >
<div style="width:60%; padding-bottom: 250px"
class="bg-transparent text-center q-gutter-y-lg absolute-center ">
<q-carousel
animated
v-model="slide"
arrows
navigation
infinite
control-type="flat"
control-color="secondary"
class="bg-transparent text-center">
>
<template v-slot:navigation-icon="{ active, btnProps, onClick }">
<q-btn v-if="active" size="lg" icon="home" color="yellow" flat round dense #click="onClick" />
<q-btn v-else size="sm" :icon="btnProps.icon" color="white" flat round dense #click="onClick" />
</template>
<q-carousel-slide :name="1" >1</q-carousel-slide>
<q-carousel-slide :name="2">2</q-carousel-slide>
<q-carousel-slide :name="3">3</q-carousel-slide>
</q-carousel>
</q-card-section>
My script:
export default {
data() {
return {
slide : 1,
}
}
}
Your code is actually working, but you kind of mixed opening and closing tags.
There is no open tag for </q-card-section>
There is no closing tag for your leading <div>
The below code should work for you:
<div
style="width: 60%; padding-bottom: 250px"
class="bg-transparent text-center q-gutter-y-lg absolute-center"
>
<q-carousel
animated
v-model="slide"
arrows
navigation
infinite
control-type="flat"
control-color="secondary"
class="bg-transparent text-center"
>
<template v-slot:navigation-icon="{ active, btnProps, onClick }">
<q-btn
v-if="active"
size="lg"
icon="home"
color="yellow"
flat
round
dense
#click="onClick"
/>
<q-btn
v-else
size="sm"
:icon="btnProps.icon"
color="white"
flat
round
dense
#click="onClick"
/>
</template>
<q-carousel-slide :name="1">1</q-carousel-slide>
<q-carousel-slide :name="2">2</q-carousel-slide>
<q-carousel-slide :name="3">3</q-carousel-slide>
</q-carousel>
</div>
You should consider setting up auto formatting in your code editor/IDE to auto format your source code, if you are using VS Code you can do this quite easily: https://stackoverflow.com/a/29973358/13765033
This way, you shouldn't run into such trouble again (it also helps Stack Overflow users to read your source code).

How to change Layout of Quasar App and put the left Drawer to the right?

I am lost with the layout of Quasar and don't understand it, although I've read the documentation of it.
The scaffolder gave me the following code. But this makes the Drawer on the left side. How can I put it to the right side?
<template>
<q-layout view="lHh Lpr lFf">
<q-header elevated>
<q-toolbar>
<q-btn
flat
dense
round
icon="menu"
aria-label="Menu"
#click="leftDrawerOpen = !leftDrawerOpen"
/>
<q-toolbar-title>
Quasar App
</q-toolbar-title>
<div>Quasar v{{ $q.version }}</div>
</q-toolbar>
</q-header>
<q-drawer
v-model="leftDrawerOpen"
show-if-above
bordered
content-class="bg-grey-1"
>
<q-list>
<q-item-label
header
class="text-grey-8"
>
Essential Links
</q-item-label>
<EssentialLink
v-for="link in essentialLinks"
:key="link.title"
v-bind="link"
/>
</q-list>
</q-drawer>
<q-page-container>
<router-view />
</q-page-container>
</q-layout>
</template>
I thought it has something to do with the letters in
However. Playing with them won't change anything.
I also tried to put the q-drawer-tag below the q-page-container, with no luck either.
How to make the drawer visible on the right side?
you can set the behavior to side="right"
https://quasar.dev/layout/drawer#QDrawer-API
<q-drawer
v-model="leftDrawerOpen"
side="right"
show-if-above
bordered
content-class="bg-grey-1"
>
<q-list>
<q-item-label header class="text-grey-8">
Essential Links
</q-item-label>
<EssentialLink
v-for="link in essentialLinks"
:key="link.title"
v-bind="link"
/>
</q-list>
</q-drawer>

ion-input component (required, size) properties not working

Currently I am working on PWA project. In that I am using ionic-vue package. I am using ion-input for taking user input in form but required and size properties not working.
Code for vue component:
<template>
<div>
<ion-header>
<ion-toolbar>
<ion-icon name="close" class="close-icon" #click.prevent="redirectTo(close)"></ion-icon>
<ion-title>{{ title }}</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="occupie-field" padding>
{{ content }}
<div>
<ion-input class="modal-ion-input"
:placeholder="rootElement.$t('number-of-person')"
type="number" mode="ios" inputmode="decimal"
:value="total_person"
#input="total_person = $event.target.value"
></ion-input>
<ion-input class="modal-ion-input"
:placeholder="rootElement.$t('name')"
:value="booking_name"
#input="booking_name = $event.target.value"
></ion-input>
<ion-input class="modal-ion-input"
:placeholder="rootElement.$t('mobile-no')"
type="tel" inputmode="tel"
:value="contact_number"
#input="contact_number = $event.target.value"
></ion-input>
<ion-input class="modal-ion-input"
:placeholder="rootElement.$t('gstin-no')"
:value="gst_no" size="15" type="text"
#input="gst_no = $event.target.value"
></ion-input>
<!-- <tags-input
element-id="tags"
:existing-tags="available_table_list"
:typeahead="true"
:showAddedTags="true"
typeahead-style="dropdown"
:delete-on-backspace="false"
:only-existing-tags="true"
placeholder="Merge Table"
#tag-added="onTagAdded"
#tag-removed="onTagRemoved"
v-model="selectedTags"
></tags-input> -->
<!-- <ion-list> -->
<ion-item class="select-tablee">
<ion-label hidden>{{rootElement.$t('select-tables')}}</ion-label>
<ion-select :placeholder="rootElement.$t('merge-table')" multiple="true" value="merge_tables" #ionChange="selectChange($event)" :disabled="Object.keys(available_table_list).length === 0">
<ion-select-option v-for="(table, index) in available_table_list" :key="index" :value="index">{{ table }}</ion-select-option>
</ion-select>
</ion-item>
<!-- </ion-list> -->
</div>
<ion-button class="occupie-table-button" #click.prevent="submit" :disabled="disableBtn">{{rootElement.$t('occupie-table')}}</ion-button>
</ion-content>
When I add required property in ion-input then required property not working and form submitted without checking.
see this - https://gist.github.com/mlynch/2ff3692341276ba959fea96a620097f9
try
<IonInputVue v-model="contact_number" size="10"/>