Losing my data when i refresh page in vuejs - vue.js

I'm creating a social network for project in my formation, i have a like system and it work.
i have a components cardArticle with all info and i try to do a like count. It work but when i refresh the page or going on other page, i lost all my data likes (my data is not saved)
components/CardArticle.vue
<template>
<div id="card">
<div>
<a class="cardLink">
<img class="card-img" v-if="post.imageUrl !== undefined" :src="post.imageUrl" />
<h2 class="cardTitle"> {{ post.title }}</h2>
<p class="cardDescription"> {{ post.description }}</p>
</a>
</div>
<div class="buttonIcon">
<div>
<button type="button" class="buttonDelete" id="buttonDelete" #click="deletePost"
v-if="this.post.userId === this.user.userId || this.user.isAdmin === true">Supprimer</button>
<button type="button" class="buttonEdit" id="buttonEdit" #click="modifyPost"
v-if="this.post.userId === this.user.userId || this.user.isAdmin === true">
Editer
</button>
</div>
<div class="divIconLike">
<div class="iconLike">
<a #click="sendLike">
<i class="fa-regular fa-thumbs-up"></i>
</a>
</div>
<div class="countLike">
<p> {{ likes }} </p>
</div>
</div>
</div>
</div>
</template>
<script>
import axios from "axios";
import router from "../router/index.js";
export default {
name: 'CardArticle',
data () {
return {
likes: 0
}
},
props: {
post: {
type: Object
}
},
computed: {
user() {
return this.$store.getters.user;
}
},
methods: {
sendLike() {
axios.post("http://localhost:3000/api/articles/" + this.post._id + "/like", {
userId: this.user.userId
}, {
headers: {
Authorization: "Bearer " + this.user.token
}
})
.then(response => this.likes = response.data.article.likes)
.catch(error => console.log(error))
}
}
}
</script>
views/home.vue
<template>
<div class="home" v-if="this.user.token !== null">
<CardArticle v-for="post in allPosts" v-bind:key="post.id" :post="post" />
</div>
</template>
<script>
import CardArticle from "../components/CardArticle.vue"
import axios from "axios";
export default {
name: 'HomeArticle',
data () {
return {
post: {
title: "",
description: "",
imageUrl: ""
},
allPosts: [],
}
},
computed: {
user() {
return this.$store.getters.user;
}
},
components: {
CardArticle,
},
mounted() {
axios.get("http://localhost:3000/api/articles", {
headers: {
Authorization: "Bearer " + this.user.token
}
})
.then(response => {
this.allPosts = response.data;
})
.catch(error => {
return error;
})
}
}
</script>
What i should do for not losing my data ?
I would not use vuex or localstorage for that if possible, you have idea ?
Thanks for your help

If you loading data from server, then refresh page, you always will be lose data, because browser loading page again from server, and application will load data again.
If you don't want use vuex (but why not?), you can write data to cookies (by setting cookie value), then load it on app startup (when page is loaded). But it's not best practice at all. You can use vue3-cookies lib (link).
By the way, better learn to use stores, most progressive, I think, is Pinia.
Check: https://pinia.vuejs.org/

i lost all my data likes (my data is not saved)
likes is belong to each articles and It should have been saved to your db and call API to retrieve it again on component mounting:
export default {
name: 'CardArticle',
data () {
return {
likes: 0 // It's not managed by component state
}
},
methods: {
sendLike() {
axios.post("http://localhost:3000/api/articles/" + this.post._id + "/like", {
userId: this.user.userId
}, {
headers: {
Authorization: "Bearer " + this.user.token
}
})
.then(
// invalidates, update allPosts props (emit to parent)
)
.catch(error => console.log(error))
}
}
}

Related

Nuxt.js Hackernews API update posts without loading page every minute

