Checkbox labels appear under the checkbox (even with no styles loaded) - vue-formulate

How to fix the default (no styles) checkbox labels that appear under the box (it should be to the right of the box like in the example)
I'm using tailwind and styling the form through the global string option. Everything worked great so far until I tried styling checkboxes. The issue being that the label is showing beneath the box, and even if I remove all styles (as show in the screenshot) this is still and issue. I don't know how to target this label with the global string option, this is the structure I've been working on, trying to style label>box and input>box to get the label beside the checkbox.
Vue.use(VueFormulate, {
classes: {
outer: 'py-4',
wrapper(context) {
switch (context.classification) {
case "button":
return "";
default:
return "bg-regal-blue-400 py-3 w-full hover:bg-regal-blue-500";
}
},
label(context){
switch (context.classification) {
case "box":
return "bg-red-500";
default:
return "w-full block text-gray-400 px-4 text-lg";
}
},
element: 'w-full',
input(context) {
switch (context.classification) {
case "button":
return "text-lg border-2 rounded-full h-12 bg-red-900 border-red-500 font-bold px-10 hover:bg-red-500 transition-colors duration-500 focus:outline-none";
case "textarea":
return "bg-transparent w-full mt-2 h-48 focus:outline-none text-xl placeholder-gray-700 px-4";
case "box":
return "h-6 w-6";
default:
return "bg-transparent w-full h-10 focus:outline-none text-xl placeholder-gray-700 px-4";
}
},
button: 'bg-red-500',
help: '',
errors: 'px-4',
error: 'text-red-500'
}
})
Even in some of the provided codebox examples, like "Tailwind via Config", if you add a checkbox to the existing form the labels appear under the checkboxes https://codesandbox.io/s/tailwind-vue-formulate-2-password-reset-all-global-kydyp

I think all that is missing is a display: flex on the wrapper when it's a box. Here's the code you provided for your configuration modified with a flex:
Vue.use(VueFormulate, {
classes: {
outer: "py-4",
wrapper(context) {
switch (context.classification) {
case "button":
return "";
case "box":
return "w-full flex justify-start";
default:
return "bg-regal-blue-400 py-3 w-full hover:bg-regal-blue-500";
}
},
label(context) {
switch (context.classification) {
case "box":
return "bg-red-500 flex-grow";
default:
return "w-full block text-gray-400 px-4 text-lg";
}
},
element ({ classification }) {
switch (classification) {
case 'box':
return 'px-1';
default:
return 'w-full';
}
},
input(context) {
switch (context.classification) {
case "button":
return "text-lg border-2 rounded-full h-12 bg-red-900 border-red-500 font-bold px-10 hover:bg-red-500 transition-colors duration-500 focus:outline-none";
case "textarea":
return "bg-transparent w-full mt-2 h-48 focus:outline-none text-xl placeholder-gray-700 px-4";
case "box":
return "h-6 w-6";
default:
return "bg-transparent w-full h-10 focus:outline-none text-xl placeholder-gray-700 px-4";
}
},
button: "bg-red-500",
help: "",
errors: "px-4",
error: "text-red-500"
}
});
And here's a link to an example: https://codesandbox.io/s/tailwind-vue-formulate-checkbox-after-pwxh2?file=/src/main.js

Related

Keep getting error "is not defined on the instance but referenced during render"

I keep getting the following error after binding my data components to the template. Any help would be appreciated.
script:
data() {
return {
sireDetailsData: [],
horse_show_name_prop:,
}
},
async created () {
const psireName = await this.sireName;
console.log(psireName)
try {
const response = await sireDetails.fetchHorseIndex(psireName);
this.sireDetailsData = response.data;
try {
this.horse_show_name_prop = this.sireDetailsData[0].horse_details[0].horse_show_name
} catch (error) {
this.horse_show_name_prop = undefined
}
} catch (error) {
console.error(error);
}
},
template:
<a-col
:span="6"
:md="6"
align="middle"
style="margin-right: 0px;"
:horse_name="horse_show_name_prop"
>
<p
class="m-0 text-sm font-bold center"
style="min-width: 0px;"
v-if="horse_name !== undefined"
>
{{ horse_name }}%
</p>
<p
class="m-0 text-sm font-bold center"
style="min-width: 0px;"
v-else
>
-
</p>
<a-tag
class="ant-tag-primary font-semibold text-xs"
style="min-width: 30px;margin-right: 0px;"
>
Win %
</a-tag>
</a-col>
After making some live changes the data then loads in. It makes me think things are rendering out of order..
You need to declare horse_name in data:
data() {
return {
sireDetailsData: [],
horse_show_name_prop:,
horse_name: '', // Add horse_name here
}
},

