VueJS - V-for doesn't re-render after data is updated and needs page refresh to see the change - javascript

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.

Related

When moving between Dynamic Pages with NuxtLink, the data of store cannot be retrieved

A demo is provided below.
stackblitz
When I move from the top page to a post page, the correct content is displayed, but when I move from a post page to another post page, the correct content is not displayed.
However, reloading will display the correct content.
Could you please show us how to display the correct content after transitioning from one posting page to another?
The code for the submission page is as follows.
// pages/post/_id.vue
<template>
<div></div>
</template>
<script>
import { fetchPosts } from '../../lib/post';
export default {
name: 'Post',
layout: 'post/index',
async asyncData({ route, store }) {
const posts = await fetchPosts();
const post = posts.find(({ id }) => id === route.params.id);
store.dispatch('setPost', post);
store.dispatch('setPosts', posts);
},
};
</script>
// layouts/post/index.vue
<template>
<div>
<h1 v-if="post">{{ post.title }}</h1>
<p v-if="post">{{ post.title }} page</p>
<ul>
<li v-for="post in posts" :key="post.id">
<NuxtLink :to="'/post/' + post.id">
{{ post.title }}
</NuxtLink>
</li>
</ul>
<NuxtLink to="/">Top</NuxtLink>
</div>
</template>
<script>
export default {
data() {
return {
post: null,
posts: [],
};
},
created() {
this.post = this.$store.getters['post'].post;
this.posts = this.$store.getters['posts'].posts;
},
};
</script>
The process flow is as follows
pages retrieves data from the server and dispatches it to the store
laytous retrieves data from the store and displays the data in laytous
I know that the use of pages and layouts is not common, but the project I am currently working on specifies this usage and I cannot change this usage.
That's because the layout was already rendered when the route changes (when the route changes, the layout/component used is already created, so the hook created is not called again), a simple fix would be to add a watch for the route:
...
watch: {
'$route': function (value) {
this.post = this.$store.getters['post'].post;
this.posts = this.$store.getters['posts'].posts;
}
},
...

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.

Static HTML Elements loads before data fetch in Vue Application

I'm using VueJS and VueRouter to create my application. My problem is that when I'm fetching data the static HTML elements used on the Vue Component load before the actual fetch, causing empty forms and tables to be displayed before data is fetched. I call my fetch method from the created() lifecycle hook, so the result is confusing me. Here are my code so far:
<template>
<div id="app">
<h1>My Data length: {{fetchedData.length}}</h1>
<li v-for="value in fetchedData">{{value}}</li>
</div>
</template>
export default {
name: "MyVueComponent",
data() {
return {
fetchedData: [],
}
},
methods: {
async getData() {
await fetch('https://cli-vue-application.herokuapp.com/user/getUser', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
})
.then(function (response) {
return response.json();
}).then(function (data) {
this.fetchedData= data
}.bind(this));
}
},
created() {
this.getData();
}
}
The data is fetched correctly, but the h1 element displays first and the length is "0" for the first couple of seconds, because the fetch happnds after the static HTML is loaded as explained above.
you could wrap everything in a simple if fetchedData has something see everything, otherwise not:
<template>
<div id="app">
<div v-if="fetchedData.length">
<h1>My Data length: {{fetchedData.length}}</h1>
<li v-for="value in fetchedData">{{value}}</li>
</div>
</div>
</template>
if you want you can also add an else, that is, if nothing has arrived yet, instead of showing nothing enter a "loading" text or better a spinner like these:
https://bootstrap-vue.org/docs/components/spinner#spinners
therefore:
<template>
<div id="app">
<div v-if="fetchedData.length">
<h1>My Data length: {{fetchedData.length}}</h1>
<li v-for="value in fetchedData">{{value}}</li>
</div>
<div v-else>
Loading...
//Or your spinner
</div>
</div>
</template>

Javascript/Vue js/Firestore: innerHTML is null, but it worked in the first time

I am working on my own projects I made a query and it gets the total users and store it in the p tag.
<v-card class="mt-10 mb-5 users" max-width="344">
<v-card-text>
<p class="display-1 text--primary text-center">Users</p>
<div class="display-1 text--primary text-center">
<p id="users"></p>
</div>
</v-card-text>
</v-card>
created() {
// Get all user profile
db.collection("Profile").get().then((res) => {
document.getElementById('users').innerHTML = res.size
})
}
But now I get the error I didn't change anything.
The error
Uncaught (in promise) TypeError: Cannot set property 'innerHTML' of null
As others have mentioned, Vue works best when you use data to drive your templates. Directly manipulating the DOM is an anti-pattern.
For example, use a data property for the information you want to display and assign it a value when your query completes
<p>{{ profileCount }}</p>
export default {
data: () => ({ profileCount: null }),
async created () {
const { size } = await db.collection("Profile").get()
this.profileCount = size
}
}
Do not manipulate DOM directly as it's normally done while using vanilla JS (plain javascript) or jQuery because when you are using vue.js it'snice to follow reactive pattern.
<template>
<p> {{ users }} </p>
</template>
<script>
export default {
data() {
return {
users: 0
};
},
// you can use created or mounted, see which works
created() {
db.collection("Profile").get().then((res) => {
this.users = res.size
})
}
};
</script>

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.

Categories