I have a nuxt.js project: https://github.com/AzizxonZufarov/newsnuxt2
I need to update posts from API every minute without loading the page:
https://github.com/AzizxonZufarov/newsnuxt2/blob/main/pages/index.vue
How can I do that?
Please help to end the code, I have already written some code for this functionality.
Also I have this button for Force updating. It doesn't work too. It adds posts to previous posts. It is not what I want I need to force update posts when I click it.
This is what I have so far
<template>
<div>
<button class="btn" #click="refresh">Force update</button>
<div class="grid grid-cols-4 gap-5">
<div v-for="s in stories" :key="s">
<StoryCard :story="s" />
</div>
</div>
</div>
</template>
<script>
definePageMeta({
layout: 'stories',
})
export default {
data() {
return {
err: '',
stories: [],
}
},
mounted() {
this.reNew()
},
created() {
/* setInterval(() => {
alert()
stories = []
this.reNew()
}, 60000) */
},
methods: {
refresh() {
stories = []
this.reNew()
},
async reNew() {
await $fetch(
'https://hacker-news.firebaseio.com/v0/topstories.json?print=pretty'
).then((response) => {
const results = response.slice(0, 10)
results.forEach((id) => {
$fetch(
'https://hacker-news.firebaseio.com/v0/item/' +
id +
'.json?print=pretty'
)
.then((response) => {
this.stories.push(response)
})
.catch((err) => {
this.err = err
})
})
})
},
},
}
</script>
<style scoped>
.router-link-exact-active {
color: #12b488;
}
</style>
This is how you efficiently use Nuxt3 with the useLazyAsyncData hook and a setInterval of 60s to fetch the data periodically. On top of using async/await rather than .then.
The refreshData function is also a manual refresh of the data if you need to fetch it again.
We're using useIntervalFn, so please do not forget to install #vueuse/core.
<template>
<div>
<button class="btn" #click="refreshData">Fetch the data manually</button>
<p v-if="error">An error happened: {{ error }}</p>
<div v-else-if="stories" class="grid grid-cols-4 gap-5">
<div v-for="s in stories" :key="s.id">
<p>{{ s.id }}: {{ s.title }}</p>
</div>
</div>
</div>
</template>
<script setup>
import { useIntervalFn } from '#vueuse/core' // VueUse helper, install it
const stories = ref(null)
const { pending, data: fetchedStories, error, refresh } = useLazyAsyncData('topstories', () => $fetch('https://hacker-news.firebaseio.com/v0/topstories.json?print=pretty'))
useIntervalFn(() => {
console.log('refreshing the data again')
refresh() // will call the 'topstories' endpoint, just above
}, 60000) // every 60 000 milliseconds
const responseSubset = computed(() => {
return fetchedStories.value?.slice(0, 10) // optional chaining important here
})
watch(responseSubset, async (newV) => {
if (newV.length) { // not mandatory but in case responseSubset goes null again
stories.value = await Promise.all(responseSubset.value.map(async (id) => await $fetch(`https://hacker-news.firebaseio.com/v0/item/${id}.json?print=pretty`)))
}
})
function refreshData() { refreshNuxtData('topstories') }
</script>

How to send a delete request from Vue Js to Json server?

I am creating a simple to do list and using Json server as a mock data base. I am trying to add a delete button that will delete the goals that have already added to the Vue application.
This is my main app
<template>
<h1>To Do List </h1>
<addProject />
<div v-for="newGoal in newGoals" :key="newGoal.id" class = 'newGoal'>
<li >{{newGoal.goal}} <deleteGoalVue /> </li>
</div>
</template>
<script>
import addProject from './components/addProject.vue';
import deleteGoalVue from './components/deleteGoal.vue';
export default {
name: 'App',
components: { addProject, deleteGoalVue },
data(){
return {
newGoals: [],
}
},
mounted() {
fetch('http://localhost:3000/newGoals')
.then(res => res.json())
.then(data => this.newGoals = data)
.catch(err => console.log(err.message))
}
}
This is my add project/goal component
<template>
<form #submit.prevent="handleSubmit">
<h3><div class= instructions >Type in your goal for the day</div></h3>
<input type="text" v-model="goal" placeholder="Your goal">
<input type="submit" value="Add Goal" #click="storeGoal" >
</form>
</template>
<script>
export default {
props: ['project'],
data (){
return{
goal: "",
}
},
handleSubmit(){
let project = {
goal: this.goal,
}
fetch('http://localhost:3000/newGoals', {
method: 'POST',
headers: { 'Content-Type' : 'application/json' },
body: JSON.stringify(project)
})
}
}
And this is my Delete Goal component
<template>
<button #click ="deleteGoal" >Delete Goal</button>
</template>
<script>
export default {
methods: {
deleteGoal() {
fetch('http://localhost:3000/newGoals', { method: 'DELETE'})
}
}
}
</script>
The error that I am getting when I press the 'Delete Goal' button says
DELETE http://localhost:3000/newGoals 404 (Not Found)

