How to display data from multiple tables in vue js and Laravel? - mysql

I'm trying to display data from three tables in my database on one page. I've managed to do it, everything is working the way I want but I have an error: Error in render: "TypeError: Cannot read property 'file' of undefined"
It seems like I'm getting this error when trying to retrieve the videos data WITHOUT using a v-for loop.
Is it possible to display data on a page without using a v-for loop?
Here is my code.
CandidateProfileController.php:
public function index()
{
$videos = Video::all();
$resumes = Resume::all();
$profile = CandidateProfile::all();
return Response::json(array(
'videos' => $videos,
'resumes' => $resumes,
'profiles' => $profile
));
}
CandidateProfileIndex.vue:
<template>
<div class="col-md-12 grid-margin stretch-card">
<div class="card">
<div class="card-body">
<h3 class="card-title">My Profile</h3>
<b-container class="bv-example-row">
<b-row>
<b-embed
type="video"
aspect="16by9"
:src="`${$store.state.serverPath}/storage/videos/${videos[0].file}`"
allowfullscreen
controls
></b-embed>
</b-row> <br>
<b-row>
<b-col class="text-right" cols="8"></b-col>
<b-col class="text-right" cols="2">
<b-embed
type="video"
aspect="16by9"
:src="`${$store.state.serverPath}/storage/videos/${videos[1].file}`"
controls
class="video-thumbnail"
></b-embed>
</b-col>
<b-col class="text-right" cols="2">
<b-embed
type="video"
aspect="16by9"
:src="`${$store.state.serverPath}/storage/videos/${videos[2].file}`"
controls
class="video-thumbnail"
></b-embed>
</b-col>
</b-row>
</b-container>
<br>
<b-container v-for="(profile, index) in profiles" :key="index">
<div class="b-row">
<div class="b-col" v-for="(resume, index) in resumes" :key="index">
<h4 style="float: left;">Resume:</h4>
<span style="font-size: 0.88rem;">{{resume.file}}</span><br><br>
</div>
</div>
<div class="b-row">
<div class="b-col">
<h4>Experience</h4>
<p>{{profile.experience}}</p>
</div>
</div>
<div class="b-row">
<div class="b-col">
<h4>Additional Skills</h4>
<p>{{profile.skills}}</p>
</div>
</div>
</b-container>
</div>
</div>
</div>
</template>
<script>
import * as groupedService from '../../services/grouped_data_service.js';
export default {
name: "candidateProfileIndex",
data() {
return {
profiles: [],
videos: [],
resumes: [],
};
},
mounted() {
this.loadGroupedData();
},
methods: {
loadGroupedData: async function() {
try {
const response = await groupedService.loadGroupedData();
console.log(response);
this.resumes = response.data.resumes;
this.videos = response.data.videos;
this.profiles = response.data.profiles;
console.log(this.resumes);
} catch (error) {
this.$toast.error("Some error occurred, please refresh!");
}
}
}
}
</script>
grouped_data_service.js:
import {http, httpFile} from './http_service';
export function loadGroupedData() {
return http().get('/candidate-profile');
}

Related

How to make custom slide change button in Swiper? (Vue.js)

I need to delete default navigation (nextEl, prevEl) and make button with "NEXT SLIDE BUTTON" text scroll to next slide
<swiper class="news-slider" :sliderd-per-view="1" :modules="[Navigation, Pagination, A11y, Lazy]" navigation="navigation" :navigation="{ nextEl: '.nextArrow'}" :pagination="{ clickable: true, dynamicBullets: true, dynamicMainBullets: 5 }" preload-images="false" lazy="lazy">
<swiperSlide class="swiper-slide" v-for="n in 10" :key="n">
<div class="news-container">
<div class="news"><img class="news__image swiper-lazy" src="#/assets/img/lookism-transformed-transformed.png" alt=""/>
<div class="swiper-lazy-preloader"></div>
<div class="news__info">
<div class="news__title">title</div>
<div class="news__description">description</div>
<div class="news__buttons">
<button class="normal-button news-button">more</button>
<button class="next-button"><span class="next-button__text">NEXT SLIDE BUTTON</span><img class="next-button__icon" src="#/assets/img/arrow-right.svg" alt=""/></button>
</div>
</div>
</div>
</div>
</swiperSlide>
</swiper>
<script>
export default {
components: {
Swiper,
SwiperSlide,
},
setup() {
const onSwiper = (swiper) => {
console.log(swiper);
};
const onSlideChange = () => {
console.log('slide change');
};
return {
onSwiper,
onSlideChange,
modules: [Navigation, Pagination, Scrollbar, A11y],
};
},
};
</script>
How can i do that? I will be very grateful for your help
This should do it, all you need to do is import useSwiper and assign it to a const variable and use it in the div element as swiper.slideNext()
And also if you do not wish to use swiper's own elements then remove the navigation module.
Here's the doc source which is clearer
https://swiperjs.com/vue#use-swiper

