Error on saving to local storage for Vue.js - javascript

In my Vue JS app I am trying to save data I get back from a rest api into a list component but I am getting the following error:
Error in mounted hook: "SyntaxError: Unexpected token u in JSON at position 0"
I can't see what could be causing this?
Component:
<template>
<div class="mb-5 mt-5 container">
<div class="list-group">
<a href="#" class="list-group-item list-group-item-action active">
My List
<span class="badge badge-light">{{List.length}}</span>
</a>
<a
href="#"
class="list-group-item list-group-item-action"
v-for="(result, index) in List"
:key="index"
>
{{result.collectionName}}
<br>
<b>{{result.artistName}}</b>
<br>
<div class="row">
<div class="col-md-6">
<button
class="btn btn-success btn-sm btn-block"
v-on:click="removeElement(index)"
>Remove</button>
</div>
<div class="col-md-6">
<button
class="btn btn-info btn-sm btn-block"
#click="toiTunesAlbum(result)"
>View on iTunes</button>
</div>
</div>
</a>
</div>
<button class="btn btn-info mt-5 btn-lg btn-block" #click="saveList()">Save</button>
</div>
</template>
<script>
export default {
name: "List",
props: ["List"],
mounted() {
if(localStorage.result) this.result = JSON.parse(this.result);
},
methods: {
saveList() {
localStorage.result = this.result;
localStorage.setItem('result', JSON.stringify(this.result));
/* eslint-disable no-console */
console.log(this.result + 'List Saved');
/* eslint-disable no-console */
},
removeElement: function(index) {
this.List.splice(index, 1);
},
resizeArtworkUrl(result) {
return result.artworkUrl100.replace("100x100", "160x160");
},
toiTunesAlbum(result) {
window.open(result.collectionViewUrl, "_blank");
}
}
};
</script>
<style scoped>
.album-cover {
width: 100%;
height: auto;
background-color: aqua;
}
.album-container {
height: 350px;
}
</style>
Any ideas on how I can save to local storage the rest api result that would be great. Should I be using the List array or result?

I think your problem is that you are only initializing this.result if localStorage.result has a value. If localStorage.result doesn’t have a value the first time you call saveList this.result is still undefined. Try setting a default value or skip trying to save if the value is still undefined. I am also not sure of the relationship between this.List and this.result, since List seems to be a list of results?

Related

Vuejs 3 - Problem with data function inside a component