Why Vue doesn't refresh list using props?

On my App, on mounted() method, I call an API, which give to me a JSON with a list of items; than, I update the prop I've set in my target Homepage component:
Homepage.pages = resJSON.data.pages;
Here's the App code:
<template>
<div id="app">
<Homepage title="PWA Test"/>
</div>
</template>
<script>
import Homepage from './components/Homepage.vue'
export default {
name: 'App',
components: {
Homepage
},
mounted() {
let endpoint = "http://localhost:5000/api/graphql?query={pages(orderBy:{contentItemId:asc}){contentItemId,createdUtc,author,displayText,description%20}}";
fetch(endpoint, {
method:'GET',
headers: {
'Accept':'application/json',
'Content-Type':'application/json'
}
})
.then((response) => {
// check for HTTP failure
if (!response.ok) {
throw new Error("HTTP status " + response.status);
}
// read and parse the JSON
return response.json();
})
.then((res) => {
Homepage.pages = res.data.pages;
})
.catch((error) => {
console.log(error);
});
}
}
</script>
<style>
</style>
Here's the Homepage component:
<template>
<div id="homepage">
<h1>{{ title }}</h1>
<ul>
<li v-for="page in pages" :key="page.description">#{{ page.description }}</li>
</ul>
</div>
</template>
<script>
export default {
name: 'Homepage',
props: {
title: String,
pages: []
}
}
</script>
<style scoped>
</style>
But the ul doesn't update after receiving the JSON and updating the props pages. Where's my error?
you need to get the response.json(); in a data property of the App and then pass it down to the Homepage component. So your code should you look like this,
App code:
<template>
<div id="app">
//binding page into data property
<Homepage title="PWA Test" :pages="pages"/>
</div>
</template>
<script>
import Homepage from './components/Homepage.vue'
export default {
name: 'App',
data: function () {
return {
//data propety
pages : []
}
},
components: {
Homepage
},
mounted() {
let endpoint = "http://localhost:5000/api/graphql?query={pages(orderBy:{contentItemId:asc}){contentItemId,createdUtc,author,displayText,description%20}}";
fetch(endpoint, {
method:'GET',
headers: {
'Accept':'application/json',
'Content-Type':'application/json'
}
})
.then((response) => {
if (!response.ok) {
throw new Error("HTTP status " + response.status);
}
// assign the result to the data property
this.page = response.json();
})
.then((res) => {
Homepage.pages = res.data.pages;
})
.catch((error) => {
console.log(error);
});
}
}
</script>
Do you pass the props in a template after this.pages = res.data.pages?
<Homepage :pages="pages" />
I think there are some mistakes that you have done in your code, if you want change update prop value then you have to initialized your props values in script.
<template>
<div id="homepage">
<h1>{{ title }}</h1>
<ul>
<li v-for="page in currentPages" :key="page.description">#{{ page.description }}
</li>
</ul>
</div>
</template>
<script>
export default {
name: 'Homepage',
props: {
title: String,
pages: []
}
data: function () {
return {
currentPages: this.pages
}
}
}
</script>
I hope this will help you to solve your issue- thanks!

Can't find out why vuex getters ById doensim get a company by id