Alpine JS fetch data - limit x-for iteration results and store data for later use

Alpine JS fetch data
How we should do to limit x-for iteration (like the json have 10 results but i want to show only five) and store data for later use with another script outside like a slider to add data ater each slide.
In short, retrieve the json response data to load the next slider image only when the slider arrow will be clicked or the slider will be swiped.
The data should be stored for use in javascript.
HTML:
<div class="main" x-data="init()">
<h4 class="font-xxlarge">Movie search in Alpine.js</h4>
<div class="searchArea">
<input
class="inputText"
type="text"
placeholder="Type to search a fact"
x-model="q"
#keyup.enter="search()"
/>
<button class="bg-default" #click="search()">Search</button>
<br><br>
</div>
<div>
<template x-for="result in results">
<div class="movieCard">
<div>
<img x-bind:src="result.Poster" />
</div>
<div>
<div class="movieDetailItem">
<span style="padding-right: 5px">Title:</span
><span><b x-text="result.Title">Man of Steel</b></span>
</div>
<div class="movieDetailItem">
<span style="padding-right: 5px">Year:</span
><span><b x-text="result.Year">2008</b></span>
</div>
</div>
</div>
</template>
JS:
function init() {
return {
results: [],
q: "",
search: function () {
fetch(
"https://www.omdbapi.com/?&apikey=e1a73560&s=" + this.q + "&type=movie"
)
.then((response) => response.json())
.then((response) => (this.results = response.Search))
.then(response => console.log(response))
.catch((err) => console.log(err));
// console.log(response);
},
};
}
Codepen example: https://codepen.io/onigetoc/pen/yLKXwQa
Alpine.js calls this feature getters, they return data based on other states. Let's say we have startIndex and endIndex variables, then we can do a simple filtering with filter() in the getter method, that returns the items between these two indexes.
<script src="https://unpkg.com/alpinejs#3.x.x/dist/cdn.min.js"></script>
<script>
function init() {
return {
results: ['#1', '#2', '#3', '#4', '#5'],
startIndex: 2,
endIndex: 4,
get filteredResults() {
return this.results.filter((val, index) => {
return index >= this.startIndex && index <= this.endIndex
})
}
}
}
</script>
<div class="main" x-data="init()">
Items:<br>
<template x-for="result in results">
<span x-text="`${result} `"></span>
</template>
<br><br>
Filtered items between index: <span x-text="`${startIndex} and ${endIndex}`"></span>:<br>
<template x-for="result in filteredResults">
<span x-text="`${result} `"></span>
</template>
</div>

Encountering 'TypeError: Cannot read properties of undefined (reading 'memes')' upon attempting to upload a file using ng-upload in Angular

