Axios get request and display results to html with vue - vue.js

Currently learning Vue and Axios, and i'm trying to get data from an API and display it within an HTML card, but it seems that it tries to display the data before getting it from the API and then nothing renders.
<script type="text/javascript">
const {createApp} = Vue;
const data = null;
const error = null;
const app = createApp({
data(){
return{
movieID: localStorage.getItem("movieID"),
movieDetail: null
}
},
methods:{
getMovieDetail(){
var config = {
method: 'get',
url: 'https://api.themoviedb.org/3/movie/'+localStorage.getItem("movieID"),
headers: {
'Authorization': 'Bearer'
}
};
axios(config);
app.movieDetail = respuesta.data;
document.getElementById("tituloPeli").innerHTML = app.movieDetail.title
.then(function (response) {
app.movieDetail = response.data;
})
.catch(function (error) {
console.log(error);
});
}
},
mounted(){
this.getMovieDetail();
}
}).mount("#contenedor");
</script>
<div id="contenedor">
<div class="container-xxl my-4">
<h1>Data from movie with ID {{movieID}}</h1>
</div>
<div class="container">
<div class="row align-items-start">
<div class="col-12">
<div class="card my-2">
<img class="card-img-top" :src="'https://image.tmdb.org/t/p/w600_and_h900_bestv2' + movieDetail.poster_path">
<div class="card-body">
<h6 class="card-title">
{{movieDetail.title}}
</h6>
</div>
</div>
</div>
</div>
</div>
</div>
Highly appreciated if you could help me, thank u so much

The key is to not render the data until it's ready. This is called conditional rendering and can be done using the v-if directive
<h6 v-if="movieDetail?.title" class="card-title">
{{movieDetail.title}}
</h6>
Along with standard javascript optional chaining you can guarantee an object and it's properties are all defined before rendering it in your template.

Related

Laravel + IntertiaJS = Global Transition with Page

How to make transitions with VueJS between pages (component)? I use InteriaJS.
Here is an example of a page layout:
<script setup>
import {gsap} from "gsap";
const anim_leave = (el, done) => {
console.log("leave");
return gsap.to(el, {opacity:0,y:"-=30px", duration:5,oncomplete:done})
}
const anim_enter = (el, done) => {
console.log("enter");
return gsap.to(el, {opacity:1, y:0, duration:.4,ease:"back.out", delay:.3,oncomplete:done})
}
const anim_before_enter = (el) => {
gsap.set(el, {opacity:0, y:"+=30px"})
}
</script>
<template>
<div class="container-fluid vh-100 d-flex justify-content-center align-items-center">
<Transition
appear
#before-enter="anim_before_enter"
#enter="anim_enter"
#leave="anim_leave"
:css="false">
<div :key="$page.url">
<slot />
</div>
</Transition>
</div>
</template>
anim_before_enter and anim_enter functions are called but never the anim_leave function.
Do you have some ideas to help me out and to make nice transitions between the different pages of Inertia ?
PS : I use Laravel 9

Unable to filter data in props according to search criteria in Vue 3

I'm designing a client management system linked to a mySQL database and is trying to filter this displayed data from the database that is called through an API using laravel.
I have now tried it every which way and I'm struggling. Any help with this will be appreciate it.
My code is as follows.
My Template:
<template>
<div class="team">
<div class="team-members table">
<div class="table-header table-row">
<div class="table-col name">Name</div>
<div class="table-col phone">Phone</div>
<div class="table-col phone">Address</div>
<div class="table-col search">
<input type="text" class="search-bar" v-model="searchQuery" placeholder="Search"/>
</div>
<div class="table-col table-actions">Actions</div>
</div>
<div class="member table-row" v-for="user in clientSearch" :key="user.id">
<div class="table-col name">{{ user.name }}</div>
<div class="table-col phone">{{ user.phone }}</div>
<div class="table-col phone">{{ user.address }}</div>
<div class="table-col actions">
<div class="button-group group-end">
<button class="button button-small" #click="() => toggleForm(user.id)">Update</button>
<button class="button button-small button-alert" #click="() => deleteUser(user.id)">Delete</button>
</div>
</div>
</div>
</div>
</div>
</template>
My script:
<script>
import APIController from '#/controllers/api'
import { ref, reactive, computed } from 'vue'
export default {
props: ["users", "fetchUsers", "toggleForm"],
data(){
return {
}
},
setup (props) {
const clients = reactive(props.users);
const searchQuery = ref("");
const deleteUser = async id => {
const success = await APIController.DeleteUser(id);
if(success) {
props.fetchUsers();
}
}
const clientSearch = computed(() => {
return clients.filter((item) => {
return item.value.name.toLowerCase().indexOf(searchQuery.value.toLowerCase()) != -1;
});
});
return {
deleteUser,
clientSearch,
clients,
searchQuery
}
},
}
</script>
I've tried everything I could think of. And my internet research has turned up nothing. Please help.
I manage to figure out the issue. Through a lot of trial and error and console.logging almost everything I could think of I came to the conclusion that I wasn't accessing the data in the props properly when filtering the array. The API endpoint wrote the data as an array inside an Object and I wasn't targeting the array part of the Object properly with the filter function. So my advise is to study the anatomy of the variable you wish to filter and make sure you target the right data as the filter() function only works on arrays and not key-value pair Objects.
I eventually did away with the reactive variable and wrote the fuction as follows.
const clientList = computed(() => {
let client = ref([]);
client.value = props.users;
if(client.value.user){
return client.value.user.filter(item => { // client.value.user is the actual array...
return item.name.toLowerCase().match(searchQuery.value.toLowerCase());
});
} else {
return client.value.user;
}
});

