Vue: How pass the data to another component (with database) - javascript

I'm trying to get a clicked button id from MovieTable.vue to WatchedForm.vue component. WatchedForm.vue component updates the data in the database based on the given id. So this Movie_id is the id obtained from the database. I've already tried using props, but I didn't get it to work. Please help! I'm losing my mind..
App.vue:
<template>
<div class="container p-5">
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#movieModal">
Add Movie
</button>
<movie-form #add:movie="addMovie" />
<movie-table
:movies="movies"
#delete:movie="deleteMovie"
#edit:movie="WatchedMovie"
#edit2:movie="unWatchedMovie"
/>
</div>
<watched-form
#edit:movie="watchedMovie"
#edit2:movie="unWatchedMovie"
/>
</template>
<script>
//importing bootstrap 5
import "bootstrap/dist/css/bootstrap.min.css";
import "bootstrap/dist/js/bootstrap.min.js";
import MovieTable from '#/components/MovieTable.vue';
import MovieForm from '#/components/MovieForm.vue';
import WatchedForm from '#/components/WatchedForm.vue';
export default {
components: {
MovieTable,
MovieForm,
WatchedForm
},
data() {
return {
movies: {},
}
},
mounted() {
this.getMovies()
},
methods: {
async getMovies() {
try {
const response = await fetch('http://localhost:8081/api/movies')
const data = await response.json()
this.movies = data
} catch (error) {
console.error(error)
}
},
async addMovie(movie) {
try {
const response = await fetch('http://localhost:8081/api/addMovie', {
method: 'POST',
body: JSON.stringify(movie),
headers: {"Content-type": "application/json; charset=UTF-8"}
})
const data = await response.json()
this.movies = [...this.movies, data]
} catch (error) {
console.error(error)
}
},
async deleteMovie(Movie_id) {
try {
await fetch(`http://localhost:8081/api/delete/${Movie_id}`, {
method: 'DELETE'
})
this.movies = this.movies.filter(movie => movie.Movie_id !== Movie_id)
} catch (error) {
console.error(error)
}
},
async watchedMovie(Movie_id, updatedMovie) {
try {
const response = await fetch(`http://localhost:8081/api/movies/watched/${Movie_id}`, {
method: 'PUT',
body: JSON.stringify(updatedMovie),
headers: { "Content-type": "application/json" }
})
const data = await response.json()
this.movies = this.movies.map(movie => movie.Movie_id === Movie_id ? data : movie)
} catch (error) {
console.error(error)
}
},
async unWatchedMovie(Movie_id, updatedMovie) {
try {
const response = await fetch(`http://localhost:8081/api/movies/unwatched?id=${Movie_id}`, {
method: 'PUT',
body: JSON.stringify(updatedMovie),
headers: { "Content-type": "application/json" }
})
const data = await response.json()
this.movies = this.movies.map(movie => movie.Movie_id === Movie_id ? data : movie)
this.movies = this.getMovies();
} catch (error) {
console.error(error)
}
}
}
}
</script>
MovieTable.vue:
<template>
<div id="movie-table">
<p v-if="movies.length < 1" class="empty-table">No movies</p>
<table v-else>
<thead>
<tr>
<th>Name</th>
<th>Genre</th>
<th>Duration</th>
<th>Rating</th>
<th>Watched?</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr :key="movie.Movie_id" v-for="movie in movies " :id="'form' + movie.Movie_id">
<td>{{movie.Name}}</td>
<td>{{movie.Genre}}</td>
<td>{{movie.Duration}}</td>
<td>{{movie.Rating}}</td>
<td>
<span v-if="movie.is_watched">Yes</span>
<span v-else>No</span>
</td>
<td>
<button type="button" class="btn btn-primary" #click="unWatchedMovie(movie)" v-if="movie.is_watched === 1">
Unwatched
</button>
<!-- THIS BUTTON ID I WANT TO GET -->
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#watchedModal" #click="watchedMovie( movie)" v-else>
Watched
</button>
<button class="btn btn-primary" #click="$emit('delete:movie', movie.Movie_id)">Delete</button>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default {
name: 'movie-table',
props: {
movies: Array
},
data() {
return {
editing: null,
}
},
methods: {
watchedMovie(movie) {
if (movie.Name === '' || movie.Genre === '' || movie.Duration === '' || movie.is_watched === '') return
this.$emit('edit:movie', movie.Movie_id, movie)
this.editing = null
},
unWatchedMovie(movie) {
if (movie.Name === '' || movie.Genre === '' || movie.Duration === '' || movie.is_watched === '') return
this.$emit('edit2:movie', movie.Movie_id, movie)
this.editing = null
},
handler(id) {
console.log(id);
}
}
}
And WatchedForm.vue:
<template>
<div id="watched-form">
<div class="modal fade" id="watchedModal" tabindex="-1" aria-labelledby="watchedModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title text-danger" id="watchedModalLabel">Add new view</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form #submit.prevent="handleSubmit">
<!-- TRYING THERE TO SHOW ID FROM MOVIETABLE -->
<div id="watched-form1">{{movie.movie_id}}</div>
<div class="mb-3">
<label class="form-label">Place</label>
<input ref="first"
type="text"
v-model="movie.Place"
#focus="clearStatus"
#keypress="clearStatus"
class="form-control"
/>
</div>
<div class="mb-3">
<label class="form-label">Date</label>
<input
type="Date"
v-model="movie.Date"
#focus="clearStatus"
class="form-control"
/>
</div>
<div class="mb-3">
<label class="form-label">Rating 1-5</label>
<input
type="number"
min="1"
max="5"
:class="{ 'has-error': submitting && invalidRating}" class="form-control"
v-model="movie.Rating"
#focus="clearStatus"
/>
</div>
<div class="mb-3">
<label class="form-label">Comments</label>
<textarea class="form-control" v-model="movie.Comments" #focus="clearStatus"></textarea>
</div>
<div class="mb-3">
<label class="form-label">Watched?</label>
<input
type="checkbox"
v-model="movie.is_watched.checked"
#focus="clearStatus"
checked
disabled
/>
</div>
<p v-if="error && submitting" class="error-message">❗Please fill out rating required field</p>
<p v-if="success" class="success-message">✅ Movie successfully added</p>
<button type="submit" class="btn btn-primary">Save</button>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-warning" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'watched-form',
data() {
return {
submitting: false,
error: false,
success: false,
movie: {
Name: '',
Genre: '',
Duration: '',
Rating: '',
is_watched: '',
}
}
},
methods: {
handleSubmit() {
this.clearStatus()
this.submitting = true
if (this.invalidRating ) {
this.error = true
return
}
this.$emit('edit:movie', this.movie)
this.$refs.first.focus()
this.movie = {
Name: '',
Genre: '',
Duration: '',
Rating: '',
is_watched: ''
}
this.error = false
this.success = true
this.submitting = false
},
clearStatus() {
this.success = false
this.error = false
},
},
computed: {
invalidRating() {
return this.movie.Rating === ''
}
},
}
</script>