I am struggling with this for a while now. I am trying to upload an image using ng-upload in angular, the file is saved in the database, however I am receiving 'Cannot read properties of undefined' when the upload queue is over. Code used for receiving the file:
Upload.html
<div class="container text-center" *ngIf="memeUploadMode">
<div class="row mt-3">
<div class="col-md-3">
<h3>Dodaj mema</h3>
<div ng2FileDrop
[ngClass]="{'nv-file-over': hasBaseDropzoneOver}"
(fileOver)="fileOverBase($event)"
[uploader]="uploader"
class="card bg-faded p-3 text-center mb-3 my-drop-zone">
<i class="fa fa-upload fa-3x"></i>
Przeciągnij mema tutaj
</div>
<input type="file" ng2FileSelect [uploader]="uploader" />
</div>
<div class="col-md-9" style="margin-bottom: 40px" *ngIf="uploader?.queue?.length">
<h3>Kolejka</h3>
<p>Długość kolejki: {{ uploader?.queue?.length }}</p>
<table class="table">
<thead>
<tr>
<th width="50%">Nazwa</th>
<th>Rozmiar</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of uploader.queue">
<td><strong>{{ item?.file?.name }}</strong></td>
<td *ngIf="uploader.options.isHTML5" nowrap>{{ item?.file?.size/1024/1024 | number:'.2' }} MB</td>
</tr>
</tbody>
</table>
<div>
<div>
Queue progress:
<div class="progress">
<div class="progress-bar" role="progressbar" [ngStyle]="{ 'width': uploader.progress + '%' }"></div>
</div>
</div>
<button type="button" class="btn btn-success btn-s"
(click)="uploader.uploadAll()" [disabled]="!uploader.getNotUploadedItems().length">
<span class="fa fa-upload"></span> Upload all
</button>
</div>
</div>
</div>
And here's the code where I'm trying to add the image to the member object:
Upload.ts
constructor(public accountService: AccountService, private memberService: MembersService,
private router: Router, private toastr: ToastrService) {
this.accountService.currentUser$.pipe(take(1)).subscribe(user => this.user = user);
}
ngOnInit(): void {
this.initializeUploader();
}
fileOverBase(e: any) {
this.hasBaseDropzoneOver = e;
}
deletePhoto(photoId: number) {
this.memberService.deletePhoto(photoId).subscribe(() => {
this.member.photos = this.member.photos.filter(x => x.id !== photoId);
})
}
initializeUploader() {
this.uploader = new FileUploader({
url: this.baseUrl + 'users/add-meme',
authToken: 'Bearer ' + this.user.token,
isHTML5: true,
allowedFileType: ['image'],
removeAfterUpload: true,
autoUpload: false,
maxFileSize: 10 * 1024 * 1024
});
this.uploader.onAfterAddingFile = (file) => {
file.withCredentials = false;
}
this.uploader.onSuccessItem = (item, response, status, headers) => {
if (response) {
const meme: Meme = JSON.parse(response);
this.member.memes.push(meme);
this.user.memeUrl = meme.url;
this.member.memeUrl = meme.url;
this.accountService.setCurrentUser(this.user);
}
}
}
memeToggle() {
this.memeUploadMode = !this.memeUploadMode;
}
The error occurs at this line:
this.member.memes.push(meme);
I've already looked at similar questions, however none of the answers helped me. I must add that I already have a similar feature for adding user's photos, which is set up identically and it works. Any help would be much appreaciated.
Cheers,
Filip
You should define member in your component
member = {
memes: [],
memeUrl: ''
};
Ah, right. I see. I had those in my Upload.ts file when the error occured:
#Input() member: Member;
members: Member[];
memes: Meme[] = [];
model: any = {}
uploader: FileUploader;
After defining the member as you suggested, the compiler asked me to define every other attribute as well. Now it works, you are my savior, my good Sir :)

React map is duplicating DOM Elements