I have a list of projects which I get from an api. They get forwarded one by one to this component:
<template>
<button id="project-button" #contextmenu="handler($event)" class="btn btn-outline-secondary mt-1 ms-1" data-bs-toggle="collapse" :data-bs-target="`#${project.Name + project.ID}`">{{project.Name}}</button>
<div :id="`${project.Name + project.ID}`" class="collapse">
<button #click="setProjectId" id="add-object-button" class="btn btn-outline-secondary mt-1 ms-1" data-bs-toggle="modal" data-bs-target="#createObject">
<b>Add Data Object</b>
<i id="plus-icon" class="fas fa-plus"></i>
</button>
<DataObjects :dataObjects="dataobjects"/>
</div>
<CreateObjectModal #add-object="addObject" :projectId="projectId"/>
</template>
<script>
import DataObjects from './DataObjects'
import CreateObjectModal from './CreateObjectModal'
export default {
name: 'Project',
data() {
return {
dataobjects: [],
projectId: 0,
}
},
props: {
project: Object,
},
components: {
DataObjects,
CreateObjectModal
},
methods: {
setProjectId() {
console.log(this.project.ID)
this.projectId = this.project.ID
console.log(this.projectId)
},
async addObject(objectName) {
console.log(objectName)
console.log("After adding object: " + this.projectId)
console.log("Event arrived")
},
Inside the data function I have an object which has the attribute projectId. When I click on the Add Data Object button a modal opens where the user can type in the name of the data object. The modal opens through the data-bs-toggle and data-bs-target attributes. Now in my click event for this button I can get the individual id of a project depending on which button I click. But the problem here is that I will need the specific id of a project inside my addObject function. The addObject function gets called when I click 'Ok' in my modal after the user types in a name. The logs show clearly that if I click on a button the different id gets set correctly but when I click 'Ok' from the modal and the AddObject function gets called then the id remains 1 which is the first id of the projects
I dont know if I am complicating things but I tried to send the id to my modal component which didnt work. The idea was to send it back to the addObject function via $emit. I am kinda stuck with this problem and hope someone can help me. Here is the Modal Component:
<template>
<div>
<div class="modal fade" id="createObject" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">New Data Object</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="form-floating mb-3">
<input v-model="objectName" class="form-control" id="input">
<label for="input">Object Name:</label>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
<Button data-bs-dismiss="modal" #click="createObject" :text="'Save'"/>
<p>{{projectId}}</p>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import Button from './Button'
export default {
name: 'CreateObjectModal',
data() {
return {
objectName: '',
}
},
props:{
projectId: Number
},
methods: {
createObject() {
this.$emit('add-object', this.objectName)
}
},
components: {
Button
},
emits: ['add-object']
}
</script>

Vue Object not updating with axios POST request

<template>
<div>
<div class="dropdown d-inline-block ml-2">
<button type="button" class="btn btn-sm btn-dual" id="page-header-notifications-dropdown" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-on:click="MarkAllAsRead($event)">
<i class="si si-bell"></i>
<span class="badge badge-danger badge-pill" v-if="CountUnread(notifications)">{{ CountUnread(notifications) }}</span>
</button>
<div class="dropdown-menu dropdown-menu-lg dropdown-menu-right p-0 border-1 font-size-sm" aria-labelledby="page-header-notifications-dropdown">
<div class="p-2 bg-primary text-center">
<h5 class="dropdown-header text-uppercase text-white">Notifications</h5>
</div>
<ul class="nav-items mb-0" style="overflow-y:scroll; height:500px;">
<!-- notifications should have been updated by axios but they never appear -->
<li v-for="notification of notifications">
<div v-if="notification.length">
<notification v-bind:notification="notification[0]"></notification>
</div>
<div v-else>
<notification v-bind:notification="notification"></notification>
</div>
</li>
<div class="p-2 border-top" v-if="hasMore">
<a class="btn btn-sm btn-light btn-block text-center" href="javascript:void(0)" v-on:click="loadMoreNotifications($event)">
<i class="fa fa-fw fa-arrow-down mr-1"></i> Load More..
</a>
</div>
</ul>
</div>
</div>
</div>
</template>
<script>
export default {
props:[
'user'
],
data: function() {
return{
notificationsIndex:0,
notifications:[],
hasMore:1
}
},
methods: {
loadNotifications: function(){
var data = {
index: this.notificationsIndex
};
axios.post('/notifications/getAll',data).then( response => {
if(response.data){
if(this.notifications.length==0){
this.notifications=response.data;
console.log(this.notifications);//data fetched here successfully
}
else{
this.notifications.push.apply(this.notifications,response.data);
}
}
else{
this.hasMore = 0;
}
});
console.log(this.notifications);//couldn't find data, just observer object
},
loadMoreNotifications: function(event){
event.preventDefault();
this.notificationsIndex++;
this.loadNotifications();
},
CountUnread: function(notifications){
var count=0;
for(var i=0;i<notifications.length;i++){
if(notifications[i].read_at == null){
count++;
}
}
return count;
},
HasNotification: function(notification){
list = this.notifications.filter(item => {
return item.id == notification.id;
});
return list.length
}
},
created: function(){
this.loadNotifications();
window.Echo.private('App.User.' + this.user.id)
.notification((notification) => {
if(this.HasNotification){
this.notifications.unshift(notification);
}
});
}
}
</script>
And notifications object has nothing in html template as well.
Note: This same code works fine on all pages where I have this only instance of vue. On another page (localhost/horizon)(using laravel horizon package and updating it's layout.blade.php ) where there are two instances of vue, one for notifications and other of Laravel/Horizon, this request returns nothing.
The problem here as #cbaconnier mentioned is that you're using an async function and not waiting for the result.
The console.log() that's failing is due to the fact that it's executed before the post request is retrieved (that's why you're receiving an observer).
The same happens in your created method.
try taking all the code that's dependant on the received notifications into the .then() callback of the axios call.
The problem here was, I was using a bootstrap shipped with app theme I was using and when I installed Horizon and updated Horizon layout.blade.php file according to my customisations, it broke this piece of code because it was using some other bootstrap version and once I removed the bootstrap from the horizon require('bootstrap'); in one of it's app.js file, everything worked fine.

Axios making DELETE and GET in the wrong order

I'm working on a small project in vue.js connected to a lumen API (working).
I currently have a list of students ('Etudiants') in which I can click in the list to select one, and delete it via a button in a toolbar.
When a student is deleted I'm reloading the student list (since it's not up to date anymore), therefore I'm doing 2 api calls via axios.
DELETE http://www.url.com/etudiants (param: idEtudiant)
GET http://www.url.com/etudiants (param: page)
The problem is that my API calls are not done in the right order, as shown here on a screenshot of the calls (with watterfall):
This problem involves 3 vue files.
'Etudiants.vue' and its 2 childs: 'ListeEtudiants.vue' (student list) and 'BarreOutilsEtudiant.vue' (toolbar)
This simple image shows the hierarchy of the 3 files and the order of which everything should execute.
In my case (when it's not working) the action number 3, the axios DELETE, happens in last.
Here are the contents of my files:
Etudiants.vue:
<template>
<div id="etudiants" class="container-fluid h-100">
<div class="row">
<div class="col-3 borderR">
<ListeEtudiants ref="list" #idEtudiantChanged="updateIdEtudiant"/>
</div>
<div class="col-9 bg-light">
<BarreOutilsEtudiant v-if="idEtudiant != null" :idEtudiant="idEtudiant" #delEtudiant="delEtudiant"/>
<InfosEtudiant v-if="idEtudiant != null" :idEtudiant="idEtudiant"/>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
import ListeEtudiants from '#/components/ListeEtudiants.vue'
import InfosEtudiant from '#/components/InfosEtudiant.vue'
import BarreOutilsEtudiant from '#/components/BarreOutilsEtudiant.vue'
export default {
components: {
ListeEtudiants,
InfosEtudiant,
BarreOutilsEtudiant
},
data: function(){
return {
idEtudiant: null
}
},
methods:{
updateIdEtudiant(idEtudiant){
this.idEtudiant=idEtudiant;
},
delEtudiant(){
axios
.delete('http://82ab2617.ngrok.io/etudiants', {params: {"idEtudiant" :this.idEtudiant}})
.then(this.$refs.list.loadList())
.catch(error => console.log(error));
}
}
}
</script>
ListeEtudiants.vue:
<template>
<div id="ListeEtudiants">
<div class="row bg-light">
<!-- Trigger Modal Ajout Etudiant -->
<button class="btn btn-light w-100" data-toggle="modal" data-target="#addModal">
<font-awesome-icon icon="plus" size="1x"/>
<span> Ajouter un Etudiant</span>
</button>
</div>
<ul v-if="etudiants != null" id="list" class="row flex-nowrap list-group list-group-flush pr-0">
<button v-for="etudiant in etudiants.data" v-on:click='$emit("idEtudiantChanged",etudiant.idEtudiant)' class="btn btn-light text-left list-group-item pl-5 py-1">{{ etudiant.nom }} {{ etudiant.prenom }}</button>
</ul>
<ul v-else class="row flex-nowrap list-group list-group-flush pr-0">
</ul>
<div class="row bg-light">
<button class="btn btn-light col-3" v-on:click="page -= 1" :disabled="page === 1 || disabled"><font-awesome-icon icon="chevron-left" size="1x"/></button>
<div class="align-middle col-6 my-auto">{{ page }} / {{ maxPage }}</div>
<button class="btn btn-light col-3" v-on:click="page += 1" :disabled="page === maxPage || disabled"><font-awesome-icon icon="chevron-right" size="1x"/></button>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: "ListeEtudiants",
data: function(){
return {
etudiants: null,
maxPage:1,
disabled:false,
page:1,
}
},
methods: {
parseAndDisplay: function(data){
this.etudiants = data;
this.maxPage = data.last_page;
this.page = data.current_page;
this.disabled = false;
},
loadList: function(){
this.disabled = true;
this.etudiants = null;
axios
.get('http://82ab2617.ngrok.io/etudiants', {params: {page:this.page}})
.then(response =>this.parseAndDisplay(response.data))
.catch(error => console.log(error));
}
},
watch: {
'page': function(newVal, oldVal){
if((newVal === 0 && oldVal === 1) || (newVal === this.maxPage+1 && oldVal === this.maxPage)){
this.page = oldVal;
}else{
if(oldVal !== 0 && oldVal !== this.maxPage+1) {
this.loadList();
}
}
}
}
,
mounted() {
this.loadList();
}
}
</script>
BarreOutilsEtudiant.vue:
<template>
<div class="row p-2 navbar-expand navbar-info bg-info">
<button class="btn btn-info mr-5" type="button"><font-awesome-icon icon="download" size="1x"/> Télécharger le Bulletin</button>
<button class="btn btn-info ml-auto" type="button"><font-awesome-icon icon="user-edit" size="1x"/></button>
<button class="btn btn-danger ml-4" v-on:click="$emit('delEtudiant')" type="button"><font-awesome-icon icon="trash-alt" size="1x"/></button>
</div>
</template>
<script>
export default {
name: "BarreOutilsEtudiant"
}
</script>
<style scoped>
</style>
Thank you very much for helping me.
I believe the problem is here:
.then(this.$refs.list.loadList())
That will call loadList immediately and pass the value it returns to then, which isn't what you want.
Instead it should be something like this, wrapping it in a function:
.then(() => this.$refs.list.loadList())

*ngFor in angular 5 bootstrap modal not working

Need some expert help to show api-answer data in a bootstrap modal.(the other thread about this in SO did not answer this question)
I have a .ts file that looks like below.
NgbdModal1Content and NgbdModalStacked are the ones I'm having trouble with. Ignore NgbdModal2Content & NgbdModal3Content and their components.
Also, I added a sleep part to make sure the api answer had come back and populated
getResultatFromApi3: string [] = [];
getResultatFromApi4: Observable<Station> [];
Before the modal is rendered. The getResultatFromApiX are console logged before modal is rendered.
import { Component, Injectable } from '#angular/core';
import { NgbActiveModal, NgbModal } from '#ng-bootstrap/ng-bootstrap';
import { GetApiService } from './get-api.service';
import { Observable } from 'rxjs/Observable';
import { Station } from './station';
import { Station2 } from './station2';
#Component({
selector: 'app-modal-stacked',
templateUrl: './modal-stacked2.html',
})
// tslint:disable-next-line:component-class-suffix
export class NgbdModal1Content {
constructor(private modalService: NgbModal, public activeModal: NgbActiveModal) {}
open() {
this.modalService.open(NgbdModal2Content, {
size: 'lg'
});
}
open2() {
this.modalService.open(NgbdModal3Content, {
size: 'lg'
});
}
}
#Component({
template: `
<div class="modal-header">
<h4 class="modal-title">Teststation 1</h4>
<button type="button" class="close" aria-label="Close" (click)="activeModal.dismiss('Cross click')">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<p>Kör test här!</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-dark" (click)="activeModal.close('Close click')">Close</button>
</div>
`
})
// tslint:disable-next-line:component-class-suffix
export class NgbdModal2Content {
constructor(public activeModal: NgbActiveModal) {}
}
#Component({
template: `
<div class="modal-header">
<h4 class="modal-title">Teststation 2</h4>
<button type="button" class="close" aria-label="Close" (click)="activeModal.dismiss('Cross click')">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<p>Kör test här!</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-dark" (click)="activeModal.close('Close click')">Close</button>
</div>
`
})
// tslint:disable-next-line:component-class-suffix
export class NgbdModal3Content {
constructor(public activeModal: NgbActiveModal) {}
}
#Component({
// tslint:disable-next-line:component-selector
selector: 'ngbd-modal-stacked',
templateUrl: './modal-stacked.html'
})
// tslint:disable-next-line:component-class-suffix
export class NgbdModalStacked {
constructor(private modalService: NgbModal, private _getApiService: GetApiService) {}
getResultatFromApi3: string [] = [];
getResultatFromApi4: Observable<Station> [];
getApiData: string [];
// Triggered when opening Modal (that contains two buttons for two other modals
open() {
this._getApiService.method3Call().subscribe(function(data) {
console.log('Test från Y-Tube-videon', data);
this.getResultatFromApi4 = data;
this.getResultatFromApi3 = data;
console.log(this.getResultatFromApi4);
console.log(this.getResultatFromApi3);
});
this.delay(5000).then(any => {
this.modalService.open(NgbdModal1Content);
console.log('du klickade på Teststationer');
});
}
async delay(ms: number) {
await new Promise(resolve => setTimeout(() => resolve(), ms)).then(() => console.log('fired'));
}
}
My api call comes back with information when I trigger Open() in NgbdModalStacked part and answer looks like this, from console log:
I moved the NgbdModal1Content hmtl part to a separate html file to make it easier. That html file looks like this:
<div class="modal-header">
<h4 class="modal-title">Teststationer</h4>
<button type="button" class="close" aria-label="Close" (click)="activeModal.dismiss('Cross click')">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<p><button class="btn btn-lg btn-outline-primary" (click)="open()">Teststation 1 {{getResultatFromApi4}}</button></p>
<p><button class="btn btn-lg btn-outline-primary" (click)="open2()">Teststation 2</button></p>
<p>hej2<p>
<ul *ngFor="let data of getResultatFromApi3">
<li>Reported: {{data.Name}} <span>Mer info</span></li>
</ul>
<table>
<tr *ngFor="let data of getResultatFromApi3">
<td>{{data.Name}}</td>
</tr>
</table>
<ul>
<li *ngFor="let data of getResultatFromApi4">
{{data.Name}}
</li>
</ul>
<ul>
<li *ngFor="let data of getResultatFromApi4">
{{data.Name}}
</li>
</ul>
<p>hejigen2<p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-dark" (click)="activeModal.close('Close click')">Close</button>
</div>
But it doesn't output the data (see pic below) and I can't understand what I'm doing wrong? Tried data.Name and data.name, have both string and object array but api answer is not displayed but the other things in p are displayed.
HOW DO I MAKE THE DATA.NAME SHOW?
Thank you
public getResultatFromApi10 = [];
this._getApiService.method3Call().subscribe(
data => data.forEach(item => {this.getResultatFromApi10.push(item); })
);
This code made it work for me. The other subscribe would output info in the console.log but not carry any data from .ts to the html-part wheras the push(item) seem to have done the trick.
Hope this can help somebody else who might run into the same problem.