You just need to do the below changes in App.vue, when you emit the movie id from MovieTable.vue, you got to send it as props to WatchedForm.vue not as a variable instead as a computed property. Check the below approach
<movie-table
:movies="movies"
#delete:movie="deleteMovie"
#edit:movie="WatchedMovie"
#edit2:movie="unWatchedMovie"
/>
</div>
<watched-form
:movieId="getMovieId"
/>
and add a variable called movieId inside data() like
data() {
return {
movieId: '',
....other variables
};
}
and in computed
computed: {
getMovieId() {
return this.movieId;
}
}
and in your methods where you set the movieId that is emitted from MovieTable.vue such that the computed property runs dynamically
methods: {
async watchedMovie(Movie_id, updatedMovie) {
this.movieId = Movie_id; // setting the movie_id in localvariable so that the computed property sends the updated value to WatchedForm.vue
// ...Other lines of code
},
async unWatchedMovie(Movie_id, updatedMovie) {
this.movieId = Movie_id; // setting the movie_id in localvariable so that the computed property sends the updated value to WatchedForm.vue
// ...Other lines of code
}
}

Related

How do I watch the output of a function?

I have 2 buttons. One adds a movie to local storage, the other removes it from there. I made a function that basically switches the button. If the movie is added it shows "remove", if the movie's not been added it shows the button "add".
The function works but it doesn't know when the boolean changes so the button doesn't change. Someone explained that i should use watch property, but how am I supposed to watch an output of a function?
here is the code
<template>
<div>
<div class="card" v-for="movie in movies"
:key="movie.id">
{{movie.title}}
{{movie.release_date}}
<button v-show="!showButton(movie.id)" type="submit" #click="storeMovie(movie.id)" >
Aggiungi
</button>
<button v-show="showButton(movie.id)" type="submit" #click="removeMovie(movie.id)">
Rimuovi
</button>
</div>
<div class="card" v-for="favourite in watchlist"
:key="favourite.id">
{{favourite.title}}
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'HomeComp',
data () {
return {
movies: [],
watchlist: [],
movie: null,
}
},
mounted () {
axios
.get('https://api.themoviedb.org/3/movie/popular?api_key=###&language=it-IT&page=1&include_adult=false&region=IT')
.then(response => {
this.movies = response.data.results
// console.log(response.data.results)
})
.catch(error => {
console.log(error)
this.errored = true
})
.finally(() => this.loading = false)
},
watch: {
switchButton(oldValue, newValue) {
if (oldValue != newValue) {
this.showButton(id) = true;
} //made an attempt here
}
},
methods: {
storeMovie(id) {
const favouriteMovie = this.movies.find(movie => movie.id === id )
this.watchlist.push(favouriteMovie);
localStorage.setItem("watchlist", JSON.stringify(this.watchlist));
},
removeMovie(id) {
const removedMovie = this.watchlist.find(movie => movie.id === id )
const indexMovie = this.watchlist.indexOf(removedMovie);
if (indexMovie > -1) {
this.watchlist.splice(indexMovie, 1);
}
localStorage.setItem("watchlist", JSON.stringify(this.watchlist));
},
showButton(id) {
const favouriteMovie = this.watchlist.find(movie => movie.id === id )
if (favouriteMovie && favouriteMovie.length > 0) {
return true
} else{
return false
}
}
},
}
</script>
<style scoped lang="scss">
</style>
A better approach would be to store the state of a movie being stored or not in the watchlist directly on the movie object.
Then use a computed to get the watchlist from the movie list instead of using two different arrays.
<template>
<div>
<div class="card" v-for="movie in movies" :key="movie.id">
{{movie.title}}
{{movie.release_date}}
<button v-show="!movie.toWatch" type="submit" #click="storeMovie(movie.id)">
{{ movie.toWatch ? 'Rimuovi' : 'Aggiungi' }}
</button>
</div>
<div class="card" v-for="favourite in watchList" :key="favourite.id">
{{favourite.title}}
</div>
</div>
</template>
<script>
export default {
name: 'HomeComp',
data() {
return {
movies: [],
}
},
computed: {
// Get the watchList from the movies list
watchList() {
return this.movies.filter(movie => movie.toWatch)
}
},
watch: {
watchList(newWatchList) {
// Update the localStorage whenever the list changes
localStorage.setItem("watchlist", JSON.stringify(newWatchList));
}
},
mounted() {
// your axios call
},
methods: {
storeMovie(id) {
const favouriteMovie = this.movies.find(movie => movie.id === id)
if (favouriteMovie) {
// just reverse the boolean
favouriteMovie.toWatch = !favouriteMovie.toWatch
}
},
},
}
</script>