infinite loop when create infinite scroll using vue.js and laravel

good day; kindly need your support to finalize this issue i try to make infinite scrolle using laravel and vue.js and my proplem is get in finite loop to set request to data base and mu applocation hang up this is my code x component
<template>
<div class="container" style="margin-top:50px;">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header"><strong> Laravel Vue JS Infinite Scroll - ItSolutionStuff.com</strong></div>
<div class="card-body">
<div>
<p v-for="item in list">
<a v-bind:href="'https://itsolutionstuff.com/post/'+item.slug" target="_blank">{{item.title}}</a>
</p>
<infinite-loading #distance="1" #infinite="infiniteHandler"></infinite-loading>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
mounted() {
alert()
console.log('Component mounted.')
},
data() {
return {
list: [],
page: 1,
};
},
methods: {
infiniteHandler($state) {
let vm = this;
this.$http.get('/Services?page='+this.page)
.then(response => {
return response.json();
}).then(data => {
$.each(data.data, function(key, value) {
vm.list.push(value);
});
$state.loaded();
});
this.page = this.page + 1;
},
},
}
</script>
this is my route
Route::get('/Services', 'ServicesController#Services');
Problem 1
You are binding to the distance property wrong.
Solution
Instead of <infinite-loading #distance="1" #infinite="infiniteHandler"></infinite-loading>
it should be
<infinite-loading :distance="1" #infinite="infiniteHandler"></infinite-loading>
Problem 2
In the code, this.page is being incremented before $http.get is resolved.
This may result in unintentional side effects.
Solution
As per the example in docs vue-infinite-loading hacker news example you should be incrementing the page after data is loaded.

v-if not working for form validation and displaying errors