i am still new to VUEX and i am following this tutorial vuex store
The only thing i am doing differently is i am using sequelize instead of mLab like he does.
This is what my getters look like
export const companyGetters = {
allCompanies: (state, getters) => {
return state.companies
},
companyById: (state, getters) => id => {
if (getters.allCompanies.length > 0) {
return getters.allCompanies.filter(c => c._id === id)[0]
} else {
return state.company
}
}
Exactly like what he did.
My action looks like this
companyById ({commit}, payload) {
commit(COMPANY_BY_ID)
axios.get(`${API_BASE}/companies/${payload}`).then(response => {
console.log(payload, response.data)
commit(COMPANY_BY_ID_SUCCESS, response.data)
})
}
Next in my details i have
<template>
<div>
<company-details :company="company" ></company-details>
</div>
</template>
<script>
import CompanyDetails from '../components/company/CompanyDetails'
export default {
created () {
if (!this.company.companyName) {
this.$store.dispatch('companyById', this.$route.params['id'])
}
},
computed: {
company () {
return this.$store.getters.companyById(this.$route.params['id'])
}
},
components: {
'company-details': CompanyDetails
}
}
</script>
and then finally my companyDetails looks like this
<template>
<v-ons-col style="width: 350px; float:left;">
<v-ons-card>
<h1>{{company}}</h1>
</v-ons-card>
</v-ons-col>
</template>
<script>
export default {
props: ['company']
}
</script>
here is the mutations
export const companyMutations = {
[ALL_COMPANYS] (state) {
state.showLoader = true
},
[ALL_COMPANYS_SUCCESS] (state, payload) {
state.showLoader = false
state.companies = payload
},
[COMPANY_BY_ID] (state) {
state.showLoader = true
},
[COMPANY_BY_ID_SUCCESS] (state, payload) {
state.showLoader = false
state.company = payload
}
and here is my actions
allCompanies ({commit}) {
commit(ALL_COMPANYS)
axios.get(`${API_BASE}/companies`).then(response => {
commit(ALL_COMPANYS_SUCCESS, response.data)
})
},
companyById ({commit}, payload) {
commit(COMPANY_BY_ID)
axios.get(`${API_BASE}/companies/${payload}`).then(response => {
console.log(payload, response.data)
commit(COMPANY_BY_ID_SUCCESS, response.data)
})
My CompanyList looks like this
<template>
<div>
<div class="companies">
<div class="container">
<template v-for="company in companies" >
<company :company="company" :key="company.id"></company>
</template>
</div>
</div>
</div>
</template>
<script>
import Company from './Company.vue'
export default {
name: 'companies',
created () {
if (this.companies.length === 0) {
this.$store.dispatch('allCompanies')
}
},
computed: {
companies () {
return this.$store.getters.allCompanies
}
},
components: {
'company': Company
}
}
</script>
Imported Company looks like this
<template>
<v-ons-col style="width: 350px; float:left;">
<router-link :to="'/details/'+company.id" class="company-link">
<v-ons-card>
<img :src="company.imageURL" style="width:100% ;margin: 0 auto;display: block;">
<div class="title">
<b>{{company.companyName}}</b>
</div>
<div class="description">
{{company.description}}
</div>
<div class="content">
<!-- <template v-for="company in companies">
{{company}}
</template> -->
</div>
</v-ons-card>
</router-link>
</v-ons-col>
</template>
<script>
export default {
name: 'company',
props: ['company']
}
</script>
So when i click on one of these "companies" its suppose to get it by id and show the details however in the getters this return getters.allCompanies.filter(c => c._id === id)[0] returns undefined, when i refresh the page then it gets the correct company and displays it, what is going on please help. If you need more info please ask

how to unittest a vuejs component update with methods using promise

I'm testing a Vue component with Jest. This is my working directory :
Here is my component Subscription.vue :
<template>
<div id="page">
<page-title icon="fa-list-alt">
<translate slot="title">Subscription</translate>
<translate slot="comment">Consult the detail of your subscription</translate>
</page-title>
<panel v-if="error">
<span slot="title">
<icon icon="fa-exclamation-triangle"></icon>
<translate>Error</translate>
</span>
{{ error }}
</panel>
<panel v-else-if="subscription_dict">
<span slot="title">{{ _subscription_address }}</span>
<div class="row" v-if="subscription_dict.product.hsi">
<div class="col-xs-12">
<subscription-hsi-panel
:hsi="subscription_dict.product.hsi"
:business="context.business">
</subscription-hsi-panel>
</div>
</div>
<div class="row" v-if="subscription_dict.product.itv">
<div class="col-xs-12">
<subscription-itv-panel
:itv="subscription_dict.product.itv">
</subscription-itv-panel>
</div>
</div>
<div class="row" v-if="subscription_dict.product.voip">
<div class="col-xs-6">
<panel icon="icon-voip">
<translate slot="title">Phone</translate>
telefon products
</panel>
</div>
</div>
</panel>
<div v-else class="text-center">
<i class="fa fa-spinner fa-pulse fa-3x fa-fw"></i>
</div>
<span v-if="subscription_dict"><b>subscription_dict.product.voip : </b>{{ subscription_dict.product.voip }}</br></span>
<span v-if="subscription_dict"><b>subscription_dict.product : </b>{{ subscription_dict.product }}</br></span>
</div>
</template>
<script>
import PageTitle from '../../core/components/PageTitle.vue'
import SubscriptionHsiPanel from './SubscriptionHsiPanel.vue'
import SubscriptionItvPanel from './SubscriptionItvPanel.vue'
import Panel from '../../core/components/Panel.vue'
import Icon from '../../core/components/Icon.vue'
import api from '../../core/api.js'
export default {
data() {
return {
subscription_dict: false,
error: false
}
},
props: ['requests_url', 'context'],
computed: {
_subscription_address() {
var sub_address = this.subscription_dict.subscription_address
return sub_address + ' - ' + this.subscription_dict.package.join(' - ')
}
},
created() {
this.get_subscription()
this.translate()
},
methods: {
get_subscription() {
let self = this
api.get_subscription(self.requests_url.subscriptions_request)
.then(function(response) {
self.subscription_dict = response
})
.catch(function(error) {
if(error) {
self.error = error
} else {
self.error = self.$gettext(
'We were not able to retrieve your subscription information!')
}
})
},
translate() {
this.$gettext('Bridge hsi')
this.$gettext('Bridge voip_biz')
this.$gettext('Router')
}
},
components: {
PageTitle,
Panel,
Icon,
SubscriptionHsiPanel,
SubscriptionItvPanel
}
}
</script>
<style lang="sass">
#import "../../core/css/tooltip"
.table
table-layout: fixed
> tbody > tr >
th
width: 33%
td
vertical-align: middle
</style>
And here is my test subscription.js:
import Vue from 'vue'
import translations from 'src/translations.json'
import GetTextPlugin from 'vue-gettext'
import VTooltip from 'v-tooltip'
import Subscription from 'src/subscription/components/Subscription'
jest.mock('../../core/api');
Vue.use(GetTextPlugin, {
availableLanguages: {
en: 'English',
fr: 'Français',
de: 'Deutsch',
},
defaultLanguage: 'fr',
translations: translations
})
Vue.use(VTooltip)
it('render when error', (done) => {
const renderer = require('vue-server-renderer').createRenderer()
const vm = new Vue({
el: document.createElement('div'),
render: h => h(Subscription, {
props: {
requests_url: {
subscriptions_request: 'error_empty_promise'
}
}
})
})
renderer.renderToString(vm, (err, str) => {
setImmediate(() => {
expect(str).toMatchSnapshot()
done()
})
})
})
The component's method get_subscription() use my API function get_subscription:
import axios from 'axios'
export default {
get_subscription(url) {
return axios.get(url, {
credentials: 'same-origin'
})
.then(function(response){
if(response.data.error){
return Promise.reject(response.data.error)
}else{
return Promise.resolve(response.data)
}
})
.catch(function(error) {
return Promise.reject(false)
})
}
}
For my test, I have mocked this function like this :
const promises_object = {
error_empty_promise: new Promise((resolve, reject) => {
process.nextTick(
() => reject(false)
)
})
}
export default {
get_subscription(url) {
return promises_object[url]
}
}
Now, in the test I render and compare against a snapshot. My issue is, I can't find a way to wait that the promise of get_subscription is reject before making the comparison.
The result is that my snapshot reflect the state of the component before the update of the DOM, which is done after the asynchronous call on the API.
Is there a way to tell jest to wait until the Promise is reject before calling expect(str).toMatchSnapshot() ?
Jest docs say
it expects the return value to be a Promise that is going to be
resolved.
You have a promise that is going to be rejected, so you need a Promise that resolves when your promise is rejected.
isRejected(rejectingPromise, someExpects) {
return new Promise((resolve) => {
rejectingPromise.then(
() => null, // if it resolves, fail
(err) => { // if it rejects, resolve
// Maybe do someExpects() here
resolve();
}
});
}