I am new to JS and Vue, so please bear with me :)
I have a table that is rendered using two Vue components which are a parent (the table - orders) and child (the row - order).
There is a button that can be pressed on each row of the table that carries out an AJAX against that row, but I also need to have the table (parent) refresh when the action is carried out so it has the updated data.
I think I need to use $emit in the child to pass the action on to the parent, but I can't get it to work. Here is the code (sorry its long, I removed everything non-essential);
const order = {
template: `
...// table content
<td><button class="btn btn-default btn-sm" #click="assignAdvisor(id,
selectedOption)">Set Advisor</button></td>
`,
methods: {
// following is the method that is run when the button is pressed
assignAdvisor(id, selectedOption) {
axios.post('url').then(response => {
..// show response message
orders.$emit('refreshAfterUpdate'); // also tried
// this.$parent.$emit(...)
})
},
};
const orders = {
components: { order, },
props: {
orders: {
type: Object,
},
},
mounted() {
// this is basically the code that I need to re-run when button is pressed,
// which I have repeated below in a method
var refresh = () => {
axios.get('/admin/ajax/unassigned-orders')
.then(response => {
this.ordersData = response.data;
setTimeout(refresh, 5000);
});
}
refresh();
},
methods: {
refreshAfterUpdate() {
axios.get('/admin/ajax/unassigned-orders')
.then(response => {
this.ordersData = response.data;
console.log(response);
});
},
}
};
new Vue({
render(createElement) {
const props = {
orders: {
type: Object,
},
};
return createElement(orders, { props });
},
}).$mount('#unassignedOrders');
I don't get any error message or anything - it just doesn't work.
Thanks
OK so thanks to #Patrick Steele I have figured it out.
I was not using $on - oops.
Added code to the mounted() section and it now works:
const order = {
template: `
...// table content
<td><button class="btn btn-default btn-sm" #click="assignAdvisor(id,
selectedOption)">Set Advisor</button></td>
`,
methods: {
// following is the method that is run when the button is pressed
assignAdvisor(id, selectedOption) {
axios.post('url').then(response => {
..// show response message
orders.$emit('refreshAfterUpdate'); // also tried
// this.$parent.$emit(...)
})
},
};
const orders = {
components: { order, },
props: {
orders: {
type: Object,
},
},
mounted() {
// this is basically the code that I need to re-run when button is pressed,
// which I have repeated below in a method
var refresh = () => {
axios.get('/admin/ajax/unassigned-orders')
.then(response => {
this.ordersData = response.data;
setTimeout(refresh, 5000);
});
}
refresh();
$this.on('refreshAfterUpdate', () => {
axios.get('/admin/ajax/unassigned-orders')
.then(response => {
this.ordersData = response.data;
console.log(response);
});
},
},
},
};
new Vue({
render(createElement) {
const props = {
orders: {
type: Object,
},
};
return createElement(orders, { props });
},
}).$mount('#unassignedOrders');
Related
first of all I am using the Mockjs to simulate the backend data:
{
url: "/mockApi/system",
method: "get",
timeout: 500,
statusCode: 200,
response: { //
status: 200,
message: 'ok',
data: {
'onlineStatus|3': [{
'statusId': '#integer(1,3)',
'onlineStatusText': '#ctitle(3)',
'onlineStatusIcon': Random.image('20*20'),
'createTime': '#datetime'
}],
'websiteInfo': [{
'id|+1': 1,
}]
}
}
}
the data structure would be: https://imgur.com/a/7FqvVTK
and I retrieve this mock data in Pinia store:
import axios from "axios"
import { defineStore } from "pinia"
export const useSystem = defineStore('System', {
state: () => {
return {
systemConfig: {
onlineStatus: [],
},
}
},
actions: {
getSystemConfig() {
const axiosInstance = axios.interceptors.request.use(function (config) {
// Do something before request is sent
config.baseURL = '/mockApi'
return config
}, function (error) {
// Do something with request error
return Promise.reject(error);
})
axios.get('/system/').then(res => {
this.systemConfig.onlineStatus = res.data.data.onlineStatus
})
// console.log(res.data.data.onlineStatus)
axios.interceptors.request.eject(axiosInstance)
}
}
})
I use this store in the parent component Profile.vue:
export default {
setup() {
const systemConfigStore = useSystem()
systemConfigStore.getSystemConfig()
const { systemConfig } = storeToRefs(systemConfigStore)
return {
systemConfig,
}
},
computed: {
getUserOnlineStatusIndex() {
return this.userData.onlineStatus//this would be 1-3 int.
},
getUserOnlineStatus() {
return this.systemConfig.onlineStatus
},
showUserOnlineStatusText() {
return this.getUserOnlineStatus[this.getUserOnlineStatusIndex - 1]
},
},
components: {UserOnlineStatus }
}
template in Profile.vue I import the child component userOnlineStatus.vue
<UserOnlineStatus :userCurrentOnlineStatus="userData.onlineStatus">
{{ showUserOnlineStatusText }}
</UserOnlineStatus>
here is what I have got https://imgur.com/fq33uL8
but I only want to get the onlineStatusText property of the returned object, so I change the computed code in the parent component Profile.vue:
export default {
setup() {
const systemConfigStore = useSystem()
systemConfigStore.getSystemConfig()
const { systemConfig } = storeToRefs(systemConfigStore)
return {
systemConfig,
}
},
computed: {
getUserOnlineStatusIndex() {
return this.userData.onlineStatus//this would be 1-3 int.
},
getUserOnlineStatus() {
return this.systemConfig.onlineStatus
},
showUserOnlineStatusText() {
return this.getUserOnlineStatus[this.getUserOnlineStatusIndex - 1]['onlineStatusText']//👀I chage it here!
},
},
components: {UserOnlineStatus }
}
but I will get the error in the console and it doesn't work:
https://imgur.com/Gb68Slk
what should I do if I just want to display the specific propery of the retrived data?
I am out of my wits...
I have tried move the store function to the child components, but get the same result.
and I google this issue for two days, nothing found.
Maybe it's because of I was trying to read the value that the Profile.vue hasn't retrieved yet?
in this case, how could I make sure that I have got all the value ready before the page rendered in vue3? Or can I watch this specific property changed, then go on rendering the page?
every UX that has data is coming from remote source (async data) should has spinner or skeleton.
you can use the optional chaining for safe access (if no time to await):
return this.getUserOnlineStatus?.[this.getUserOnlineStatusIndex - 1]?.['onlineStatusText']
App.vue
template:
<ResponsiveNavigation
:nav-links="navLinks"
/>
script
data: () => ({
navLinks: []
}),
created: function() {
this.getSocialNetworks();
},
methods: {
getSocialNetworks() {
var self = this;
axios
.get(MY_API_URL)
.then(function(res) {
var fb_url = res.data.data.filter(obj => {
return obj.key === "Social_Facebook";
});
self.navLinks.fb = fb_url[0].defaultValue;
//
var ig_url = res.data.data.filter(obj => {
return obj.key === "Social_Instagram";
});
self.navLinks.ig = ig_url[0].defaultValue;
//
})
.catch(function(error) {
console.log("Error", error);
});
}
}
ResponsiveNavigation.vue:
<a :href="$props.navLinks.fb"></a>
if I console.log the $props.navLinks I have everything stored.
however in the href doesn't work after the FIRST load.
I am fairly sure that this is due to the reactive nature and UNreactive of arrays.
You're not really using an array, but an object
data: () => ({
navLinks: []
}),
to
data: () => ({
navLinks: {
fb:'',
ig:''}
}),
and I think it would setup the reactive props more suitably.
If you need an array, then use array.push() so it can react accordingly. I may also consider moving it to the mounted() method. Finally, you put $props in your code, do you have other props you've not shown us which may be conflicting?
hello how to push new data to the top list using vue.js and laravel, I tried but still failed, I hope someone can help with the problem.
this is my Controller
public function addComment()
{
$this->validate(request(), [
'comment' => 'required',
]);
$comment = [
'comment' => request()->comment,
'article_id' => request()->article_id,
'user_cid' => Auth::user()->user_cid,
];
$comment = ArticleComment::create($comment);
return new ArticleCommentResource($comment);
}
and this is my Vue.js Method
data() {
return {
data: [],
comments:[],
form: new Form({
comment: '',
article_id: this.articleid,
})
}
},
methods: {
onSubmit() {
this.showLoader = true
this.form.post('add-comment')
.then(response => {
console.log(response.article_id);
this.form.article_id = response.article_id;
});
},
}
how to handle it, thank you
I hope someone can help
Assuming your list simply loops through your comments array, you need to push the response at the first position of the list:
onSubmit() {
this.showLoader = true
this.form.post('add-comment')
.then(response => {
this.comments.unshift(response);
});
},
This assumes that response is the actual comment (I can't see into your form class).
<script>
import Form from 'form-backend-validation';
export default {
data:() => ({
form: new Form({
article_id: null,
}),
}),
mounted() {
this.fetch();
},
methods: {
async fetch() {
const response = await this.form.post('add-comment');
this.form.article_id = response.comment.article_id;
}
}
}
</script>
Please try this one.
When I click a profile (of an author) component, I can't figure out how it should render a scoped sub-component, listing the main entities of the app, so-called fabmoments (containers for 3D print information).
My current solution looks like this:
export default {
name: 'Multipe',
props: [
'author'
],
data () {
return {
// search: '',
localAuthor: '',
fabmoments: []
}
},
created () {
this.localAuthor = this.author
if (typeof localAuthor !== 'undefined') {
this.$http.get(`/users/${this.$route.params.id}/fabmoments`)
.then(request => this.buildFabmomentList(request.data))
.catch(() => { alert('Couldn\'t fetch faboments!') })
} else {
this.$http.get('/fabmoments')
.then(request => this.buildFabmomentList(request.data))
.catch(() => { alert('Couldn\'t fetch faboments!') })
}
},
methods: {
buildFabmomentList (data) {
this.fabmoments = data
}
},
components: {
// Box
}
}
This renders all in the profile, where it should render a list scoped to the current profile's author.
And it renders nothing in the home (without receiving the prop), where it should render all.
I am not much of star in JavaScript. What am I doing wrong?
UPDATE
This works as a solution, though not very elegant.
export default {
name: 'Multipe',
props: [
'author'
],
data () {
return {
fabmoments: []
}
},
created () {
if (this.author.id >= 0) {
this.$http.get(`/users/${this.$route.params.id}/fabmoments`)
.then(request => this.buildFabmomentList(request.data))
.catch(() => { alert('Couldn\'t fetch faboments!') })
} else {
this.$http.get('/fabmoments')
.then(request => this.buildFabmomentList(request.data))
.catch(() => { alert('Couldn\'t fetch faboments!') })
}
},
methods: {
buildFabmomentList (data) {
this.fabmoments = data
}
},
components: {
// Box
}
}
Not sure which part is wrong, but you may definitely debug your code to find out why fabmoments is empty array assuming there is no error occurred yet.
There are three parts to debug:
http response -- to check if data is properly returned
this -- to check if this pointer still points at the component
template -- to check if fabmoments are correctly bind to the element
At last, it would be better to separate your http request logics from your components.
Good luck!
I have been working on the feature of comment deleting and came across a question regarding a mutation for an action.
Here is my client:
delete_post_comment({post_id, comment_id} = {}) {
// DELETE /api/posts/:post_id/comments/:id
return this._delete_request({
path: document.apiBasicUrl + '/posts/' + post_id + '/comments/' + comment_id,
});
}
Here is my store:
import Client from '../client/client';
import ClientAlert from '../client/client_alert';
import S_Helper from '../helpers/store_helper';
const state = {
comment: {
id: 0,
body: '',
deleted: false,
},
comments: [],
};
const actions = {
deletePostComment({ params }) {
// DELETE /api/posts/:post_id/comments/:id
document.client
.delete_post_comment({ params })
.then(ca => {
S_Helper.cmt_data(ca, 'delete_comment', this);
})
.catch(error => {
ClientAlert.std_fail_with_err(error);
});
},
};
delete_comment(context, id) {
context.comment = comment.map(comment => {
if (!!comment.id && comment.id === id) {
comment.deleted = true;
comment.body = '';
}
});
},
};
export default {
state,
actions,
mutations,
getters,
};
I am not quite sure if I wrote my mutation correctly. So far, when I am calling the action via on-click inside the component, nothing is happening.
Guessing you are using vuex the flow should be:
according to this flow, on the component template
#click="buttonAction(someParams)"
vm instance, methods object:
buttonAction(someParams) {
this.$store.dispatch('triggerActionMethod', { 'something_else': someParams })
}
vuex actions - Use actions for the logic, ajax call ecc.
triggerActionMethod: ({commit}, params) => {
commit('SOME_TRANSATION_NAME', params)
}
vuex mutations - Use mutation to make the changes into your state
'SOME_TRANSATION_NAME' (state, data) { state.SOME_ARG = data }