How to get rid of cloning / duplication of elements in vue?

I'm a beginner programmer and I create a spa like trello. Creates boards. In the board creates lists, they are displayed different with different id, but the list items are displayed with the same id and they are duplicated in each list. Sorry for my english:) Help me, please and tell in detail what the problem is.. Thank you very much
vuex file router.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
boards: JSON.parse(localStorage.getItem('boards') || '[]'),
lists: [],
items: []
// items: JSON.parse(localStorage.getItem('items') || '[]')
// lists: JSON.parse(localStorage.getItem('lists') || '[]')
},
mutations: {
addBoard(state, board) {
state.boards.push(board)
localStorage.setItem('boards', JSON.stringify(state.boards))
},
addList(state, list) {
state.lists.push(list)
// localStorage.setItem('lists', JSON.stringify(state.lists))
},
createItemListt(state, item) {
state.items.push(item)
// localStorage.setItem('items', JSON.stringify(state.items))
}
},
actions: {
addBoard({commit}, board) {
commit('addBoard', board)
},
addList({commit}, list) {
commit('addList', list)
},
createItemListt({commit}, item) {
commit('createItemListt', item)
}
},
getters: {
boards: s => s.boards,
taskById: s => id => s.boards.find(t => t.id === id),
lists: d => d.lists,
items: a => a.items
},
modules: {
}
})
the page on which the lists are created MyBoard.vue
<template>
<div>
<div class="wrapper">
<div class="row">
<h1>{{board.title}}</h1>
<div class="list " v-for="list in lists" :key="list.idList">
<div class="list__title">
<h3>{{list.titleList}}</h3>
</div>
<div class="list__card" v-for="item in items" :key="item.idItemList">
<span class="list__item">{{item.itemList}}</span>
<a class="btn-floating btn-tiny btn-check" tag="button">
<i class="material-icons">check</i>
</a>
</div>
<createItemList />
</div>
<createList />
</div>
</div>
</div>
</template>
<script>
export default {
computed: {
board() {
return this.$store.getters.taskById(+this.$route.params.id);
},
lists() {
return this.$store.getters.lists;
},
items() {
return this.$store.getters.items;
}
},
components: {
createList: () => import("../components/createList"),
createItemList: () => import("../components/createItemList")
}
};
</script>
CreateList.Vue
<template>
<div>
<div class="row">
<div class="new-list" v-show="isCreating">
<div class="list__title input-field">
<input
type="text"
required
id="list-title"
class="none validate"
tag="button"
autofocus
v-model="titleList"
v-on:keyup.enter="createList"
/>
<label for="list-title">Enter Title List</label>
</div>
<a class="btn-floating transparent btn-close" tag="button" #click="closeList">
<i class="material-icons">close</i>
</a>
</div>
<div class="create-list z-depth-2" v-show="!isCreating">
<p>Create list</p>
<a
class="btn-floating btn-large waves-effect waves-light deep-purple lighten-2 pulse"
tag="button"
#click="addList"
v-on:keyup.enter="addList"
>
<i class="material-icons">add</i>
</a>
</div>
</div>
</div>
</template>
<script>
export default {
data: () => ({
isCreating: false,
titleList: "",
idList: ""
}),
methods: {
addList() {
this.isCreating = true;
},
closeList() {
this.isCreating = false;
},
createList() {
if (this.titleList == "") {
return false;
}
const list = {
idList: Date.now(),
titleList: this.titleList
};
this.$store.dispatch("addList", list);
this.titleList = "";
this.isCreating = false;
console.log(list.titleList);
}
}
};
</script>
CreateItemList.vue
<template>
<div>
<div class="add-item">
<div class="textarea-item input-field" v-show="isAdding">
<input
type="text"
class="validate"
id="list-item"
v-model="itemList"
v-on:keyup.enter="createItemList"
autofocus
/>
<label for="list-item">Enter Item List</label>
</div>
<a class="waves-effect waves-light btn" v-show="!isAdding" #click="addCard">
<i class="material-icons right">add</i>Add Card
</a>
</div>
</div>
</template>
<script>
export default {
data: () => ({
isAdding: false,
itemList: "",
}),
methods: {
addCard() {
this.isAdding = true;
},
createItemList() {
if (this.itemList == "") {
return false;
}
const item = {
idItemList: Date.now(),
itemList: this.itemList
};
this.$store.dispatch("createItemListt", item);
this.itemList = "";
this.isAdding = false;
}
}
};
</script>
attach photo
Tried to go with the basic idea of the structure you laid out. I added:
id to all items, so they can be identifed
children to appropriate items, so you can keep track of what's in them
const store = new Vuex.Store({
state: {
tables: [
{ id: 1, children: ['1.1', '1.2'] },
{ id: 2, children: ['2.1'] }
],
lists: [
{ id: '1.1', children: ['1.1.1'] },
{ id: '1.2', children: ['1.2.1'] },
{ id: '2.1', children: ['2.1.1', '2.1.2'] },
],
cards: [
{ id: '1.1.1' },
{ id: '1.2.1' },
{ id: '2.1.1' },
{ id: '2.1.2' },
]
},
mutations: {
ADD_CARD(state, listId) {
const list = state.lists.find(e => e.id === listId)
const cards = state.cards
const card = { id: Date.now() }
cards.push( card )
list.children.push( card.id )
},
ADD_LIST(state, tableId) {
const table = state.tables.find(e => e.id === tableId)
const lists = state.lists
const list = { id: Date.now(), children: [] }
lists.push( list )
table.children.push( list.id )
},
ADD_TABLE(state) {
const tables = state.tables
const table = { id: Date.now(), children: [] }
tables.push( table )
},
TRY_MOVING_LIST(state) {
const table1 = state.tables.find(e => e.id === 1)
const table2 = state.tables.find(e => e.id === 2)
const item = table1.children.pop() // remove the last item
table2.children.push(item)
}
},
actions: {
addCard({ commit }, listId) {
commit('ADD_CARD', listId)
},
addList({ commit }, tableId) {
commit('ADD_LIST', tableId)
},
addTable({ commit }) {
commit('ADD_TABLE')
},
tryMovingList({ commit }) {
commit('TRY_MOVING_LIST')
}
},
getters: {
getTables: s => s.tables,
getListById: s => id => s.lists.find(e => e.id === id),
getCardById: s => id => s.cards.find(e => e.id === id),
}
})
Vue.component('CustomCard', {
props: ['card'],
template: `<div>
card ID: {{ card.id }}<br />
</div>`
})
Vue.component('CustomList', {
props: ['list'],
template: `<div>
list ID: {{ list.id }}<br />
<custom-card
v-for="item in list.children"
:key="item"
:card="getCard(item)"
/>
<button #click="addCard">ADD CARD +</button>
<hr />
</div>`,
methods: {
getCard(id) {
return this.$store.getters.getCardById(id)
},
addCard() {
this.$store.dispatch('addCard', this.list.id)
}
}
})
Vue.component('CustomTable', {
props: ['cTable'],
template: `<div>
table ID: {{ cTable.id }}<br />
<custom-list
v-for="item in cTable.children"
:key="item"
:list="getList(item)"
/>
<button #click="addList">ADD LIST +</button>
<hr />
</div>`,
methods: {
getList(id) {
return this.$store.getters.getListById(id)
},
addList(id) {
this.$store.dispatch('addList', this.cTable.id)
}
}
})
new Vue({
el: "#app",
store,
computed: {
tables() {
return this.$store.state.tables
}
},
methods: {
addTable() {
this.$store.dispatch('addTable')
},
tryMovingList() {
// this function will move the last list in table ID 1
// to the end of table ID 2's lists
// NOT FOOLPROOF - you should add error handling logic!
this.$store.dispatch('tryMovingList')
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/vuex"></script>
<div id="app">
<button #click="tryMovingList()">MOVE LIST</button><br />
<button #click="addTable()">ADD TABLE +</button>
<hr />
<custom-table v-for="item in tables" :key="'table-' + item.id" :c-table="item" />
</div>
With this setup you can change the hierarchy quite easily: just delete an ID from one Array of children and add it to another (e.g. remove '1.1' from table ID 1's children array and add it to table ID 2's children array - everything moved to table ID 2. tryMovingList() does exactly this - this method/action is not foolproof, just for you to try out moving a whole list)
There could be other patterns to solve this problem (like a real linked list data structure or the mediator pattern), but for smaller apps this is OK, I think (I would use it... :) ).
ONE PIECE OF ADVICE
If you want to store state in localStorage on mutations, don't do it by yourself - use Vuex's integrated subscribe mechanism: https://vuex.vuejs.org/api/#subscribe

vue js axios cannot read property 'data' of undefined

I am trying to get the json to display in cards or list after search. The search part seems to be working since I can see the call to the API with the text written in the search box in the console. but nothing is displaying and I get this error TypeError: Cannot read property 'data' of undefined. I don't think I am getting the json path correct.
app.vue
<template>
<div id="app">
<Header/>
<SearchForm v-on:search="search"/>
<SearchResults
v-if="results.length > 0"
v-bind:results="results"
v-bind:reformattedSearchString="reformattedSearchString"
/>
<Pagination
v-if="results.length > 0"
v-bind:prevPageToken="api.prevPageToken"
v-bind:nextPageToken="api.nextPageToken"
v-on:prev-page="prevPage"
v-on:next-page="nextPage"
/>
</div>
</template>
<script>
import Header from './components/layout/Header';
import SearchForm from './components/SearchForm';
import SearchResults from './components/SearchResults';
import Pagination from './components/Pagination';
import axios from 'axios';
export default {
name: 'app',
components: {
Header,
SearchForm,
SearchResults,
Pagination
},
data() {
return {
results: [],
reformattedSearchString: '',
api: {
baseUrl: 'https://geodeepdive.org/api/v1/articles?',
max: 10,
q: '',
prevPageToken: '',
nextPageToken: ''
}
};
},
methods: {
search(searchParams) {
this.reformattedSearchString = searchParams.join(' ');
this.api.q = searchParams.join('+');
const { baseUrl, q, max} = this.api;
const apiUrl = `${baseUrl}&term=${q}&title=${q}&max=${max}`;
this.getData(apiUrl);
},
prevPage() {
const { baseUrl, q, max, prevPageToken } = this.api;
const apiUrl = `${baseUrl}&term=${q}&title=${q}&max=${max}&pageToken=${prevPageToken}`;
this.getData(apiUrl);
},
nextPage() {
const { baseUrl, q, max,nextPageToken } = this.api;
const apiUrl = `${baseUrl}&term=${q}&title=${q}&max=${max}&pageToken=${nextPageToken}`;
this.getData(apiUrl);
},
getData(apiUrl) {
axios
.get(apiUrl)
.then(res => {
this.results = res.success.data;
this.api.prevPageToken = res.success.data.prevPageToken;
this.api.nextPageToken = res.success.data.nextPageToken;
})
.catch(error => console.log(error))
}
}
};
</script>
searchresults
<template>
<div class="container mb-3">
<div class="d-flex mb-3">
<div class="mr-auto">
<h3>Search Results for "{{ reformattedSearchString }}"</h3>
</div>
<div class="btn-group ml-auto" role="group">
<button
#click="changeDisplayMode('grid')"
type="button"
class="btn btn-outline-secondary"
v-bind:class="{ active: displayMode === 'grid' }"
>
<i class="fas fa-th"></i>
</button>
<button
#click="changeDisplayMode('list')"
type="button"
class="btn btn-outline-secondary"
v-bind:class="{ active: displayMode === 'list' }"
>
<i class="fas fa-list"></i>
</button>
</div>
</div>
<div class="card-columns" v-if="displayMode === 'grid'">
<div class="card" v-bind:key="result.link.url" v-for="result in results">
<ArticleGridItem v-bind:result="result"/>
</div>
</div>
<div v-else>
<div class="card mb-2" v-bind:key="result.link.url" v-for="result in results">
<ArticleListItem v-bind:result="result"/>
</div>
</div>
</div>
</template>
<script>
import ArticleListItem from './ArticleListItem';
import ArticleGridItem from './ArticleGridItem';
export default {
name: 'SearchResults',
components: {
ArticleListItem,
ArticleGridItem
},
data() {
return {
title: 'Search Results',
displayMode: 'grid'
};
},
methods: {
changeDisplayMode(displayMode) {
this.displayMode = displayMode;
}
},
props: ['results', 'reformattedSearchString']
};
</script>
json
{
success: {
v: 1,
data: [
{
type: "article",
_gddid: "5ea0b2b3998e17af826b7f42",
title: "The current COVID-19 wave will likely be mitigated in the second-line European countries",
volume: "",
journal: "Cold Spring Harbor Laboratory Press",
link: [
{
url: "https://www.medrxiv.org/content/10.1101/2020.04.17.20069179v1",
type: "publisher"
}
],
publisher: "bioRxiv",
author: [
{
name: "Samuel Soubeyrand"
}
],
pages: "",
number: "",
identifier: [
{
type: "doi",
id: "10.1101/2020.04.17.20069179"
}
],
year: "2020"
}
]
}
}
you are missing res.data object
this.results = res.data.success.data;
this.api.prevPageToken = res.data.success.data.prevPageToken;
this.api.nextPageToken = res.data.success.data.nextPageToken;

Setting a checkbox as checked with Vue.js and Laravel

I have been googling and playing with every combination I know but I cannot get my checkboxes to be initialised as checked.
my controller
public function changeStatus(Request $request, about $about)
{
$this->validate($request, [
'status' => 'in:true,false'
]);
$about->update(['status' => !$about->status]);
return response()->json($about);
}
my migration
$table->boolean('status')->default(false)->nullable();
my template vuejs
<template v-for="item in abouts" :key="item.id"> <a href="javascript:void(0)"
class="togglebutton btn btn-link btn-sm btn-sm"
#click="changeStatusItem(item)">
<label>
<input type="checkbox" name="status" v-model="item.status"
v-bind:id="item.id" :checked="item.status"/>
<span class="toggle"></span>
</label>
</a></template>
my script
export default{props: {
checked: Boolean
},
data() {
return {
abouts: {},
}
},
methods: {
changeStatusItem(item) {
//Start Progress bar
this.$Progress.start();
axios.get(`/change_status_abouts/${item.id}`).then((res) => {
/** Alert notify bootstrapp **/
console.log(res);
})
},}}
Here is how I would do it. Remember that it could be something as simple as the casting from Laravel model i.e. returns a string "1" and not a boolean so to fix that you can use a method that checks for boolean or string, etc. Also need to set the response back to the collection. Also don't use v-model - you don't need it in your example
<template>
<div v-for="item in abouts"
:key="item.id">
<a href="javascript:void(0)"
class="togglebutton btn btn-link btn-sm btn-sm"
#click="changeStatusItem(item)">
<label>
<input type="checkbox"
name="status"
:id="item.id"
:checked="isChecked(item)"/>
<span class="toggle"></span>
</label>
</a>
</div>
</template>
<script>
export default {
name: "Example",
data: function() {
return {
abouts: {}
}
},
methods: {
isChecked(obj) {
return (typeof obj.status === "boolean" )
? obj.status
: typeof obj.status === "string" && obj.status === '1';
},
changeStatusItem: function (item) {
let vm = this;
axios.get(`/change_status_abouts/${item.id}`)
.then((response) => {
for (key in vm.abouts) {
if (vm.abouts.hasOwnProperty(key)) {
vm.abouts[key].status = (vm.abouts[key].id === item.id)
}
}
});
},
getAbouts: function () {
let vm = this;
axios.get(`/get/abouts`)
.then((response) => {
vm.abouts = response.data;
});
},
init() {
this.getAbouts();
}
},
mounted: function () {
this.$nextTick(this.init());
}
}
</script>
When you calling this below method, you can use Array.find() once get response laravel and update into your about array in VueJs
changeStatusItem(item) {
axios.get(`/change_status_abouts/${item.id}`).then((res) => {
let rec = this._data.about.find(o=>o.id == item.id);
if(rec){
rec.status = res.status;
}
})
}
And also you can directly use click event on <input type="checkbox" #click="checkChange($event,item)"> and that event you can call the your ajax call. So in that case you don't need to update the status in about array
Please check below code snippet.
var demo = new Vue({
el: '#demo',
data: {
about:[]
},
methods: {
changeStatusItem(item) {
let rec = this._data.about.find(o=>o.id == item.id);
if(rec){
rec.status = !rec.status;
}
},
getValue(item) {
return `${item.id}. - ${item.title.toLocaleUpperCase()}`;
}
},
created: function() {
let self = this;
fetch('https://jsonplaceholder.typicode.com/posts')
.then(response => response.json())
.then(json =>{self.$data.about = json.map(o=>Object.assign(o,{status:false}))
document.querySelector('.main-div').style.display='none'
})
}
})
.main-div{background-color:#40e0d0;color:#fff;font-size:24px;padding:26px 0;text-align:center;height:100%;width:100%;display:inline-block}.lds-roller{display:inline-block;position:relative;width:64px;height:64px}.lds-roller div{animation:lds-roller 1.2s cubic-bezier(.5,0,.5,1) infinite;transform-origin:32px 32px}.lds-roller div:after{content:" ";display:block;position:absolute;width:6px;height:6px;border-radius:50%;background:#fff;margin:-3px 0 0 -3px}.lds-roller div:nth-child(1){animation-delay:-36ms}.lds-roller div:nth-child(1):after{top:50px;left:50px}.lds-roller div:nth-child(2){animation-delay:-72ms}.lds-roller div:nth-child(2):after{top:54px;left:45px}.lds-roller div:nth-child(3){animation-delay:-108ms}.lds-roller div:nth-child(3):after{top:57px;left:39px}.lds-roller div:nth-child(4){animation-delay:-144ms}.lds-roller div:nth-child(4):after{top:58px;left:32px}.lds-roller div:nth-child(5){animation-delay:-.18s}.lds-roller div:nth-child(5):after{top:57px;left:25px}.lds-roller div:nth-child(6){animation-delay:-216ms}.lds-roller div:nth-child(6):after{top:54px;left:19px}.lds-roller div:nth-child(7){animation-delay:-252ms}.lds-roller div:nth-child(7):after{top:50px;left:14px}.lds-roller div:nth-child(8){animation-delay:-288ms}.lds-roller div:nth-child(8):after{top:45px;left:10px}#keyframes lds-roller{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}.list{padding: 10px;border-bottom: 1px solid #ccc;}
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900|Material+Icons">
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/vuetify#1.4.0/dist/vuetify.min.css">
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#1.4.0/dist/vuetify.min.js"></script>
<div id="demo" >
<div class="main-div"><div class="lds-roller"><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div></div><p>Pleaes wait...!</p>
</div>
<div class="list" v-for="item in about">
<a href="javascript:void(0)"
class="togglebutton btn btn-link btn-sm btn-sm"
#click="changeStatusItem(item)">
<label>
<input type="checkbox" name="status" v-model="item.status"
v-bind:id="item.id"/>
<span class="toggle"></span>
</label> {{getValue(item)}}
</a>
</div>
</div>

Vue.js cannot change data in other hook and methods

I'm developing vlog service with flask, vue.
Between two components of vue - PostList and PostDetail vue -, I use Eventbus to send postId. PostList send postId and PostDetail receive it. And then i request GET method to server using axios and postId. And i changed data like 'this.sth = result.data' but it doesn't changed
here is my code except css
postlist
<template>
<div class="list-container">
<div class="post-list" v-for="post in posts" v-bind:key="post.key" >
<img :src="`http://54.180.79.49/${post.image}`" class="post-img">
<div class="post-container" >
<div class="post-title" v-on:click="redirect(post.post_id)"><p class="text-title">{{ post.title }}</p></div>
<div class="content-container">
<p class="post-content">{{ post.content }}</p>
<div class="meta-container">
<p class="post-meta">{{ formatDate(post.creation_time) }}, {{ post.commentCount }}개</p>
<div class="heart-container">
<img src="../assets/heart.png" class="heart-img">
<p class="heart-cnt">0</p>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import moment from 'moment'
export default {
name: 'PostList',
data () {
return {
posts: [],
category: '',
isAll: false
}
},
created: function () {
this.$EventBus.$on('category', (category) => {
this.isAll = !this.isAll
this.category = category
if (this.isAll === false) {
this.$http.get('/post', {params: {category: 'All'}}).then((result) => {
this.posts = result.data
console.log(result.data)
}).catch(function (error) {
console.log(error)
})
} else {
console.log(this.category)
this.$http.get('/post', {params: {category: this.category}}).then((result) => {
this.posts = result.data
console.log(result.data)
}).catch(function (error) {
console.log(error)
})
}
})
this.$http.get('/post', {params: {category: 'All'}}).then((result) => {
this.posts = result.data
console.log(result.data)
}).catch(function (error) {
console.log(error)
})
},
methods: {
tmp: function () {
console.log(1)
},
formatDate: function (date) {
return moment(date, 'YYYY-MM-DD').format('YYYY-MM-DD')
},
redirect: function (id) {
this.$router.push('/detail')
this.$EventBus.$emit('post_id', id)
}
}
}
</script>
postdetail
<template>
<div class="body-wrapper">
<div class="profile-container">
<div class="profile-wrapper">
<img src="../assets/profile.png" class="small-profile">
<div class="intro-wrapper">
<p class="small-name">test</p>
<p class="small-intro">test</p>
</div>
</div>
</div>
<div class="all-wrapper">
<div class="title-container">
<div class="title-wrapper">
<div class="heart-wrapper">
<img src="../assets/heart.png" class="big-heart">
<div class="count">256</div>
</div>
<div class="title"><p>{{ this.info.title }}</p></div>
</div>
</div>
<div class="wrapper-wrapper">
<div class="meta-wrapper">
<div class="date"><p>{{ this.info.creation_time }}</p></div>
<div class="category"><p>{{ this.info.category }}</p></div>
</div>
</div>
<div class="detail-wrapper">
<div class="detail-body">
<!-- <img v-for="image in this.images" v-bind:key="image.key" :src="`http://54.180.79.49/${image}`" class="post-img"> -->
<div class="post-content"><p>{{ this.info.content }}</p></div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'PostDetailBody',
data () {
return {
info: {}
}
},
created: function () {
console.log(this.id)
this.$EventBus.$on('post_id', (id) => {
this.$http.get('/post/' + id).then((result) => {
this.info = result.data
}).catch(function (error) {
console.log(error)
})
})
}
}
I saw the problem in here:
redirect: function (id) {
this.$router.push('/detail')
this.$EventBus.$emit('post_id', id)
}
and
created: function () {
console.log(this.id)
this.$EventBus.$on('post_id', (id) => {
this.$http.get('/post/' + id).then((result) => {
this.info = result.data
}).catch(function (error) {
console.log(error)
})
})
}
Basically, at the time you emit the event this.$EventBus.$emit('post_id', id), PostDetail component is not created yet. So it this.$EventBus.$on isn't registered.
One possible solution is embedded id in url, and read post's id from router params
redirect: function (id) {
this.$router.push('/detail/' + id)
}

Categories