Vuejs set method return to template - javascript

I'm new to Vue and I'm stuck at the moment. For the practice I'm making an app for episode checklist for series. The first part of the app searches series and add one of them to a database. Result for the search gives me a result like this: https://i.stack.imgur.com/QuOfc.png
Heres my code with template and script:
<template>
<div class="series">
<ul>
<li v-for="item in series" :key="item.id">
<img :src="image_url+item.poster_path"/>
<div class="info">
{{item.name}}
<br/>
<h5>{{item.id}}</h5>
Start Date: {{item.first_air_date}}
<br/>
{{getEpisodeNumber(item.id)}}
<br/>
{{getSeasonNumber(item.id)}}
</div>
</li>
</ul>
</div>
</template>
<script>
export default {
name: "series",
props: ["series"],
data() {
return {
image_url: "https://image.tmdb.org/t/p/w500",
api_key: {-api key-},
episode_url: "https://api.themoviedb.org/3/tv/",
}
},
methods: {
async getEpisodeNumber(showID) {
const json = await fetch(this.episode_url + showID + this.api_key)
.then((res) => { return res.json() })
.then((res) => { return res.number_of_episodes })
return await json
},
async getSeasonNumber(showID) {
const json = await fetch(this.episode_url + showID + this.api_key)
.then((res) => { return res.json() })
.then((res) => { return res.number_of_seasons })
return await json;
}
},
}
</script>
Methods should return to me a number but they return an object, probably promise object. But when I try to console.log the data in the methods they print a value(int). I need reach this value but I'm stuck. I tried to sort of thinks but it fails every time.

I just create a new component called show and pass item.id to this component. In show component, I use another fetch() to get show data again and now it works like I want.

Related

I have an ID of an object of an API, can i get and print that object?

I made this simple application. There is a homepage where i print movies with an API, and if I click the movie it opens a page with the selected movie info. In the info page I made another Api call. I customized the url so when you click on more info, it returns the id of the object that contains the movie's info. So I made a function that takes the id from the url and confronts it with the one of the call API. if they match, the function returns true. But how am i supposed to get and print the movie info with this data? What would you do? Here is the code:
<template>
<div>
<div v-for="info in movieInfo"
:key="info.id">
{{info.id}}
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'ViewComp',
data() {
return{
movieInfo: [],
}
},
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.movieInfo = response.data.results
// console.log(response.data.results)
})
.catch(error => {
console.log(error)
this.errored = true
})
.finally(() => this.loading = false)
},
methods: {
confrontID(){
var url = window.location.href;
var idUrl = url.substring(url.lastIndexOf('/') + 1);
var idMovie = this.info.id;
if (idUrl === idMovie) {
return true;
}
}
}
}
</script>
<style scoped lang="scss">
/*Inserire style componente*/
</style>
You can get rid of the "return true" on as it will return true if they match. Then instead return the movie info associated with the idUrl
if (idUrl === idMovie) {
return idUrl;
}
Then use that to reference the movie

How to update a row with contenteditable in Vue?