Vue bind multiple class with conditional

I have the following div in my component:
<div :class="sidebarOpen ? 'translate-x-0 ease-out transition-medium' : '-translate-x-full ease-in transition-medium'" class="sidebar fixed z-30 inset-y-0 left-0 w-64 px-8 py-4 border-r overflow-auto lg:static lg:inset-auto lg:translate-x-0">
I want to add another class to this, which is a computed property:
computed: {
borderColor() {
return {
"bg-white": this.$apollo.loading,
[colorBorder[this.board?.color]]: true
}
},
}
Can anyone advise how I can do this please?
Try to use the array syntax :
<div :class="[sidebarOpen ?'...':'...',borderColor]"

How can I validate a custom select component made of a button using VeeValidate?

I'm trying to build a custom select input, the functionality works as expected but not with the validation. I can't get it work by following the example since a <button> can't have a v-model. I don't think I can use a computed get-set approach like shown in the docs because I only use computed prop to get the text of the selected value to be shown in the UI, not to be sent to parent, nor to set a value.
Setting the value are done using #click="$emit(...)" on <li> element.
To be clear, here I provide both the script and the implementation, please have a look
BaseSelect.vue
<template>
<ValidationProvider
tag="div"
:vid="name"
:mode="validationMode"
:rules="validationRules"
v-slot="{ errors }"
>
<label :id="name" class="block font-medium mx-2 mb-3">{{ label }}</label>
<div class="relative">
<button
type="button"
aria-haspopup="listbox"
:aria-expanded="open"
:aria-labelledby="name"
class="relative w-full bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-700 rounded-md shadow-sm pl-3 pr-10 py-2 text-left cursor-default focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
#click.prevent="open = !open"
>
<span v-if="value">{{ selected }}</span>
<span v-else>{{ placeholder }}</span>
<!-- selectable indicator icon (svg) -->
...
</button>
<!-- Error message -->
<small v-if="errors.length > 0" class="text-red-500 mt-3 mx-2">{{
errors[0]
}}</small>
<transition
leave-active-class="transition ease-in duration-100"
leave-class="opacity-100"
leave-to-class="opacity-0"
>
<div
class="absolute mt-1 w-full rounded-md bg-white dark:bg-gray-800 shadow-lg"
v-show="open"
>
<ul
tabindex="-1"
role="listbox"
:aria-labelledby="name"
aria-activedescendant="listbox-item-3"
class="max-h-56 rounded-md py-1 text-base ring-1 ring-black ring-opacity-5 overflow-auto focus:outline-none sm:text-sm"
>
<li
v-for="item in items"
:key="item.id"
:id="`listbox-item-${item.id}`"
role="option"
class="text-gray-900 dark:text-white hover:bg-gray-200 dark:hover:bg-gray-700 cursor-default select-none relative py-2 pl-3 pr-9"
#click="$emit('input', item[preferedValue])"
>
<span
class="ml-3 block font-normal truncate"
v-text="item.name"
/>
<!-- Checkmark, only display for selected option. (svg) -->
...
</li>
</ul>
</div>
</transition>
</div>
</ValidationProvider>
</template>
<script>
import { ValidationProvider } from 'vee-validate'
export default {
inheritAttrs: false,
components: { ValidationProvider },
props: {
name: {
type: String,
required: true,
},
label: {
type: String,
required: true,
},
hideLabel: {
type: Boolean,
default: false,
},
placeholder: {
type: String,
default: 'Choose one',
},
value: {
type: [String, Number, Object],
default: null,
},
required: {
type: Boolean,
default: true,
},
rules: {
type: String,
required: false,
},
validationMode: {
type: String,
default: 'eager',
},
items: {
type: Array,
required: true,
default: () => [],
},
preferedValue: {
type: String,
default: 'id',
},
},
data: () => ({
open: false,
}),
computed: {
/**
* The text to be shown based on the selected item and
* the prefered value as it's key.
*
* #returns string
*/
selected() {
if (this.value === null || !this.items.length) return ''
let index = _.findIndex(
this.items,
(item) => item[this.preferedValue] === this.value
)
return this.items[index].name
},
/**
* The validation rules to be applied in this input field.
*
* #returns string
*/
validationRules() {
if (!this.required) return this.rules
return this.rules ? `required|${this.rules}` : 'required'
},
},
mounted() {
document.addEventListener('click', this.close)
},
destroyed() {
document.removeEventListener('click', this.close)
},
methods: {
toggle() {
this.open = !this.open
},
close(e) {
if (!this.$el.contains(e.target)) {
this.open = false
}
},
},
}
</script>
implementation.vue
<template>
<BaseForm
ref="form"
reference="subcategory_form"
:errors="errors"
action="/subcategories"
method="POST"
#submit="store"
>
...
<BaseSelect
class="mb-4"
name="category_id"
label="Category"
placeholder="Choose one"
:items="categories.data"
v-model="subcategory.category_id"
/>
</BaseForm>
</template>
<script>
layout: 'dashboard',
async fetch() {
await this.$store.dispatch('categories/load')
},
data: () => ({
subcategory: {
category_id: null,
name: '',
},
errors: {},
}),
computed: {
categories() {
return this.$store.state.categories.pagination
},
},
methods: {
async store() {
...
},
},
}
</script>
BaseForm.vue
<template>
<ValidationObserver :ref="reference" tag="div" v-slot="{ handleSubmit }">
<form
:action="action"
:method="method"
#submit.prevent="handleSubmit(onSubmit)"
>
<slot></slot>
</form>
</ValidationObserver>
</template>
<script>
import { ValidationObserver } from 'vee-validate'
export default {
components: { ValidationObserver },
props: {
action: {
type: String,
required: true,
},
method: {
type: String,
required: true,
},
reference: {
type: String,
required: true,
},
errors: {
type: Object,
default: () => {},
},
},
watch: {
/**
* Watch for `errors`.
*
* Everytime it changes, assuming it comes from the backend,
* assign the errors to the `ValidationObserver`.
*/
errors(val) {
this.$refs[this.reference].setErrors(val)
},
},
methods: {
/**
* Emit `submit` event to the parent component.
*
* #returns void
*/
onSubmit() {
this.$emit('submit')
},
},
}
</script>
So, is this possible to validate with vee?
You can use the validate function on the slot props of the ValidationProvider, there is a guide for this here.
https://vee-validate.logaretm.com/v3/advanced/model-less-validation.html#html-file-validation
The validate function accepts the input value to be set as the current value internally which will be validated.
<ValidationProvider v-slot="{ validate }">
<button #click="validate(someValue)"></button>
</ValidationProvider>