I have the following issue:
I am building a web application, BackEnd in spring and FrontEnd in React.
Now I am adding a feature that shows how many products are in the cart while the client is clicking on "buy". The problem is that when I do a map to get the API in the dom tree, it seems like is duplicating the element.
Images:
Bug: "Carrinho ( )" is being duplicated
Note:
I am consuming two APIs
Code:
import React, { useEffect, useState } from 'react';
import {
Row,
CardBody,
Container,
} from 'reactstrap';
import api from '../../resources/api_produtos';
import apiCart from '../../resources/api_cart';
import axios from 'axios';
const Main = () =>{
const[product, setProduct] = useState([]);
const[cart, setCart] = useState([]);
const fetchData = () =>{
const productApi = api.get('');
const cartApi = apiCart.get('');
axios.all([productApi, cartApi]).then(
axios.spread((...allData) =>{
const allProductData = allData[0].data;
const allDataCart = allData[1].data;
setProduct(allProductData);
setCart(allDataCart);
console.log(allDataCart);
})
)
}
useEffect(() =>{
fetchData()
}, [])
return (
<div>
<div className="p-3 mb-2 bg-dark text-white d-flex justify-content-between">
<div>
<strong>Game Store</strong>
</div>
<div>
{cart.map(carrinho =>(
<div key={carrinho.id}>
<div>
Carrinho ( {carrinho.amount} ) HERE IS BEING DUPLICATED
</div>
</div>
))}
</div>
</div>
<Container>
<div className="jumbotron mt-3"><h1>Produtos</h1></div>
{product.map(produto => (
<div className="card mb-3">
<div key={produto.id}className="card-header d-flex justify-content-between">
<span>
Id: {produto.id}
</span>
<div>
<nav>
<form method="POST" action={"http://localhost:8080/comprar/" + produto.id}>
<input type="submit" className="btn btn-secondary" value="Comprar" ></input>
</form>
</nav>
</div>
</div>
<CardBody>
<Row>
<div className="col-12 col-sm-8 mb-3">
<div className="row">
<div key={produto.id}>
<div >
Nome: {produto.name}
</div>
<div >
Preço: R$ {produto.price}
</div>
</div>
</div>
</div>
<div className="col-12 col-md-4">
<figure key={produto.id}>
<img className="img-thumbnail"src={produto.image} />
</figure>
</div>
</Row>
</CardBody>
</div>
))}
</Container>
</div>
);
}
export default Main;
And this is how the API "Cart" looks like:
[{"amount":"7"},[{"id":12,"name":"Mortal Kombat XL","price":69.99,"score":150,"image":"https://images-americanas.b2w.io/produtos/01/00/offers/01/00/item/126077/6/126077695_1GG.png"},{"id":12,"name":"Mortal Kombat XL","price":69.99,"score":150,"image":"https://images-americanas.b2w.io/produtos/01/00/offers/01/00/item/126077/6/126077695_1GG.png"},{"id":12,"name":"Mortal Kombat XL","price":69.99,"score":150,"image":"https://images-americanas.b2w.io/produtos/01/00/offers/01/00/item/126077/6/126077695_1GG.png"},{"id":12,"name":"Mortal Kombat XL","price":69.99,"score":150,"image":"https://images-americanas.b2w.io/produtos/01/00/offers/01/00/item/126077/6/126077695_1GG.png"},{"id":12,"name":"Mortal Kombat XL","price":69.99,"score":150,"image":"https://images-americanas.b2w.io/produtos/01/00/offers/01/00/item/126077/6/126077695_1GG.png"},{"id":12,"name":"Mortal Kombat XL","price":69.99,"score":150,"image":"https://images-americanas.b2w.io/produtos/01/00/offers/01/00/item/126077/6/126077695_1GG.png"},{"id":12,"name":"Mortal Kombat XL","price":69.99,"score":150,"image":"https://images-americanas.b2w.io/produtos/01/00/offers/01/00/item/126077/6/126077695_1GG.png"}]]
How do I fix this duplication?
The problem is the response object from your cart API.
It's bad-formed, since your BE is returning an array which gives no sense since the response includes one single cart.
So, the map function iterate over an array, modifying its content by the callback, place them in a new array, and then returns it.
So essentially you are trying to modify the obj on the index 0, which is and obj with the "amount" field, and then, you are trying to map an array at index 1.
So you have to update your response from the BE, something like that:
{
"id": "cart_id",
"items": []
}
Which has more sense compared with yours. Note that since items is an array you don't need the "amount" field, you can access it with carrinho.items.length for instance. Then render it with
<div key={carrinho.id}>
<div>Carrinho ({carrinho.items.length})</div>
</div>

Use modal on a v-for element?

I am getting a GET request from my api to disply all users , and i want to modify maybe the name of the email of each users with a modal. The problem is , imagine i have 3 users displayed , 3 modals will be displayed even if i press on only one modal button.
How can i do to fix it ?
there is my code :
<template>
<div class="container">
<div>
<h1>
<b> Users </b>
<input
type="text"
v-model="search"
class="search"
placeholder="Search User"
/>
</h1>
</div>
<div class="overlay-container">
<form>
<div class="form-group">
<div
v-for="user in filterUser"
v-bind:key="user"
class="form-control"
>
<p>
<b>{{ user.fname }} </b> {{ user.lname }}
</p>
<b-button v-b-modal.modal-1>Modifiy</b-button>
<b-modal id="modal-1" title="User Modification">
<p class="my-4">Please edit informations</p>
</b-modal>
</div>
</div>
</form>
</div>
</div>
</template>
<script>
import axios from "axios";
import VueAxios from "vue-axios";
export default {
name: "users",
data() {
return {
search: "",
users: [],
};
},
computed: {
filterUser() {
return this.users.filter((user) => {
return user.fname.match(this.search);
});
},
},
created() {
axios
.get(`http://localhost:4000/api/users`)
.then((Response) => (this.users = Response.data.data));
},
};
</script>
I want to each users open his own modal, not all modals when i press only one modal button.
I hope its clear !
First, pull out the modal inside of v-for, then create a method to get your user index, and add it to the Modify button. this is how you can get the data a user without sending another HTTP Request. and then you can show this.user data in your modal as you want.
<div
v-for="(user, index) in filterUser"
v-bind:key="user"
class="form-control"
>
<p>
<b>{{ user.fname }} </b> {{ user.lname }}
</p>
<b-button
v-b-modal.modal-1
#click="getUserInfo(index)"
>
Modifiy
</b-button>
</div>
data() {
return {
search: "",
users: [],
user: {}
};
},
methods: {
getUserInfo(index) {
this.user = this.users[index]
}
}