My application is set up to fetch data from a rest api, and it consists of two key components:
1 - (main) Display the results 2 - (list) To display a list of selected items from the main results
The feature I am trying to implement involves adding a save button to the list(2) component. This button will allow users to save a selected list of items from the api. However, I encountered an error which states:
Error in mounted hook: "SyntaxError: Unexpected token u in JSON at position 0"
List 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="toAlbum(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(localStorage.result.length + 'List Saved');
/* eslint-disable no-console */
},
removeElement: function(index) {
this.List.splice(index, 1);
},
resizeArtworkUrl(result) {
return result.artworkUrl100.replace("100x100", "160x160");
},
toiAlbum(result) {
window.open(result.collectionViewUrl, "_blank");
}
}
};
</script>
<style scoped>
.album-cover {
width: 100%;
height: auto;
background-color: aqua;
}
.album-container {
height: 350px;
}
</style>
Main component
<template>
<div class="container search">
<div class="row">
<div class="col-md-8">
<div class="jumbotron mt-5" style="clear:both">
<h1 class="display-4">{{title}}</h1>
<p class="lead">{{intro}}</p>
<hr class="my-4">
<p v-if="validated" :class="errorTextClass">Enter a valid search term</p>
<button
type="button"
class="btn btn-primary btn-lg mb-3"
v-on:click="refreshPage"
v-if="result.length > 1"
>
<font-awesome-icon icon="redo"/> Start again
</button>
<input
class="form-control form-control-lg mb-3"
type="search"
placeholder="Search"
aria-label="Search"
v-model="search"
required
autocomplete="off"
id="search"
>
<div v-for="(result, index) in result" :key="index">
<div class="media mb-4">
<img
:src="resizeArtworkUrl(result)"
alt="Album Cover"
class="album-cover align-self-start mr-3"
>
<div class="media-body">
<h4 class="mt-0">
<button
type="button"
class="btn btn-primary btn-lg mb-3 float-right"
v-on:click="addItem(result)"
:disabled="result.disableButton"
>
<font-awesome-icon icon="plus"/>
</button>
<b>{{result.collectionName}}</b>
</h4>
<h6 class="mt-0">{{result.artistName}}</h6>
<p class="mt-0">{{result.primaryGenreName}}</p>
</div>
</div>
</div>
<div :class="loadingClass" v-if="loading"></div>
<button
class="btn btn-success btn-lg btn-block mb-3"
type="submit"
v-on:click="getData"
v-if="result.length < 1"
>
<font-awesome-icon icon="search"/>Search
</button>
</div>
</div>
<div class="col-md-4">
<List :List="List"/>
</div>
</div>
<!-- <div class='div' v-bind:class="[isActive ? 'red' : 'blue']" @click="toggleClass()"></div> -->
</div>
</template>
<script>
import List from "../components/myList.vue";
export default {
name: "Hero",
components: {
List
},
data: function() {
return {
title: "Simple Search",
isActive: true,
intro: "This is a simple hero unit, a simple jumbotron-style.",
subintro:
"It uses utility classes for typography and spacing to space content out.",
result: [],
errors: [],
List: [],
search: "",
loading: "",
message: false,
isValidationAllowed: false,
loadingClass: "loading",
errorTextClass: "error-text",
disableButton: false
};
},
watch: {
search: function(val) {
if (!val) {
this.result = [];
}
}
},
computed: {
validated() {
return this.isValidationAllowed && !this.search;
},
isDisabled: function() {
return !this.terms;
}
},
methods: {
// methods removed for brevity
}
};
</script>
<style>
.loading {
background-image: url("../assets/Rolling-1s-42px.gif");
background-repeat: no-repeat;
height: 50px;
width: 50px;
margin: 15px;
margin-left: auto;
margin-right: auto;
}
.error-text {
color: red;
}
.media {
text-align: left;
}
.album-cover {
width: 80px;
height: auto;
}
.red {
background: red;
}
.blue {
background: blue;
}
.div {
width: 100px;
height: 100px;
display: inline-block;
border: 1px solid black;
}
</style>
If you have any insights or suggestions regarding my setup, especially the use of local storage in the list component instead of the main component, please let me know. Your feedback is highly appreciated!