How to render vuejs component on sibling submit - javascript

I have the following code
<body>
<div class="content" id="app">
<file-management></file-management>
<attachment-list></attachment-list>
</div>
<script src="{{ asset('js/app.js') }}"></script>
</body>
FileManagement component code:
<template>
<div>
<button type="button" #click="storeList()">
Save
</button>
</div>
</template>
<script>
export default {
methods: {
storeList: function () {
axios.post('/list', this.data, config)
.then(() => {
// on save I want to be able to load the table again that is found in AttachmentList component
});
},
}
}
</script>
AttachmentList component code:
<template>
<div>
<tr v-for="attachment in attachments" :key="attachment.id">
<td>{{ attachment.name }}</td>
</tr>
</div>
</template>
<script>
export default {
data() {
return {
attachments: []
}
},
methods: {
getList() {
axios.get(`/list`)
.then((data) => {
this.attachments = data;
});
}
}
}
</script>
What I want to do is that I want to be able to load the table of the list when I click save in the other component (after the post request has completed). How will I be able to achieve this?

Easiest way would be to have your FileManagement component emit an event which the parent can listen to, then trigger the AttachmentList#getList() method.
For example
// in AttachmentList
methods: {
async storeList () {
await axios.post('/list', this.data, config)
this.$emit('list-updated')
}
}
and in the parent template
<file-management #list-updated="$refs.list.getList()"></file-management>
<attachment-list ref="list"></attachment-list>

This is how I would proceed.
create a parent component for the siblings.
add a boolean data member (flag) to it with the status of the clicked button.
emit a signal from FileManagement when the button is clicked.
catch this signal in the parent component to set the flag.
pass this flag to the AttachmentList component as a prop.
use this flag inside a v-if to show / hide the the table.

Related

Call function on child component after emit