I'm trying to validate a simple form that contains two fields:
A select box
A file field
If one of the fields aren't filled in, a div (containing an error label) should be rendered next to the corresponding input field.
The problem: My 'error divs' aren't rendered when pushing data to the errors object (if the form is invalid).
Please note my console.log statement, that tells me that my error object has a key 'file' and a key 'selectedSupplier'.
Side note: I'm following this example: https://v2.vuejs.org/v2/cookbook/form-validation.html
Differences are, that I'd like to show error labels next to the corresponding field and that I'm setting errors in my errors object, instead of a simple array. So what could I be doing wrong?
Thanks.
This is my Main.vue file:
<template>
<div>
<form #submit="upload">
<div class="mb-8">
<h1 class="mb-3 text-90 font-normal text-2xl">Import Order Csv</h1>
<div class="card">
<div class="flex border-b border-40">
<div class="w-1/5 px-8 py-6">
<label for="supplier_id" class="inline-block text-80 pt-2 leading-tight">Supplier</label>
</div>
<div class="py-6 px-8 w-1/2">
<select v-model="selectedSupplier" id="supplier_id" name="supplier_id" ref="supplier_id" class="w-full form-control form-input form-input-bordered">
<option v-for="supplier in suppliers" v-bind:value="supplier.id">{{ supplier.name }}</option>
</select>
<div v-if="errors.hasOwnProperty('selectedSupplier')" class="help-text error-text mt-2 text-danger">
Required.
</div>
</div>
</div>
<div class="flex border-b border-40">
<div class="w-1/5 px-8 py-6">
<label for="csv_file" class="inline-block text-80 pt-2 leading-tight">File</label>
</div>
<div class="py-6 px-8 w-1/2">
<input id="csv_file" type="file" name="file" ref="file" #change="handleFile">
<div v-if="errors.hasOwnProperty('file')" class="help-text error-text mt-2 text-danger">
Required.
</div>
</div>
</div>
</div>
</div>
<div class="flex items-center">
<button type="submit" class="btn btn-default btn-primary inline-flex items-center relative">Import</button>
</div>
</form>
</div>
</template>
<script>
export default {
mounted() {
this.listSuppliers();
},
data() {
return {
errors: [],
file: '',
suppliers: [],
};
},
methods: {
checkForm() {
if (!this.selectedSupplier) {
this.errors.selectedSupplier = 'Supplier required';
}
if (!this.file) {
this.errors.file = 'File required';
}
},
listSuppliers() {
const self = this;
Nova.request()
.get('/tool/import-order-csv/suppliers')
.then(function (response) {
self.suppliers = response.data.data;
})
.catch(function (e) {
self.$toasted.show(e, {type: "error"});
});
},
handleFile: function (event) {
this.file = this.$refs.file.files[0];
},
upload: function (event) {
this.checkForm();
if (this.errors.hasOwnProperty('selectedSupplier') || this.errors.hasOwnProperty('file')) {
console.log(this.errors); // this actually shows both errors!
event.preventDefault();
}
const formData = new FormData();
formData.append('file', this.file);
formData.append('supplier_id', this.$refs.supplier_id.value);
const self = this;
Nova.request()
.post('/tool/import-order-csv/upload',
formData,
{
headers: {
'Content-Type': 'multipart/form-data'
}
}
).then(function (response) {
self.$toasted.show(response.data.message, {type: "success"});
})
.catch(function (e) {
self.$toasted.show(e.response.data.message, {type: "error"});
});
}
}
}
</script>
Apparently I had to use v-show instead of v-if, because v-if would be 'lazy' and will not render my error-div when the errors var gets filled.
It's working now, but not 100% sure if this is the best way, as I found another tutorial where v-if is used for form validation.(https://medium.com/#mscherrenberg/laravel-5-6-vue-js-simple-form-submission-using-components-92b6d5fd4434)
I was getting the same error, this is how I solved the problem,
<div v-if="errors.field1.length > 0 ? true : false"> // true or false
If you fix the code like this it will work
The reason might because the way you reassign object is not reactive, which not trigger v-if to re-calculate
this.errors.selectedSupplier = 'Supplier required';
this.errors.file = 'File required';
If you still want to use v-if , try change to this approach
this.errors = {...this.errors, selectedSupplier: 'Supplier required' }
this.errors = {...this.errors, file: 'File required' }
The way I handle my errors with VueJS is through lists and their length attribute.
I have an errors object in my data that looks like this:
errors: {
field1: [],
field2: [],
}
Then, when I submit the form, I will:
Empty all the lists for the errors (ie clearing the previous errors)
.push() new errors in the right lists (and .push() makes the Vue reactive)
Finally, in my form, my respective errors divs are displayed based on the length of the list:
<div class="error" v-if="errors.field1.length > 0">
use a v-for to display all the errors from the list
</div>
Hope it helps

Axios setting response data in vue component variable but variable not displaying data on browser

Axios setting response data in Vue component variable but variable not displaying data on the browser. We can see data on console but when I display, Browser shows me nothing. Data are in JSON Format.
Snapshot:
Output:
<template>
<div class="card">
<div class="card-header">Subject</div>
<div class="card-body" >
<!-- <ul class="list-group">
<li class="list-group-item" :key = "message.id" v-for = "message in messages"> {{ message.subject}}</li>
</ul> -->
{{messages}}
</div>
</div>
</template>
<script>
export default{
name: 'subject-component',
data () {
return {
messages:{}
}
},
mounted () {
},
created(){
axios.get('http://127.0.0.1:8000/subject/get')
.then(response => {this.messages = response.data})
.catch((error) => console.log(error));
}
}
</script>
By setting up the project again, this issue has been resolved. And again I used the same approach for axios request like.
axios.get('http://127.0.0.1:8000/get/subjects')
.then((response)=>(this.subjects = response.data))
.catch(function(error){console.log(error)});
Thank you all for your effort.