Vuex error and action following the click

I am having a problem on a project in Vue JS and VueX. We have a Modal component which must open or close upon the click of a button. We have therefore well informed in Vue X the correct modal module:
namespaced: true,
state : {
show: false
},
// Getter functions
getters : {
showModal( state ) {
return state.show
}
},
actions : {
showModal({commit}){
commit('SHOWMODAL');
}
},
// Mutations
mutations : {
SHOWMODAL(state) {
state.show = !state.show
}
}
}
export default ModalModule;// export the module
On the component that triggers the action, we have imported the getters and the actions
<script>
import { mapGetters, mapActions } from 'vuex';
export default {
data() {
return {
};
},
computed: {
...mapGetters('ModalModule', [
'showModal',
])
},
components: {
},
methods: {
...mapActions('ModalModule', [
'showModal',
]),
}
};
</script>
And in the template :
<button
class="bg-green-500 text-white active:bg-green-600 font-bold uppercase text-xs px-4 py-2 rounded shadow hover:shadow-md outline-none focus:outline-none mr-1 ease-linear transition-all duration-150"
type="button"
#click="showModal()"
>
FERMER
</button>
But when i click on this button it doesn't work and when i click on the button which open the modal i've got :
<button
class="bg-green-500 active:bg-green-600 uppercase text-white font-bold hover:shadow-md shadow text-xs px-4 py-2 rounded outline-none focus:outline-none sm:mr-2 mb-1 ease-linear transition-all duration-150"
type="button"
#click="showModal()"
>
I've this error message :
Computed property "showModal" was assigned to but it has no setter.
And when i click on Fermer button i've this :
Error in v-on handler: "TypeError: _vm.showModal is not a function"
i don't understand why thank you very much if you've got a clue.
You should give an alias while mapping your action into methods so you can prevent name collision:
methods: {
...mapActions('ModalModule', {
'toggleShowModal' : 'showModal',
}),
And then in your template use the alias :
<button class="bg-green-500 active:bg-green-600 uppercase text-white font-bold hover:shadow-md shadow text-xs px-4 py-2 rounded outline-none focus:outline-none sm:mr-2 mb-1 ease-linear transition-all duration-150"
type="button"
#click="toggleShowModal()"
>

Vuejs compute property doesn't get updated properly

I'm very new to Vuejs, I'm following their documentation which is very helpful. However, I find it difficult to understand how compute properties actually are triggered.
I'm using ag-grid for my project and I would like to update the total number of rows to my custom page size drop-down list.
The following is my code:
<template>
<div id="ag-grid-demo">
<vx-card>
<!-- TABLE ACTION ROW -->
<div class="flex flex-wrap justify-between items-center">
<!-- ITEMS PER PAGE -->
<div class="mb-4 md:mb-0 mr-4 ag-grid-table-actions-left"></div>
<!-- TABLE ACTION COL-2: SEARCH & EXPORT AS CSV -->
<div class="flex flex-wrap items-center justify-between ag-grid-table-actions-right">
<vs-button class="mb-4 md:mb-0" #click="gridApi.exportDataAsCsv()">Export as CSV</vs-button>
</div>
</div>
<ag-grid-vue
ref="agGridTable"
:gridOptions="gridOptions"
class="ag-theme-material w-100 my-4 ag-grid-table"
:columnDefs="columnDefs"
:defaultColDef="defaultColDef"
:rowModelType="rowModelType"
#grid-ready="onGridReady"
rowSelection="multiple"
colResizeDefault="shift"
:animateRows="true"
:pagination="true"
:paginationPageSize="paginationPageSize"
:cacheBlockSize="cacheBlockSize"
:enableRtl="$vs.rtl"
:modules="modules"
></ag-grid-vue>
<div class="flex flex-wrap justify-between items-center">
<!-- CUSTOM PAGESIZE DROP-DWON -->
<div class="mb-4 md:mb-0 mr-4 ag-grid-table-actions-left">
<vs-dropdown vs-trigger-click class="cursor-pointer">
<div class="p-4 border border-solid d-theme-border-grey-light rounded-full d-theme-dark-bg cursor-pointer flex items-center justify-between font-medium">
<span class="mr-2"
>{{ currentPage * paginationPageSize - (paginationPageSize - 1) }} - {{ recordCount - currentPage * paginationPageSize > 0 ? currentPage * paginationPageSize : recordCount }} of {{ recordCount }}</span>
<feather-icon icon="ChevronDownIcon" svgClasses="h-4 w-4" />
</div>
<vs-dropdown-menu>
<vs-dropdown-item #click="gridApi.paginationSetPageSize(10)">
<span>10</span>
</vs-dropdown-item>
<vs-dropdown-item #click="gridApi.paginationSetPageSize(50)">
<span>50</span>
</vs-dropdown-item>
<vs-dropdown-item #click="gridApi.paginationSetPageSize(100)">
<span>100</span>
</vs-dropdown-item>
<vs-dropdown-item #click="gridApi.paginationSetPageSize(150)">
<span>150</span>
</vs-dropdown-item>
</vs-dropdown-menu>
</vs-dropdown>
</div>
<!-- CUSTOM TABLE PAGINATION -->
<div class="flex flex-wrap items-center justify-between ag-grid-table-actions-right">
<vs-pagination :total="totalPages" :max="maxPageNumbers" v-model="currentPage" />
</div>
</div>
</vx-card>
</div>
</template>
<script>
import { AgGridVue } from "ag-grid-vue";
import { ServerSideRowModelModule } from "#ag-grid-enterprise/server-side-row-model";
import { MenuModule } from "#ag-grid-enterprise/menu";
import { ColumnsToolPanelModule } from "#ag-grid-enterprise/column-tool-panel";
import CompanyServices from "../../../_services/company.service";
import "#/assets/scss/vuexy/extraComponents/agGridStyleOverride.scss";
export default {
components: {
AgGridVue
},
data() {
return {
gridOptions: {},
maxPageNumbers: 7,
gridApi: null,
defaultColDef: {
sortable: true,
editable: false,
resizable: true,
suppressMenu: false
},
columnDefs: [
{ headerName: "Id", field: "id", filter: false },
{
headerName: "Company Name",
field: "companyName",
filter: true,
checkboxSelection: true,
headerCheckboxSelectionFilteredOnly: true
}
],
rowModelType: "serverSide",
modules: [ServerSideRowModelModule, MenuModule, ColumnsToolPanelModule],
cacheBlockSize: 10,
};
},
computed: {
paginationPageSize() {
if (this.gridApi) return this.gridApi.paginationGetPageSize();
else return 10;
},
totalPages() {
if (this.gridApi) return this.gridApi.paginationGetTotalPages();
else return 0;
},
currentPage: {
get() {
if (this.gridApi) return this.gridApi.paginationGetCurrentPage() + 1;
else return 1;
},
set(val) {
this.gridApi.paginationGoToPage(val - 1);
}
},
recordCount: function() {
if (window.recordCount === undefined) return 0;
else return window.recordCount;
}
},
methods: {
onGridReady: function(params) {
var datasource = new ServerSideDatasource();
params.api.setServerSideDatasource(datasource);
}
},
mounted() {
this.gridApi = this.gridOptions.api;
this.gridColumnApi = this.gridOptions.columnApi;
}
};
window.ServerSideDatasource = function ServerSideDatasource(server) {
return {
getRows: function(params) {
CompanyServices.list({
startRow: params.request.startRow,
endRow: params.request.endRow,
SortColumn: "CompanyName",
SortOrder: "asc"
})
.then(response => {
window.recordCount = response.data.total;
params.successCallback(response.data.rows, response.data.total);
})
.catch(error => {
params.failCallback();
});
}
};
};
</script>
My issue is that computed property 'recordCount' does not get updated as 'window.recordCount' being changed. 'recordCount' always shows value as zero.
Could someone please shed a light here?
Thanks in advance!
There seems to be a couple of issues here, mainly that you are setting data and functions outside of the vue instance.
First of all, you should not be setting any data on window, Vue has plenty of ways to handle your data and makes it all reactive for you.
Start by moving your window.ServerSideDataSource function into the vue instance.
In Vue, functions are put under "methods", so after your onGridReady function, add a ,
and then put:
serverSideDatasource() {
return {
getRows: functions(params) {..}
}
}
Notice that I put serverSideDatasource with a small starting s, as camelCase is widely considered best practice when naming variables and functions.
Next, your window.recordCount should be put into Vue's data. Just add it after cacheBlockSize and set it like so:
recordCount: 0
Now that all your data and methods (functions) are within Vue, you can reach them by using "this".
So replace "window." with "this." all the places you use it.
Now, since your recordCount starts as 0, and is only changed when you call getRows, there is no need for a computed called recordCount, so delete that.
Notice: Having both a property in data and computed called recordCount would conflict, so even if you kept it, you would have to rename the computed.
Do tell me if this fixes it, or how far it gets you :)