I'm trying to create a custom button component with a spinner, it works fine - disables the button and shows a spinner when clicked but I'm having trouble resetting the button's state when whatever is "loading" is finished.
My current code is as follows:
Parent.vue
<template>
<custom-button #button-click="requestAPI" :disabled="canRequestAPI">
Request API
</custom-button>
</template>
<script>
methods: {
async requestAPI(){
// Request API here
}
</script>
CustomButton.vue
<template>
<button :disabled="disabled || loading" #click="processClick" :loading="loading">
<img src="/loader.svg" />
<span class="buttonContent">
<slot />
</span>
</button>
</template>
<script>
props: ["disabled"],
data() {
return: {
loading: false,
}
},
methods: {
processClick(){
this.$emit('button-click');
this.loading = true;
}
}
</script>
The spinner shows and the API is requested but I can't work out how to stop the spinner in a good way. I could create a prop to watch and set loading to false but I'd like to be able to use this custom button several without having to add lots of different variables.
Ideally i'd be able to do something like:
Parent.vue
<script>
methods: {
async requestAPI(e){
// Request API here
e.stopLoading();
}
</script>
CustomButton.vue
<script>
methods: {
stopLoading(){
this.loading = false;
}
}
</script>
Worked it out, Just emitted the stopLoading function to the parent and called that.
Parent.vue
<script>
methods: {
async requestAPI(stopLoading){
// Request API here
stopLoading();
}
}
</script>
Child.vue
methods: {
processClick(){
this.$emit('button-click', () => this.stopLoading());
this.loading = true;
},
stopLoading(){
this.loading = false;
}
}

Delete function in parent component in vue.js

I have a ticket as you can see in the picture below:
I have a delete button as a component and I am trying to add delete functionality to it. I am using this component in my ticket component. So this is my delete component:
<template>
<div id="delete-button" #click.prevent="removeProductFromCart(item.id)">
<input type="checkbox" id="checkbox">
<div id="bin-icon">
<div id="lid"></div>
<div id="box">
<div id="box-inner">
<div id="bin-lines"></div>
</div>
</div>
</div>
</div>
</template>
<script>
import cartHelper from "../helpers/cartHelper";
export default {
props: {
item: Object,
},
data() {
return {
loading: false,
};
},
methods: {
removeProductFromCart(id) {
this.loading = true;
setTimeout(() => {
cartHelper.removeFromCart(id, (response) => {
this.$store.dispatch('removeProductFromCart', {
cart: response.data,
})
this.loading = false
});
}, 1000)
}
}
};
</script>
So the parent component is ticket component:
[![<template>
<div id="sold-tickets">
<div class="card">
<div class="sold-tickets-actions properties">
<div class="sold-tickets-inner">
<DeleteButton :item = "item" />
</div>
</div>
</div>
</div>
</template>][1]][1]
<script>
import image from "../../../../img/Hallenbad.jpg";
import DeleteButton from "./DeleteButton";
import cartHelper from "../helpers/cartHelper";
export default {
props: {
item: Object,
},
components: {DeleteButton},
data() {
return {
image: image,
};
},
};
</script>
My problem is, the ticket is being deleted even when I click outside of the child component (Delete component). But I want to use the delete component like a button and I only want to delete the ticket when it is clicked, not outside of the button.
Try to stop propagation. You probably do not even need the prevent modifier.
#click.prevent.stop='...'
or
#click.stop='...'
If the display type is block, you might also want to inspect the div to see if it is actually filling the entire width of the page. If so, use an inline type, a flex layout, or similiar.

I want to handle components asynchronously in Nuxt.js and display alert messages

What I want to come true
I want to display an alert message considering the result of the data sent to the server.
However, since alert messages are managed by another component, it is necessary to call the component asynchronously.
The official Vue.js documentation used Vue.component, but what's the right way to do it with Nuxt.js?
Code
I want to use search.vue in success.vue
search.vue
<template>
<v-app>
<div
class="teal lighten-1 background pa-10"
>
<!-- <div
v-if="responseBook === 200"
> -->
<alert-success />
<v-sheet
width="1100px"
class="mx-auto pa-5 rounded-xl"
color="grey lighten-5"
min-height="500px"
>
<!-- 書籍検索、表示 -->
<BookPostDialog />
<!-- 選択されたデータの表示 -->
<BookPostSelected />
</v-sheet>
</div>
</v-app>
</template>
<script>
export default {
computed: {
responseBook () {
return this.$store.state.book.responseBook.status
}
}
}
</script>
<style lang="scss" scoped>
.background {
background-image: url('~/assets/images/tree.png');
background-repeat: space repeat;
}
</style>
Alert/success.vue
<template>
<v-alert type="success">
Succeeded
</v-alert>
</template>
If you want to use that kind of feature, you'll be better suited looking for something like this component: https://buefy.org/documentation/toast
Or anything like this in the jungle of CSS frameworks, pretty sure each of them have one.
Or implement it yourself, for this, you need to rely on portals.
For Vue2, this is how to do achieve it: https://portal-vue.linusb.org/guide/getting-started.html#enabling-disabling-the-portal
<portal to="destination" :disabled="true">
<p>
Your content
</p>
</portal>
If you want to show success.vue component after the connection to server (getting or posting data), you can use v-if as follows:
search.vue
<template>
<div>
<p>search compo</p>
<div v-if="this.$store.state.book.responseBook == 'ok'">
data was received.
<success />
</div>
</div>
</template>
<script>
export default {
mounted() {
this.$store.dispatch('getData')
}
}
</script>
success.vue
<template>
<div>
succeess compo
</div>
</template>
And then in your store/index.js file:
import Vuex from "vuex";
const createStore = () => {
return new Vuex.Store({
state: {
book: {
responseBook: ""
}
},
mutations: {
bookMutate(state, data) {
state.book.responseBook = data;
}
},
actions: {
getData(vuexContext) {
let vue = this;
// your request is here
setTimeout(function() {
vue.$axios.$get("https://pokeapi.co/api/v2/pokemon/ditto").then((result) => {
console.log(result);
vuexContext.commit("bookMutate", "ok");
}).catch(err => {
console.log(err);
})
}, 10000)
},
}
});
};
export default createStore;
I intentionally used setTimeout() in my action to see that the success component is loaded after the data was received. in actual situation it is better to use this action:
getData(vuexContext) {
this.$axios.$get("https://pokeapi.co/api/v2/pokemon/ditto").then((result) => {
console.log(result);
vuexContext.commit("bookMutate", "ok");
}).catch(err => {
console.log(err);
})
},
I used axios for calling the api but you can use your own method of getting data. but after that you must commit the mutation to change the state.

vue.js post list not updating after form submission

In my vue app I have two components one which is a form that posts the form data to my api. And the other gets and displays these posts in a section on the page. My issue is that when I submit a new post the posts lists aren't updated. The data stays the same unless I refresh the page. How can I get my posts list to update when I submit the form?
My Code:
client/src/App.vue
<template>
<div id="app">
<MainHeader :modalVisability="modal" v-on:showModal="toggleModal" />
<div id="content_wrap">
<Summary />
</div>
<OppForm :modalVisability="modal" />
</div>
</template>
<script>
import MainHeader from './components/MainHeader.vue';
import OppForm from './components/oppForm.vue';
import Summary from './components/Summary.vue';
export default {
name: 'App',
components: {
MainHeader,
Summary,
OppForm
},
data () {
return {
modal: false
}
},
methods: {
toggleModal (modalBool) {
this.modal = modalBool;
}
}
}
</script>
client/src/components/oppForm.vue
<template>
<div id="opp_form_modal" >
<form #submit.prevent="SubmitOpp" v-if="modalVisability">
<input type="text" name="company_name" v-model="company_name">
<button type="submit">Submit</button>
</form>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'oppForm',
props: {
modalVisability: Boolean,
},
data () {
return {
company_name: ''
}
},
methods: {
SubmitOpp () {
axios.post('http://localhost:5000/', {
company_name: this.company_name,
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
}
}
}
</script>
client/src/components/Summary.vue
<template>
<div id="summary_section">
<h2>Summary</h2>
<div id="summary_board">
<div class="column">
<div class="head">
<h3>Opportunities</h3>
</div>
<div class="body">
<div class="post"
v-for="(post, index) in posts"
:key="index"
>
<p class="company">{{ post.company_name }}</p>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return{
posts: []
};
},
created() {
axios.get('http://localhost:5000/')
.then(res => {
// console.log(res);
const data = res.data;
this.posts = data;
})
.catch(error => console.log(error));
}
}
</script>
The problem is that you're actually fetching your posts only on the app creation (i.e. inside the created() method).
You should wrap your axios call inside a function updatePosts() and then call it whenever you add a new post successfully, or you could create a custom event that is triggered whenever a new post is added.
created() is called only once (see vue lifecycle) so you fetch API before submitting form.
Try to add some console.log to understand what is called when.
You could use an global event bus and send form value as event data to summary. I could imagine also a solution where event is used to "tell" summary that form was submitted (just boolean, not data itself). In summary you then call API each time you receive event.
Or simple add an "update" button to summary to manually call API.
See Communication between sibling components in VueJs 2.0
or global vue instance for events for detailed examples.

Bootstrap-Vue Table not updating with table.refresh()

I have a table that should be changing its data constantly. When a button is clicked in the parent component, a call is made to the server and json file is then loaded into a child component(The table) through a prop.
Whenever a different button is clicked and the table needs to reload the data, it doesnt. I have tried doing:
this.$refs.dmTable.refreshTable();
and
this.$forceUpdate()
Rough structure of my code
Parent.vue
<template>
<Button getData("this")>Get This Data</Button>
<Button getData("that")>Get ThatData</Button>
<MyTable v-if="showTable" :data="data" />
<template>
<script>
export default {
data(){
return{
showTable:false,
data: null
}
},
methods:{
getData(dataType){
getDataFromServer(dataType).then(data =>{
this.data = data.body
this.showTable = true
})
}
}
}
</script>
MyTable.vue
<template>
<b-table :items="data"><b-table/>
</template>
<script>
export default{
props: ["data"]
}
</script>
If you click the first button, the data loads fine into the table. But if you then click the second button and try to load new data into the table nothing happens. I tried creating a method called updateTable() within the child component that contains this.$refs.myTable.update() but it does nothing.
Edit: One thing to note about this, the data that I am loading onto this table is quite large, 5mb json file.
The actual function that gets called:
showTableView(model, type) {
request
.get(
`/table_files/${this.folder_name}/${model}.json`
)
.then(response => {
this.type = type;
this.current_model = model;
if (type === "joins") {
this.tlorderData = response.body.fields.joins;
this.show_joins_table = true;
this.showTable = false;
this.refreshTable()
return false; // MAYBE RETURNING FALSE BREAKS THE RERENDERING?
}
else if (type === "dimension_groups") {
this.show_joins_table = false;
this.showTable = true;
this.tlorderData = response.body.fields.dimension_groups;
this.refreshTable()
return false;
}
})
.catch(err => {
this.response_error = err;
});
},
I don't see where you are defining data and showTable in your main app component.setting this.data to a value is not reactive (it is creating a non-reactive property on the app component).
Try this:
<template>
<Button #click="getData('this')">Get This Data</Button>
<Button #click="getData('that')">Get ThatData</Button>
<MyTable v-if="showTable" :data="data" />
<template>
<script>
export default {
data() {
return {
data: [],
showTable: false
}
},
methods:{
getData(dataType){
getDataFromServer(dataType).then(data =>{
this.data = data.body
this.showTable = true
})
}
}
}
</script>
The data() section will define data and showTable as reactive properties on your app/component instance.
The problem was in something that I did not mention in my code.
The way I was loading in data into the table was like this:
<template>
<b-table :items="reData"><b-table/>
</template>
<script>
export default{
props: ["data"],
data(){
return{
reData: this.data
}
}
}
</script>
This prevented my table from updating whenever data gets changed in the prop, note that I am passing in my prop to data() then passing THAT into my table. So the prop would change but the actual data that the table was showing would not.
Proper Way to pass in from props to table to prevent this from happening:
<template>
<b-table :items="data"><b-table/>
</template>
<script>
export default{
props: ["data"]
}
</script>

Categories