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.
Related
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.
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.
So this code does adds or delete an entry, But whenever I add or delete, it does not show the changes or rather re-render. I need to refresh the page in order to see what changes had.
note: I am using ME(Vue)N stack.
I have this code:
<script>
import postService from '../../postService';
export default {
name: 'postComponent',
data() {
return {
posts: [],
error: '',
text: ''
}
},
async created() {
try {
this.posts = await postService.getPosts();
}catch(e) {
this.error = e.message;
}
},
methods: {
async createPost() {
await postService.insertPost(this.text)
this.post = await postService.getPosts();
// alert(this.post,"---")
},
async deletePost(id) {
await postService.deletePost(id)
this.post = await postService.getPosts();
// alert(this.post)
}
}
}
</script>
<template>
<div class="container">
<h1>Latest Posts</h1>
<div class="create-post">
<label for="create-post">input...</label>
<input type="text" id="create-post" v-model="text" placeholder="Create a post">
<button v-on:click="createPost">Post</button>
</div>
<!-- CREATE POST HERE -->
<hr>
<p class="error" v-if="error">{{error}}</p>
<div class="posts-container">
<div class="post"
v-for="(post) in posts"
v-bind:key="post._id"
v-on:dblclick="deletePost(post._id)"
>
{{ `${post.createdAt.getDate()}/${post.createdAt.getMonth()}/${post.createdAt.getFullYear()}`}}
<p class="text">{{ post.username }}</p>
</div>
</div>
</div>
</template>
sorry if there's an error in the snippet. I just needed to show the code and I cant make the script work on the code sample {}.
Any help would be appreciate. Vuejs beginner here.
This code is copied and typed through a youtube tutorial.
Your component has a data property posts, but you're assigning to this.post in several places in the code.
I suspect a typo, but it's also worth remembering that if this additional property (this.post) isn't available when the component is instantiated, it won't be (magically) converted into a reactive property when you create/assign to it.
I'd like to render new component in vue.js as if it's new page.
I'm trying to do it with something called "dynamic component"
parent: Post.vue
child: Detail.vue
so, if one of the posts is clicked, Post is off and Detail is on.
The thing is I have to send clicked post's id to the Detail.
Here's some of my code.
Post.vue
<template>
<div>
<div v-if="loading">
loading...
</div>
<div v-else class="container">
<ul>
<li v-for="(post, index) in filteredPosts" v-bind:key="post.no">
<section class="post__main">
<div #click..?? class="main__title">{{post.title}}</div>
</section>
</li>
</ul>
</div>
</div>
</template>
<script>
created() {
axios.get(this.url, {
params: {
page: this.page,
ord: this.ord,
category: []
}
}).then(res => {
this.posts = res.data.list;
this.loading = false;
this.page++;
});
Detail.vue
<template>
<div>
{{contents}}
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'Datail',
data() {
return {
req_no: null,
contents: {}
}
},
created() {
axios.get(url, {
params: {
req_no: this.req_no
}
}).then(res => {
this.contents = this.res.data
});
}
}
</script>
I feel like I can do it with props and v-if.
Can someone help me? Thanks!
Once a post is clicked, pass post id to the click handler. In the click handler, route to detail.vue passing post id as route param.
like below:
<li v-for="(post, index) in filteredPosts" v-bind:key="post.no">
<section class="post__main">
<div #click="onPostClick(post.id)" class="main__title">{{post.title}}</div>
</section>
</li>
And in your click handler:
onPostClick(id: number) {
this.$router.push({
name: 'details',
params: {
id
}
});
}
This will work provided you set up vue router correctly in your app and have a valid route for details.
You can access the post id in details component as follows:
created() {
this.postId = this.$route.params.id;
}
I would take a look at the <component> which takes a prop :to and renders a component, this is good for something like tabs where you are rendering different component from a the same general location on the page without reloading the whole page. See here:
https://v2.vuejs.org/v2/guide/components-dynamic-async.html
This seems to be a very good use case for you, just pass into the component the props you need.
I have a Vue app which has a form which you can submit and all the submissions are shown on a different page with the option to delete them or give each a thumbs up. The issue I am having is targeting each entries thumbsUp to increment it by one. For example, I have a function "thumbsUp" which returns
return this.forms.form[1].thumbsUp++ on each click, but I would like it to target each entry automatically.
Forum submission:
<script>
import {formRef} from '../firebase' //imports reference to form object stored in firebase
export default {
data(){
return{
submitted:false,
form:{
name:'',
state:'',
review:'',
thumbsUp: 0
},
name:'hey',
}
},
methods: {
//pushes this.form object as {form} to firebase using the formRef
submitForm(){
formRef.push({form: this.form, edit: false})
this.submitted = true
}
}
}
</script>
Forum review page:
<template lang="html">
<div class="" class="wrapper">
<div v-for="review of forms" class="reviews">
{{review.name}}
<h1><strong>{{review.form.name}}</strong>, from <strong>{{review.form.state}}</strong> said: </h1>
<p>{{review.form.review}}</p>
<button #click="removePost(review['.key']) "type="button" name="button">Remove Post</button>
<span >Thumbs up: {{review.form.thumbsUp}} </span>
<button #click="thumbsUp" type="button" name="button">Thumbs up!</button>
</div>
</div>
</template>
<script>
import {formRef} from '../firebase'
export default {
data(){
return{
}
},
firebase:{
forms: formRef //stores form reference inside of "forms"
},
methods:{
//takes key from child of formRef to remove post
removePost(key){
formRef.child(key).remove()
},
thumbsUp(){
return this.forms.form[1].thumbsUp++ //needs fix
}
},
}
On the button click function, pass the form element that you loop through. In your case, thumbsUp(review.form).
Then in your thumbsUp function,
thumbsUp(form) {
form.thumbsUp +=1;
}