Update lits after dialog close in Vuejs project - vue.js

I have a list that displays items on a page, on the same page you can open a dialog where you are able to add items to the list. When I close the dialog I want the list to re-render to also show the latest added item.
Parent:
<template>
<div v-for="item in listItems" :key="item.id"></div>
<app-card-grid-work
:items="listItems">
</app-card-grid-work>
<v-dialog v-model="dialog" max-width="500px">
<app-fab slot="activator"></app-fab>
<app-add-work #closeDialog="getWork()"></app-add-work>
</v-dialog>
<template>
export default {
data() {
return {
listItems: [],
},
methods: {
getWork(){
axios
.get("/workads")
.then(res => {
const data = res.data;
const ads = [];
for (let id in data) {
const ad = data[id];
ads.push(ad);
}
this.listItems = ads;
})
.catch(error => console.log(error));
},
},
},
};
Child (app-add-work)
<template>
<v-btn
#click="onSubmit"
:disabled="!valid">
Skapa
</v-btn>
<template>
<script>
export default {
//...
methods:{
onSubmit(){
this.$emit('closeDialog')
}
},
//...
};
</script>

Related

Updated value not change without page refresh in Nuxt

I just started to learn Vue & Nuxt. So i have a page where i fetch all the order details and update the order state. The order state which is displayed in the UI is not getting updated asynchronously. How can i achieve reactive here ?
I need to update the value here Current Status : <b>{{ order_details.status.state }}</b> asynchronously.
Template
<template>
<v-container>
<v-row>
<v-col cols="12">
Current Status : <b>{{ order_details.status.state }}</b>
</v-col>
</v-row>
</v-container>
</template>
<template>
<v-form>
<v-container>
<v-row>
<v-col cols="12">
<div class="d-flex align-center justify-end">
<v-btn
color="primary"
class="text-subtitle-2 font-weight-medium"
#click="updateOrder"
>Update</v-btn
>
</div>
</v-col>
</v-row>
</v-container>
</v-form>
</template>
Script
export default {
async fetch({ store, params }) {
await store.dispatch("merchant/fetchOrderDetails", {
id: params.id
});
await store.dispatch("fetchMerchants");
await store.dispatch("fetchAllStatus");
},
data() {
return {
sortTypes: ["Date", "Distance"],
selectedSort: "Distance",
statusId: "",
};
},
computed: {
...mapState({
merchants: "merchants",
statusList: "statusList"
}),
...mapState("merchant", {
order_details: "orderDetails"
}),
},
methods: {
async updateOrder() {
await this.$axios
.$patch(
`/admin-portal/orders/${this.$route.params.id}`,
{
statusId: this.statusId
}
)
},
}
};
Store
export const state = () => ({
orderDetails: {}
});
export const mutations = {
SET_ORDER_DETAILS(state, orderDetails) {
state.orderDetails = orderDetails;
}
};
export const actions = {
async fetchOrderDetails({ commit }, { id }) {
const orderDetails = await this.$axios.$get(
`/pharmaceutical/admin-portal/orders/${id}`
);
commit("SET_ORDER_DETAILS", orderDetails);
}
};
You did a good chunk by yourself already. You need a few minor things to add. This is an example of code that would help you patch your thing and update vuex.
<template>
...
<v-btn #click="updateOrderInsideVuex">
Update
</v-btn>
...
</template>
<script>
import { mapActions } from 'vuex'
export default {
methods: {
...mapActions('merchant', ['updateOrder']),
async updateOrderInsideVuex() {
this.updateOrder({ paramsId: this.$route.params.id, statusId: this.statusId })
}
}
}
</script>
In your vuex store (module).
const actions = {
async updateOrder({ commit }, { paramsId, statusId }) {
const responseFromBackend = await this.$axios.$patch(`/admin-portal/orders/${paramsId}`, { statusId })
// remember to populate the vuex store with your result from the backend
// or make another call to fetch the current state of the API
commit('SET_ORDER_DETAILS_AFTER_PATCH', responseFromBackend)
},
Of course, you also need to write SET_ORDER_DETAILS_AFTER_PATCH, a typical mutation but I guess that it kinda depends of your actual data too.

NuxtJS component - component renders without data

I have a component that uses fetch to bring data via Axios and return data to render. I am using the store to set/get the data. The component renders with empty data and after debugging I see that data() method is called before fetch() method.
How to fix the problem and bring the data before the component is rendered
here is the component code:
<template>
<v-card
class="mx-auto"
max-width="500"
>
<v-sheet class="pa-4 primary lighten-2">
<v-text-field
v-model="search"
label="Search Company Directory"
dark
flat
solo-inverted
hide-details
clearable
clear-icon="mdi-close-circle-outline"
></v-text-field>
<v-checkbox
v-model="caseSensitive"
dark
hide-details
label="Case sensitive search"
></v-checkbox>
</v-sheet>
<v-card-text>
<v-treeview
:items="items"
:search="search"
:filter="filter"
:open.sync="open"
>
<template v-slot:prepend="{ item }">
<v-icon
v-if="item.children"
v-text="mdi-${item.id === 1 ? 'home-variant' : 'folder-network'}"
></v-icon>
</template>
</v-treeview>
</v-card-text>
</v-card>
</template>
<script>
export default {
async fetch(){
console.log("Hi from Fetch !!!")
let response = await this.$axios.get('/items/tasks', {baseURL: 'http://localhost:8055'});
let tasks = response.data.data;
debugger
this.$store.commit('SET_ASSIGNMENTS', tasks);
},
data () {
debugger
console.log("data assignments: ", this.$store.state.assignments);
return {
items: this.$store.state.assignments,
open: [1, 2],
search: null,
caseSensitive: false,
}
},
computed: {
filter () {
return this.caseSensitive
? (item, search, textKey) => item[textKey].indexOf(search) > -1
: undefined
},
}
}
</script>
For this I use vuex this way:
const appStore = {
state () {
return {
data: [],
}
},
getters: {
data(state) {
return state.data
},
},
mutations: {
SET_ASSIGNMENTS(state, payload) {
state.data = payload
},
},
actions: {
async getData({ commit }, {fromDate, toDate}) {
let response = await this.$axios.get('/items/tasks', {baseURL: 'http://localhost:8055'});
let tasks = response.data.data;
commit("SET_ASSIGNMENTS", tasks);
}
}
}
export default appStore
Component code is like this:
<template>
. . .
</template>
<script>
import { mapGetters } from 'vuex';
export default {
name: "MyComponent",
components: {
. . .
},
computed: {
...mapGetters({
data: 'data'
})
},
mounted(){
this.getData();
},
methods: {
getData() {
this.$store.dispatch('getData')
}
}
}
</script>
data is not reactive, you can create a computed property that returns your items
ex:reactiveItems() {return this.items}

Vue - make axios request upon parent event

I'm basically trying to figure out how to listen for an event and call the axios request and rerender the results.
I have the following parent component (A select drop down) that has a change event that calls "appSelected"
<template>
<v-app>
<v-container>
<v-select :items="results" item-text="name" item-value="id" v-model="selectedOption" #change="appSelected"
:results="results"
label="Choose Application"
dense
></v-select>
</v-container>
</v-app>
</template>
appSelected
appSelected() {
Event.$emit('selected', this.selectedOption);
}
In my child component,I have an an Axios request that I'm trying to figure out how to call and re render when choosing a different option in the drop down. I know I shouldn't use "async mounted" but not sure what to use instead.
<template>
<div id="app" v-if="appselected">
<div class="card-header">
<h5 class="card-title">Components</h5>
</div>
<v-app id="inspire">
<v-container fluid>
<v-layout row wrap>
<v-container>
<v-flex xs12 md12 class="greyBorder blue-grey lighten-5">
<div class="mr-4 ml-4 whiteback userGroupHeight">
<v-layout row wrap>
<v-flex v-for="result in results" :key="result.name" xs6>
<v-checkbox light color="success" :label="result.name" v-model="result.selected">
</v-checkbox>
</v-flex>
</v-layout>
</div>
</v-flex>
</v-container>
</v-layout>
</v-container>
</v-app>
</div>
</template>
<script>
import Vuetify from 'vuetify'
import axios from 'axios'
export default {
data () {
return {
results: [],
appselected: false
}
},
methods: {
checkLength(index) {
if (index < this.types.length - 1) {
index = index + 1;
return index;
}
},
},
async mounted() {
try {
const response = await axios.get('/componentpopulate', { params: { query: this.query, appid: 2 } })
this.results = response.data
} catch(err) {
console.log(err)
}
},
created() {
Event.$on('selected', (selectedOption) => {
this.selected = selectedOption;
this.appselected = true;
console.log('component List application id - ' + this.selected);
});
}
}
</script>
<style scoped>
.v-application {
height: 250px;
}
</style>
This is my created method is where I'm listening for the event
created() {
Event.$on('selected', (selectedOption) => {
this.selected = selectedOption;
this.appselected = true;
console.log('component List application id - ' + this.selected);
});
}
Here is where I moved to the event listener method
created() {
Event.$on('selected', (selectedOption) => {
this.selected = selectedOption;
this.appselected = true;
console.log('component List application id - ' + this.selected);
const response = axios.get('/componentpopulate', { params: { query: this.query, appid: this.selected } })
this.results = response.data
console.log(response);
});
}
Here's the answer. Add the following
.then((response) => {
this.results = response.data
console.log(response)
});
Like this
created() {
Event.$on('selected', (selectedOption) => {
this.selected = selectedOption;
this.appselected = true;
console.log('component List application id - ' + this.selected);
const response = axios.get('/componentpopulate', { params: { query: this.query, appid: this.selected } })
.then((response) => {
this.results = response.data
console.log(response)
});
});
}
Hope this helps some one.
You can try this. Use watch for your selectedOption and make your API calls in the parent component. However required parameters for API call should be in parent component with this way. Or in your store. After you can pass your results to your child component as props and display it there. Here is a small example:
Your Parent Component:
<template>
<div>
<!-- your select box -->
<Child :results="results" />
</div>
</template>
export default {
import Child from "./Child"
components: {
Child
},
data() {
return {
selectedOption : null,
results: []
}
},
watch: {
selectedOption: async function() {
await axios.get('http://jsonplaceholder.typicode.com/posts')
.then(response => {this.results = response.data})
}
}
}
And in child component:
<template>
<div>
<div v-for="item in results" :key="item.id">
{{item}}
</div>
</div>
</template>
<script>
export default {
props: ['results']
}
</script>

Close Vuetify dialog with Vuex

After hours of searching and trying to find the correct method my n00b brain exploded.
I've tried so many things that I'm complete lost. Everything works like I want it to, can remove the customer I want, the front refreshes etc. Except the dialog.
Can you please explain how to close this dialog?
Here is my dialog.
<template>
<div>
<template>
<tbody>
<tr v-for="customer in AllCustomers" :key="customer.id" class="todo">
<td>{{customer.ID}}</td>
<td>{{ customer.name }}</td>
<td>{{customer.telephone}}</td>
<td>{{customer.email}}</td>
<v-btn color="success" #click="showDeleteDialog(customer)">DELETE</v-btn>
</tr>
</tbody>
</template>
<v-dialog v-model="dialogDelete" persistent max-width="500px">
<v-card>
<v-card-title>Delete</v-card-title>
<v-card-text>Weet je zeker dat je {{customerToDelete}} wenst te verwijderen?</v-card-text>
<v-card-actions>
<v-btn color="primary" text #click="close">Annuleer</v-btn>
<v-btn color="primary" text #click="deleteCustomer(customer.ID)">Verwijderen</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</div>
</template>
<script>
import { mapGetters, mapActions, mapState } from "vuex";
export default {
name: "AllCustomers",
data() {
return {
customerToDelete: "",
dialogDelete: false
};
},
methods: {
...mapActions(["fetchAllCustomers", "deleteCustomer"]),
async close() {
this.dialogDelete = false;
},
async showDeleteDialog(customer) {
this.customer = Object.assign({}, customer);
this.customerToDelete = this.customer.name;
this.dialogDelete = !this.dialogDelete;
this.$store.commit("toggleDialog");
}
},
computed: mapGetters(["AllCustomers"]),
created() {
this.fetchAllCustomers();
},
...mapState(["dialogDelete"])
};
</script>
And here my module js.
import axios from 'axios';
const state = {
customers: [],
dialogDelete: false
};
const getters = {
AllCustomers: state => state.customers
};
const actions = {
async fetchAllCustomers({ commit }) {
const response = await axios.get(
'http://localhost:8888'
);
console.log(response.data.data);
commit('setAllCustomers', response.data.data);
},
async deleteCustomer({ commit }, id) {
await axios.delete(`http://localhost:8888/delete`, {
data: {
id: id
}
})
console.log(id)
commit('removeCustomer', id, this.dialogDelete = false);
},
}
const mutations = {
setAllCustomers: (state, customers) => (state.customers = customers),
removeCustomer: (state, id) =>
(state.customers = state.customers.filter(customer => customer.ID !== id)),
}
export default {
state,
getters,
actions,
mutations
};
You should use mapState to get your dialogDelete variable from store:
// in your dialog
import { mapState } from "vuex"
computed: {
...mapState(["dialogDelete"])
}
and you should change its state in mutations with a commit:
// in vuex store
const mutations = {
setAllCustomers: (state, customers) => (state.customers = customers),
removeCustomer: (state, id) =>
(state.customers = state.customers.filter(customer => customer.ID !==
id)),
toggleDialog: (state) => (state.dialogDelete = !state.dialogDelete)
}
// in your dialog
this.$store.commit("toggleDialog")
Since you didn't include the <script> tag in your code, I'm assuming that you're trying to toggle the vuex state directly by your close event from the vue component, which wouldn't work this way.
Instead, you would want to dispatch an action that commits a mutation that toggles the vuex state.
However, a better idea is to encapsulate the dialog component in a separate vue SFC (single file component) that has a local stateisActive, which you can toggle on or off via a local method this.isActive = false, and once you import that component you then give it a ref ref="deleteDialog", you'll then be able to access the component's internal methods like this: this.$refs.deleteDialog.close()
For more info about refs, see the docs.
EDIT
So for example:
DialogDelete.vue
<template>
<v-dialog v-model="isActive" persistent max-width="500px">
<v-card>
<v-card-title>Delete</v-card-title>
<v-card-text>Weet je zeker dat je {{customerToDelete}} wenst te verwijderen?</v-card-text>
<v-card-actions>
<v-btn color="primary" text #click="close">Annuleer</v-btn>
<v-btn color="primary" text #click="deleteCustomer(customer.ID)">Verwijderen</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script>
export default {
data() {
return {
isActive: false
}
},
methods: {
close() {
this.isActive = false
},
open() {
this.isActive = true
}
},
}
</script>
Parent.vue
<template>
<div>
<v-btn #click="openDialog">Open Dialog</v-btn>
<dialog-delete ref="deleteDialog" />
</div>
</template>
<script>
export default {
components: {
dialogDelete: () => import("./DialogDelete.vue"),
},
methods: {
openDialog() {
this.$refs.deleteDialog.open()
}
},
}
</script>
It's also clear to me that you're not placing the vuex helper methods (like mapGetters & mapState) where they should be, for example both of mapState & mapGetters should be within the computed:
computed: {
...mapGetters(["getterName"]),
...mapState(["stateName"])
}
check out the vuex docs.

VueJS - How to pass function to global component

I have a confirm dialog, which should be shown when users perform delete action. I need to make it works globally (Many pages can use this component by passing confirm message and delete function to it). However, I haven't found a way to pass a function to this component.
Thanks in advance!
ConfirmDialog component:
<template>
<v-dialog
v-model="show"
persistent
max-width="350"
>
<v-card>
<v-card-text class="text-xs-center headline lighten-2" primary-title>
{{ message }}
</v-card-text>
<v-card-actions class="justify-center">
<v-btn color="back" dark #click="close">キャンセル</v-btn>
<v-btn color="primary" dark>削除</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script>
export default {
data () {
return {
show: false,
message: ''
}
},
created: function () {
this.$store.watch(state => state.confirmDialog.show, () => {
const msg = this.$store.state.confirmDialog.message
if (msg !== '') {
this.show = true
this.message = this.$store.state.confirmDialog.message
} else {
this.show = false
this.message = ''
}
})
},
methods: {
close () {
this.$store.commit('closeDialog')
}
}
}
</script>
ConfirmDialog store:
export default {
state: {
show: false,
message: '',
submitFunction: {}
},
getters: {
},
mutations: {
showDialog (state, { message, submitFunction }) {
state.show = true
state.message = message
state.submitFunction = submitFunction
},
closeDialog (state) {
state.show = false
state.message = ''
}
}
}
you can get and set states easily.
try getting the value of show with ...mapState
ConfirmDialog.vue :
<template>
<v-dialog
v-if="show"
persistent
max-width="350"
>
<v-card>
<v-card-text class="text-xs-center headline lighten-2" primary-title>
{{ message }}
</v-card-text>
<v-card-actions class="justify-center">
<v-btn color="back" dark #click="close">キャンセル</v-btn>
<v-btn color="primary" dark>削除</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
</template>
<script>
import { mapState } from 'vuex';
export default {
data () {
return {
show: false,
message: ''
}
},
methods: {
close () {
this.$store.commit('closeDialog')
}
},
computed: {
...mapState({
show: 'show'
})
}
}
</script>
The store is, as the name says, a store. You have a centralized tree where you save data, not functionalities. Another reason is that functions are not serializable.
You could create this component in a global way by injecting the function as prop or by using emit and handling the functionality in the parent.