I'm trying to figure out how to get the current changes in a 'contenteditable' and update it in the row that it was changed.
<tbody>
<!-- Loop through the list get the each data -->
<tr v-for="item in filteredList" :key="item">
<td v-for="field in fields" :key="field">
<p contenteditable="true" >{{ item[field] }}</p>
</td>
<button class="btn btn-info btn-lg" #click="UpdateRow(item)">Update</button>
<button class="btn btn-danger btn-lg" #click="DelteRow(item.id)">Delete</button>
</tr>
</tbody>
Then in the script, I want to essentially update the changes in 'UpdateRow':
setup (props) {
const sort = ref(false)
const updatedList = ref([])
const searchQuery = ref('')
// a function to sort the table
const sortTable = (col) => {
sort.value = true
// Use of _.sortBy() method
updatedList.value = sortBy(props.tableData, col)
}
const sortedList = computed(() => {
if (sort.value) {
return updatedList.value
} else {
return props.tableData
}
})
// Filter Search
const filteredList = computed(() => {
return sortedList.value.filter((product) => {
return (
product.recipient.toLowerCase().indexOf(searchQuery.value.toLowerCase()) != -1
)
})
})
const DelteRow = (rowId) => {
console.log(rowId)
fetch(`${import.meta.env.VITE_APP_API_URL}/subscriptions/${rowId}`, {
method: 'DELETE'
})
.then((response) => {
// Error handeling
if (!response.ok) {
throw new Error('Something went wrong')
} else {
// Alert pop-up
alert('Delete successfull')
console.log(response)
}
})
.then((result) => {
// Do something with the response
if (result === 'fail') {
throw new Error(result.message)
}
})
.catch((err) => {
alert(err)
})
}
const UpdateRow = (rowid) => {
fetch(`${import.meta.env.VITE_APP_API_URL}/subscriptions/${rowid.id}`, {
method: 'PUT',
body: JSON.stringify({
id: rowid.id,
date: rowid.date,
recipient: rowid.recipient,
invoice: rowid.invoice,
total_ex: Number(rowid.total_ex),
total_incl: Number(rowid.total_incl),
duration: rowid.duration
// id: 331,
// date: rowid.date,
// recipient: 'new R',
// invoice: 'inv500',
// total_ex: Number(500),
// total_incl: Number(6000),
// duration: 'Monthly'
})
})
}
return { sortedList, sortTable, searchQuery, filteredList, DelteRow, UpdateRow }
}
The commented lines work when I enter them manually:
// id: 331,
// date: rowid.date,
// recipient: 'new R',
// invoice: 'inv500',
// total_ex: Number(500),
// total_incl: Number(6000),
// duration: 'Monthly'
Each cell has content editable, I'm not sure how to update the changed event
The way these run-time js frontend frameworks work could be summarized as "content is the function of data". What I mean is the html renders the data that you send it. If you want the data to be updated when the user changes it, you need to explicitly tell it to do so. Some frameworks (like react) require you to setup 1-way data binding, so you have to explicitly define the data that is displayed in the template, as well as defining the event. Vue has added some syntactic sugar to abstract this through v-model to achieve 2-way binding. v-model works differently based on whichever input type you chose, since they have slightly different behaviour that needs to be handled differently. If you were to use a text input or a textarea with a v-model="item[field]", then your internal model would get updated and it would work. However, there is no v-model for non-input tags like h1 or p, so you need to setup the interaction in a 1-way databinding setup, meaning you have to define the content/value as well as the event to update the model when the html tag content changes.
have a look at this example:
<script setup>
import { ref } from 'vue'
const msg = ref('Hello World!')
</script>
<template>
<h1 contenteditable #input="({target})=>msg=target.innerHTML">{{ msg }}</h1>
<h2 contenteditable>{{ msg }}</h2>
<input v-model="msg">
</template>
If you change the h2 content, the model is not updated because vue is not tracking the changes. If you change through input or h1, the changes are tracked, which will also re-render the h2 and update its content.
TL;DR;
use this:
<p
contenteditable="true"
#input="({target})=>item[field]=target.innerHTML"
>{{ item[field] }}</p>

Vue js vuex unable to display my data in my loop

