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()"
>
Related
I have a navbar component that contains 2 buttons "Logout" and "Dashboard" that are rendered only if the user is authenticated. (using v-if).
However when the user click on the Logout button, the navbar is not reloaded and so the buttons stay visible until you reload the page. How do I set my v-if condition so that when the value of the v-if change, also the components (divs) update?
My Navbar
<!-- ... -->
<template slot="end">
<b-navbar-item tag="div">
<div class="buttons">
<a v-if="userToken" class="button is-primary" style='padding:4px; margin-right:0;'>
<b-button size="is-normal"
type="is-primary"
icon-right="close"
outlined
inverted
style="margin-bottom: 0;"
#click="logout">
Logout
</b-button>
</a>
<a v-if="userToken" class="button is-primary" style='padding:4px; margin-left:0;'>
<b-button size="is-normal"
type="is-success"
icon-right="account-circle-outline"
style="margin-bottom: 0;"
#click="goToDashboard">
Dashboard
</b-button>
</a>
<a v-else class="button is-primary">
<b-icon icon="account-circle-outline" size="is-medium"> </b-icon>
</a>
</div>
</b-navbar-item>
</template>
</b-navbar>
</template>
<script>
export default {
data () {
return {
userToken : '',
},
methods: {
getUserToken() {
let token = this.$cookies.get('userToken')
if (token) {
this.userToken = token
this.$router.push('user_dashboard')
}
},
logout() {
this.$cookies.remove('userToken')
this.userToken = ''
}
},
mounted() {
this.getUserToken()
},
// ...
</script>
Ok I'm really sorry, I eventually solved the issue by making those changes:
methods: {
getUserToken() {
let token = this.$cookies.get('userToken')
if (token) {
this.userToken = token
this.$router.push('user_dashboard')
} else {
this.userToken = ''
}
},
logout() {
this.$cookies.remove('userToken')
this.getUserToken()
}
},
mounted() {
this.getUserToken()
},
I have an vue component which uses Vue Draggable .
<template>
<div class="row my-5">
<div v-for="column in columns" :key="column.title" class="col">
<p class="font-weight-bold text-uppercase">{{column.title}}</p>
<!-- Draggable component comes from vuedraggable. It provides drag & drop functionality -->
<draggable :list="column.tasks" :animation="200" ghost-class="ghost-card" group="tasks" :move="checkMove">
<transition-group>
<task-card
v-for="(task) in column.tasks"
:key="task.id"
:task="task"
class="mt-3 cursor-move"
></task-card>
<!-- </transition-group> -->
</transition-group>
</draggable>
</div>
</div>
</template>
<script>
import draggable from "vuedraggable";
import TaskCard from "../board/TaskCard";
export default {
name: "App",
components: {
TaskCard,
draggable,
},
data() {
return {
columns: [
.....
],
};
},
methods: {
checkMove: function(evt){
console.log('moved');
}
},
};
</script>
In TaskCard Component -
<template>
<div class="bg-white shadow rounded p-3 border border-white">
<div class="d-flex justify-content-between align-items-center mb-3">
<h2>{{task.id}}</h2>
<span>{{task.date}}</span>
</div>
<p class="font-weight-bold">{{task.title}}</p>
</div>
</template>
<script>
export default {
props: {
task: {
type: Object,
default: () => ({}),
},
},
};
</script>
When I move an item, I want a modal that confirms the change and only then move the item.
(ie. if I click on the cancel button inside the modal, the item should not be moved.)
How can this be achieved using the checkMove() function provided?
I don't think you can achieve this by using onMove event. The onEnd event it seems more suitable but unfortunately it doesn't have any cancel drop functionality.
So I think the only solution here is revert it back if the user decides to cancel.
You can listen on change event (See more in documentation)
<draggable
group="tasks"
v-model="column.tasks"
#change="handleChange($event, column.tasks)">
...
</draggable>
...
<button #click="revertChanges">Cancel</button>
<button #click="clearChanges">Yes</button>
And
...
handleChange(event, list) {
this.changes.push({ event, list })
this.modal = true
},
clearChanges() {
this.changes = []
this.modal = false
},
revertChanges() {
this.changes.forEach(({ event, list }) => {
if (event.added) {
let { newIndex } = event.added
list.splice(newIndex, 1)
}
if (event.removed) {
let { oldIndex, element } = event.removed
list.splice(oldIndex, 0, element)
}
if (event.moved) {
let { newIndex, oldIndex, element } = event.moved
list[newIndex] = list[oldIndex]
list[oldIndex] = element
}
})
this.changes = []
this.modal = false
}
...
JSFiddle example
How do I access the data entered in my input elements, which are passed through via a slot, to my child component that opens up a modal with the form elements inside of it?
I've been reading the vue docs about scoped slots but honestly, I just can't figure it out how to make it work in my example. None of the examples make use of an input element with a v-model that is being passed to the child component.
I have created a component "BaseFormModal" which contains the following code:
Note that the validation (vee-validate) occurs inside here, so this child component emits a "submit" event when the data is considered valid, which I then pick up in my parent component.
<template v-slot:default="slotProps">
<b-modal ref="base-form-modal" :title="title" :no-close-on-backdrop="true" #ok.prevent="onSubmit">
<validation-observer ref="observer" v-slot="{handleSubmit}">
<b-form ref="form" #submit.stop.prevent="handleSubmit(onSubmit)">
<slot />
</b-form>
</validation-observer>
</b-modal>
</template>
<script>
import { ValidationObserver } from 'vee-validate'
export default {
name: 'BaseFormModal',
components: {
ValidationObserver,
},
props: {
title: {
type: String,
required: true,
},
},
data () {
return {
formData: {},
}
},
methods: {
async onSubmit () {
let valid = await this.$refs.observer.validate()
if (!valid) {
return
}
this.$emit('submit', this.formData)
this.$nextTick(() => {
this.$refs['base-form-modal'].hide()
})
this.formData = {}
},
showModal () {
this.$refs['base-form-modal'].show()
},
},
}
</script>
<style lang="scss" scoped>
</style>
In my page, I have a button which opens up the modal, like so:
<b-button variant="primary" #click="$refs['addOrgUserModal'].showModal()">
<i class="far fa-plus" aria-hidden="true" /> {{ $t('organisation_settings_manage_users_add_user') }}
</b-button>
Then I have defined the base form modal component in my page as this:
<base-form-modal
ref="addOrgUserModal"
:title="$tU('organisation_settings_manage_users_add_user_modal_title')"
#submit="addOrgUser"
>
<b-row>
<b-col md="6">
<form-control-wrapper :rules="{required: true}" :label="$tU('first_name_label')">
<b-form-input
v-model="user.firstName"
type="text"
lazy-formatter
:formatter="trimSpaces"
:placeholder="$t('first_name_field_placeholder')"
/>
</form-control-wrapper>
</b-col>
<b-col md="6">
<form-control-wrapper :rules="{required: true}" :label="$tU('family_name_label')">
<b-form-input
v-model="user.familyName"
type="text"
lazy-formatter
:formatter="trimSpaces"
:placeholder="$t('family_name_field_placeholder')"
/>
</form-control-wrapper>
</b-col>
</b-row>
</base-form-modal>
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 :)
I work with Laravel 5.5, Vue 2 and use Laravel-mix, i tried to render a list on input elements using Vue, but when i try this, the console return
[Vue warn]: Property or method "task" is not defined on the instance but referenced during render
This is my template, like a loop:
<template v-for="task in tasks">
<div class="input-group mb-1">
<div class="input-group-prepend">
<div class="input-group-text bg-white cursor-pointer checkbox">
<span class="mdi mdi-crop-square"></span>
</div>
</div>
<input type="text" :id="task.id_task" class="form-control bg-white cursor-pointer task-clk" :value="task.name" #click="showTask"
readonly>
</div>
</template>
So, this is my Vue component
<script>
function Task({id_task, name, status}) {
this.id_task = id_task;
this.name = name;
this.status = status;
}
export default {
data() {
return {
tasks: []
}
},
props: ['idList, id_task, name, status'],
created() {
this.getTasks();
},
methods: {
getTasks() {
window.axios.get('/api/get-tasks-by-list/' + this.idList).then(({data}) => {
data.tasks.forEach(task => {
this.tasks.push(new Task(task));
});
});
},
showTask(ev) {
var id = ev.currentTarget.id;
console.log(id);
}
},
}
</script>
I really dont understand why this error.