Array Splice always delete an item from last?

I am facing a problem in deleting item from an array. Array splice supposed to work but its not working like I want. Its always delete the item from last. I am using Vue.js . I am pushing item dynamically to an array. But after click remove its delete from the last. why I am facing this. I am attaching the codes.
<template>
<div>
<span class="badge badge-pill mb-10 px-10 py-5 btn-add" :class="btnClass" #click="addBtn"><i class="fa fa-plus mr-5"></i>Button</span>
<div class="block-content block-content-full block-content-sm bg-body-light font-size-sm" v-if="buttons.length > 0">
<div v-for="(item, index) in buttons">
<div class="field-button">
<div class="delete_btn"><i #click="remove(index)" class="fa fa-trash-o"></i></div>
<flow-button v-model="item.title" :showLabel="false" className="btn btn-block min-width-125 mb-10 btn-border" mainWrapperClass="mb-0" wrapperClass="pt-0" placeholder="Button Title"></flow-button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import flowButton from '../assets/flow-button'
export default {
name: "textArea",
props:{
index : Number
},
data() {
return {
buttons : [],
btnClass : 'badge-primary',
}
}
components : {
flowButton
},
methods : {
addBtn () {
if(this.buttons.length >= 2) {
this.btnClass = 'btn-secondary'
}
if(this.buttons.length < 3) {
this.buttons.push({
title : ''
});
}
},
remove(index) {
this.buttons.splice(index, 1)
}
}
}
</script>
This must be because of your flow-button I have tried to replicate your error but endup to this code. I just replaced the flow-button with input and it works. Try the code below.
Use v-bind:key="index", When Vue is updating a list of elements rendered with v-for, by default it uses an “in-place patch” strategy. If the order of the data items has changed, instead of moving the DOM elements to match the order of the items, Vue will patch each element in-place and make sure it reflects what should be rendered at that particular index. This is similar to the behavior of track-by="$index"
You have missing comma between data and components, I remove the component here it won't cause any error now, and more tips don't mixed double quotes with single qoutes.
<template>
<div>
<span class="badge badge-pill mb-10 px-10 py-5 btn-add" :class="btnClass" #click="addBtn"><i class="fa fa-plus mr-5"></i>Button</span>
<div class="block-content block-content-full block-content-sm bg-body-light font-size-sm" v-if="buttons.length > 0">
<div v-for="(item, index) in buttons" v-bind:key="index">
<div class="field-button">
<div class="delete_btn"><i #click="remove(index)" class="fa fa-trash-o">sdfsdff</i></div>
<input type="text" v-model="item.title" :showLabel="false" className="btn btn-block min-width-125 mb-10 btn-border" mainWrapperClass="mb-0" wrapperClass="pt-0" placeholder="Button Title"/>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'textArea',
props: {
index: Number
},
data () {
return {
buttons: [],
btnClass: 'badge-primary'
}
},
methods: {
addBtn () {
if (this.buttons.length >= 2) {
this.btnClass = 'btn-secondary'
}
if (this.buttons.length < 3) {
this.buttons.push({
title: ''
})
}
},
remove (index) {
this.buttons.splice(index, 1)
}
}
}
</script>
I think that you may be facing a conflict with the index prop of your component. Try to use a different name for the index of your v-for loop:
<div v-for="(item, ind) in buttons">
<div class="field-button">
<div class="delete_btn"><i #click="remove(ind)" class="fa fa-trash-o"></i></div>
<flow-button v-model="item.title" :showLabel="false" className="btn btn-block min-width-125 mb-10 btn-border" mainWrapperClass="mb-0" wrapperClass="pt-0" placeholder="Button Title"></flow-button>
</div>
</div>
Try this. Removing an item correctly using this.
<div v-for="(item, ind) in buttons" :key="JSON.stringify(item)">

Categories

Resources