but i can't show the comments with v-for and i don't understand why my comment data is not working.
I know there is an error but I can't find it.
My request returns a data , but i can't display it my loop.
Thanks for your help
In store/index.js
state :{
dataComments:[]
}
mutation: {
getComments(state, dataComments) {
console.log(dataComments)
state.dataComments = dataComments;
},
}
action: {
getArticleComments: ({ commit }, dataArticles) => {
return new Promise(() => {
instance.get(`/comment/${dataArticles.article_id}`)
.then(function () {
commit('getComments');
})
.catch(function (error) {
console.log(error)
})
})
},
}
in my views/home.vue
export default {
name: "Home",
data: function () {
return {
articles: [],
comments: [],
}
},
methods: {
getArticleComments(comment) {
this.$store
.dispatch("getArticleComments",comment)
.then((res) => {
this.comments = res.data;
});
},
}
<div class="pos-add">
<button
#click="getArticleComments(article)"
type="button"
class="btn btn-link btn-sm">
Show comments
</button>
</div>
<!-- <div v-show="article.comments" class="container_comment"> -->
<div class="container_comment">
<ul class="list-group list-group comments">
<li
class="
list-group-item
fst-italic
list-group-item-action
comment
"
v-for="(comment, indexComment) in comments"
:key="indexComment"
>
{{ comment.comment_message }}
<!-- {{ comment.comment_message }} -->
</li>
</ul>
</div>
Your action getArticleComments does not return anything and I would avoid changing the action to return data. Instead remove the assignment to this.comments in home.vue
Actions do not return data, they get data, and call mutations that update your store.
Your store should have a getter that exposes the state, in this case the dataComments.
getters: {
dataComments (state) {
return state.dataComments;
}
}
Then in your home.vue you can use the helper mapGetters
computed: {
...mapGetters([
'dataComments'
])
}
You want your views to reference your getters in your store, then when any action updates them, they can be reactive.
More here: https://vuex.vuejs.org/guide/getters.html
As far as I see, you don't return any data in your getArticleComments action. To receive the comments you should return them, or even better, get them from your store data directly.
First make sure that you pass the response data to your mutation method:
getArticleComments: ({ commit }, dataArticles) => {
return new Promise(() => {
instance.get(`/comment/${dataArticles.article_id}`)
.then(function (res) {
commit('getComments', res.data);
})
.catch(function (error) {
console.log(error)
})
})
},
After dispatching you could either return the response data directly or you could access your store state directly. Best practice would be working with getters, which you should check in the vue docs.
getArticleComments(comment) {
this.$store
.dispatch("getArticleComments",comment)
.then((res) => {
// in your case there is no res, because you do not return anything
this.comments =
this.$store.state.dataComments;
});
},

Display Vue Computed Array of Objects in HTML

I am having trouble displaying an array of objects from Vue that is fetched from an express server using fetch(); The fetching of the data works but I am not sure as how to display it in html. Below is the Vue code that is successfully fetching the JSON from Express.
computed: {
async fetchData() {
fetch('http://localhost:4000/lessons').then(
function (response) {
response.json().then(
function (json) {
this.lessons = json;
console.log(this.lessons)
});
})
},
}
The console.log successfully displays the fetched array of objects but it is not being displayed in HTML. Below is the HTML code that is not displaying the fetched array of objects.
<div v-for="lesson in fetchData" class="card">
<h2 v-text ="lesson.subject"></h2>
<figure>
<img v-bind:src="lesson.image">
</figure>
<p>Location: {{lesson.location}}</p>
<p>Price: £{{lesson.price}}</p>
<p>Description: {{lesson.description}}</p>
<p>Maximum Class Size: {{lesson.maximumSpaces}} People</p>
</div>
How will I be able to display the array of objects in the HTML file? Thanks for your time.
There are a few problems: 1) Computeds are not async. 2) The template is not async, so you could not call even an async method that way. 3) Your fetch callback function should be an arrow function or it injects its own this and blocks the data setting. 4) Use a :key with v-for. Here is a proper pattern, use a method to fetch the data:
methods: {
async fetchData() {
const response = await fetch('http://localhost:4000/lessons');
this.lessons = await response.json();
}
}
You can call it in the created or mounted lifecycle hook, or somewhere else:
data: () => ({
lessons: []
}),
created() {
this.fetchData()
}
Then iterate over the data:
<div v-for="(lesson, index) in lessons" class="card" :key="index">
...
</div>
Here is a demo:
new Vue({
el: "#app",
data: () => ({
lessons: []
}),
created() {
this.fetchData()
},
methods: {
async fetchData() {
const response = await fetch('https://jsonplaceholder.typicode.com/todos');
this.lessons = await response.json();
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="(lesson, index) in lessons" :key="index">
{{ lesson }}
</div>
</div>

Vuejs searchbar filter returns no result

Today I started playing around with VUEjs for the first time, so I tried to get data out from an URL with JSON. This worked perfectly fine, but I wanted more by adding a search bar. I've watched some tutorials online and I did the same as they did, but it didn't worked out very well for me. After adding filter() to my code I couldn't see anything on my screen. I'm now stuck and don't know what I did wrong in my code.
If I write for example "Bitcoin", I want to get the symbol, name and price back.
<div id="app">
<input type="text" v-model="search" placeholder="search coin">
<ul>
<li v-for="coin in filteredCoins">
{{ coin.symbol }} {{ coin.name }} {{ coin.quotes['USD']['price']}}
</li>
</ul>
</div>
<script src="https://unpkg.com/vue"></script>
<script>
const app = new Vue({
el: '#app',
data: {
data: [],
search: ''
},
computed: {
filteredCoins: function() {
return this.data.filter((coin) => {
return coin.title.match(this.search);
});
}
},
created () {
fetch('https://api.coinmarketcap.com/v2/ticker/')
.then(response => response.json())
.then(json => {
this.data = json.data
})
}
})
</script>
Codepen
json.data is an object, not an array, so you can't use filter on it. You'd need to translate that object to an array to filter it. You can do something like what Bert suggests in his codepen.
computed: {
filteredCoins () {
return Object.values(this.data).filter(coin => coin.name.toLowerCase().match(this.search.toLowerCase()))
},
},

Categories

Resources