Laravel 8 and Vue 3 with axios - paginate - laravel

I m using Laravel 8 and Vue 3 with axios. Is this https://www.npmjs.com/package/laravel-vue-pagination laravel vue pagination working with these versions of laravel and vue?
has someone used it?
THX.

here is simple pagination in vue js, this is my example form my codes
data(){
return {
current: 1,
pageSize: 4,
matchingJobs: {},
}
},
computed: {
indexStart() {
return (this.current - 1) * this.pageSize;
},
indexEnd() {
return this.indexStart + this.pageSize;
},
paginated() {
return this.matchingJobs.slice(this.indexStart, this.indexEnd);
}
},
methods: {
prev() {
this.current--;
},
next() {
this.current++;
}
getJobs(){
axios.get('/jobs').then(response => {
this.matchingjobs = response.data
}).catch(errors => {
console.log(errors)
})
},
},
created(){
this.getJobs()
}
sett current: 1 and pageSize:whatever number of items you want to be use computed to manipulate response data from your api. I am not good in explaining but I hope those codes are understandable then this should be in your html
<div v-if="matchingJobs.length">
<div class="row">
<div v-for="(job, index) in paginated" :key="index.id" class="col-xs-6 col-sm-4 col-md-3 col-lg-3 border-light mb-3">
<SingleJob :job="job" />
</div>
</div>
<!-- PAGINATION START -->
<div class="col-12 mt-4 pt-2">
<ul class="pagination justify-content-center mb-0">
<li class="page-item"><a class="page-link" #click="prev" aria-label="Previous">Prev</a></li>
<li class="page-item active"><a class="page-link" href="javascript:void(0)">{{ current }}</a></li>
<li class="page-item"><a class="page-link" #click="next()" aria-label="Next">Next</a></li>
</ul>
</div><!--end col-->
<!-- PAGINATION END -->
I hope this will be helpful

Related

Inertia mounted hook with search resets pagination on every action

My vue file:
data() {
return {
search: '',
}
},
mounted() {
this.search = this.filters.search ?? '';
},
watch: {
search(value) {
Inertia.get('/contacts', { search: value }, {
preserveState: true,
replace: true,
})
}
The Laravel Controller:
$contacts = Model::query()
->paginate(10)
->withQueryString();
return Inertia::render('Contacts/List', [
'contacts' => $contacts,
'filters' => request()->only(['search']),
'currentPage' => request()->page,
]);
It all works perfectly if the mounted block is missing.
With it, on every Inertia reload a "new search" is registered (since it's changed in the mounted hook) and it returns to page 1, so basically, every time you change the page it returns you to page 1.
It should be working perfectly with the Composition API's setup, but not sure why can't I get it to work here.
I use axios instead of Inertia.js in pagination
<template>
<div class="row mt-4">
<div class="col-md-5">
<span
>showing {{ data.from }}-{{ data.to }} of
{{ data.total }} items</span
>
</div>
<div class="col-lg-7">
<nav aria-label="...">
<ul class="pagination float-end">
<template v-for="link in data.links" :key="link.id">
<li v-if="!link.url" class="page-item">
<a #click.prevent="paginate(link.label)" class="page-link">
<span
aria-hidden="true"
v-html="link.label"
></span>
</a>
</li>
<li
v-else
:class="{ active: link.active }"
class="page-item"
>
<a
#click.prevent="paginate(link.label)"
v-html="link.label"
class="page-link"
></a>
</li>
</template>
</ul>
</nav>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
props: ["data", "url"],
methods: {
paginate(label) {
console.log(`${route(this.url)}?page=${label}`);
axios.get(`${route(this.url)}?page=${label}`).then((res) => {
this.$emit('paginate', res.data)
});
}
}
}
</script>
I ran into the same issue, and after some digging I found this answer. The gist is that you should set your search parameter directly in the data attribute instead of using mounted():
data() {
return {
search: this.filters.search ?? '',
}
},
watch: {
search(value) {
Inertia.get('/contacts', { search: value }, {
preserveState: true,
replace: true,
})
}

Why does Vue evaluate my v-if to false despite it being true?

When I run this, vue returns the second template, even though groups.length is equal to 1.
Why? Does it have to do with the order in which the mounting occurs and v-if is evaluated? Again, I am certain that groups.length evaluates to 1. I have tried using beforeMount as opposed to mounted, but that did not work.
<template v-if = "groups.length">
<ul id = "groupList">
<li v-for = "group in groups">
<a>{{ group.name }}</a>
</li>
<div class = "addSidebar">
<label class = "btn" for = "modal-1">+</label>
</div>
</ul>
</template>
<template v-else>
<ul id = "groupList">
<li>
<a>You have not created/joined any groups.</a>
</li>
<div class = "addSidebar">
<label class = "btn" for = "modal-1">+</label>
</div>
</ul>
</template>
<script>
export default {
data() {
return {
groups: {}
}
},
methods: {
getGroups() {
axios.get('groupList').then((response) => {
this.groups = response.data
}).catch((errors) => {
console.log(errors)
});
},
newModal() {
$('#modal').modal('show');
}
},
mounted() {
this.getGroups()
},
name: "groupList"
}
</script>
you need to use javascript Async
https://www.w3schools.com/js/js_async.asp
<template >
<div>
<ul id="groupList" v-if="groups.length">
<li v-for="group in groups" :key="group.id">
<a>{{ group.name }}</a>
</li>
<div class="addSidebar">
<label class="btn" for="modal-1">+</label>
</div>
</ul>
<ul id="groupList" v-else>
<li>
<a>You have not created/joined any groups.</a>
</li>
<div class="addSidebar">
<label class="btn" for="modal-1">+</label>
</div>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
groups: {},
};
},
methods: {
async getGroups() {
await axios
.get("groupList")
.then((response) => {
this.groups = response.data;
})
.catch((errors) => {
console.log(errors);
});
},
newModal() {
$("#modal").modal("show");
},
},
async mounted() {
await this.getGroups();
},
name: "groupList",
};
</script>
in your code you created 2 <template > which is not valid syntax and vue should have root element
https://v2.vuejs.org/v2/guide/components.html#A-Single-Root-Element
Change
v-if = "groups.length"
to:
v-if="groups && groups.length > 0"
And you should have a single that contains one elements in it.

Active user does not show in real time chat app

I am following up this guide https://www.codechief.org/article/real-time-chat-app-with-laravel-6-vue-js-and-pusher#gsc.tab=0 to create real-time chat app in Laravel and Vue.
But it does not show list of active user.
Also this span never shows
<span class="text-muted" v-if="activeUser" >{{ activeUser.first_name }}` is typing...</span>
Also, this method does not work properly because in console log it shows undefined is typing...
sendTypingEvent() {
Echo.join('chat')
.whisper('typing', this.user);
console.log(this.user.fist_name + ' is typing now')
}
And it is not actually real time, because I see new messages only if I reload page.
This is Vue component
<template>
<div class="row">
<div class="col-8">
<div class="card card-default">
<div class="card-header">Messages</div>
<div class="card-body p-0">
<ul class="list-unstyled" style="height:300px; overflow-y:scroll" v-chat-scroll>
<li class="p-2" v-for="(message, index) in messages" :key="index" >
<strong>{{ message.user.first_name }}</strong>
{{ message.message }}
</li>
</ul>
</div>
<input
#keydown="sendTypingEvent"
#keyup.enter="sendMessage"
v-model="newMessage"
type="text"
name="message"
placeholder="Enter your message..."
class="form-control">
</div>
<span class="text-muted" v-if="activeUser" >{{ activeUser.first_name }} is typing...</span>
</div>
<div class="col-4">
<div class="card card-default">
<div class="card-header">Active Users</div>
<div class="card-body">
<ul>
<li class="py-2" v-for="(user, index) in users" :key="index">
{{ user.first_name }}
</li>
</ul>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props:['user'],
data() {
return {
messages: [],
newMessage: '',
users:[],
activeUser: false,
typingTimer: false,
}
},
created() {
this.fetchMessages();
Echo.join('chat')
.here(user => {
this.users = user;
})
.joining(user => {
this.users.push(user);
})
.leaving(user => {
this.users = this.users.filter(u => u.id != user.id);
})
.listen('ChatEvent',(event) => {
this.messages.push(event.chat);
})
.listenForWhisper('typing', user => {
this.activeUser = user;
if(this.typingTimer) {
clearTimeout(this.typingTimer);
}
this.typingTimer = setTimeout(() => {
this.activeUser = false;
}, 1000);
})
},
methods: {
fetchMessages() {
axios.get('messages').then(response => {
this.messages = response.data;
})
},
sendMessage() {
this.messages.push({
user: this.user,
message: this.newMessage
});
axios.post('messages', {message: this.newMessage});
this.newMessage = '';
},
sendTypingEvent() {
Echo.join('chat')
.whisper('typing', this.user);
console.log(this.user.fist_name + ' is typing now')
}
}
}
</script>
"Also, this method does not work properly because in console log it shows undefined is typing..."
i assume you made a typo in your console.log, you probably meant:
this.user.first_name
Concerning your "realtime" problem, i suspect it might be because your broadcasted Event is being queued, so you might want to use the ShouldBroadcastNow instead of ShouldBroadcast

infinite scroll using laravel and vue request not sent to display data

good day;
i new in vue.js
i have simple problem when i using infinite scroll i make configuration as below but when page reloaded it must send request to data base to show data when page =1 this request not sent
i want help to get it work
this is my
this is service component
<template>
<div>
<!-- start covr-page --------------------- -->
<div class="covr-page">
<h2>خدمات لوجستية متكاملة </h2>
<div class="overlay"></div>
</div>
<!-- end covr-page --------------------- -->
<!-- start servic --------------------- -->
<div class="servic sections">
<div class="container">
<h3 class="start-title">
<img src="/images/start-title.png" alt="">
خدمات لوجستية متكاملة
</h3>
<div class="row">
<div v-for="service,key in list" class="col-lg-offset-3 col-lg-6 col-md-12 col-md-8 col-sm-offset-1 col-sm-10">
<a href="order-service.html">
<div class="email-signature">
<div class="signature-details">
<div class="signature-img">
<img :src="`upload/${service.icon}`" alt="">
</div>
</div>
<div class="contant-serv">
<h4>{{service.ar_name}}</h4>
<p>
{{service.ar_description}}
</p>
</div>
</div>
</a>
</div>
<infinite-loading #distance="100" #infinite="infiniteHandler"></infinite-loading>
</div>
</div>
</div>
</div>
</template>
this is my route
Route::get('/Services', 'ServicesController#Services');
this is my js code
<script>
export default {
name: "Services",
data: function () {
return {
list: [],
page:1
}
},
mounted(){
// this.infiniteHandler()
// axios.get('/Services')
// .then((response) =>{
// this.list=response.data
// }).catch((error) =>this.errors=error.response.data.errors );
},
methods: {
infiniteHandler($state) {
this.$http.get('/Services?page='+this.page)
.then(response => {
return response.json();
}).then(data => {
$.each(data.data, (key, value)=> {
this.list.push(value);
});
$state.loaded();
});
this.page = this.page + 1;
},
},
}
</script>
you have not declared vue-infinite-loading
import InfiniteLoading from 'vue-infinite-loading';
export default {
components: {
InfiniteLoading,
},

Laravel-Vue-Pagination error when trying to go to a different page

api.php
Route::get('/products', 'ProductsController#index');
Query:
$products = DB::table('sizes')
->join('products', 'sizes.id', '=', 'products.sizes')
->join('categories', 'products.category', '=', 'categories.id')
->select('products.*', 'categories.catname', 'categories.catimage', 'categories.catdescription', 'sizes.size')
->where([['products.is_active', '=', 1],['categories.is_active', '=', 1],])
->orderBy('products.id', 'ASC')
->paginate(5);
return $products;
Vue component:
<div v-for="product in products.data" :key="product.id">
<h1>{{ product.name }}</h1>
</div>
<pagination :data="products" #pagination-change-page="getResults"></pagination>
methods: {
getResults(page = 1) {
this.$url.get('products/results?page=' + page)
.then(response => {
console.log(response)
this.products = response.data;
});
}
}
The initial load of products works, it shows 5 products and shows pagination. Whenever I try to click a new page from the pagination, I end up with multiple errors.
CORS(which I don't see how since my app is completely public) and two network errors
from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource
net::ERR_FAILED
Uncaught (in promise) Error: Network Error
Is there something I'm missing here? Am I supposed to make another endpoint that handles pagination or should this be handled from the same exact endpoint where it fetches initial pagination?
1. Copy and paste this code in a new component, like 'Pagination.vue'
<template>
<nav aria-label="...">
<ul class="pagination justify-content-center">
<li class="page-item" :class="{ disabled: pagination.current_page <= 1 }">
<a style="cursor:pointer" class="page-link" #click.prevent="changePage(1)" >First page</a>
</li>
<li class="page-item" :class="{ disabled: pagination.current_page <= 1 }">
<a style="cursor:pointer" class="page-link" #click.prevent="changePage(pagination.current_page - 1)"><i class="fa fa-arrow-left"></i></a>
</li>
<li class="page-item" v-for="(page,index) in pages" :key="page" :class="isCurrentPage(page) ? 'active' : ''">
<a style="cursor:pointer" class="page-link" #click.prevent="changePage(page)">{{ page }}
<span v-if="isCurrentPage(page)" class="sr-only">(current)</span>
</a>
</li>
<li class="page-item" :class="{ disabled: pagination.current_page >= pagination.last_page }">
<a style="cursor:pointer" class="page-link" #click.prevent="changePage(pagination.current_page + 1)"><i class="fa fa-arrow-right"></i></a>
</li>
<li class="page-item" :class="{ disabled: pagination.current_page >= pagination.last_page }">
<a style="cursor:pointer" class="page-link" #click.prevent="changePage(pagination.last_page)">Last Page</a>
</li>
</ul>
</nav>
</template>
<script>
export default {
props:['pagination', 'offset'],
methods: {
isCurrentPage(page){
return this.pagination.current_page === page
},
changePage(page) {
if (page > this.pagination.last_page) {
page = this.pagination.last_page;
}
this.pagination.current_page = page;
this.$emit('paginate');
}
},
computed: {
pages() {
let pages = []
let from = this.pagination.current_page - Math.floor(this.offset / 2)
if (from < 1) {
from = 1
}
let to = from + this.offset -1
if (to > this.pagination.last_page) {
to = this.pagination.last_page
}
while (from <= to) {
pages.push(from)
from++
}
return pages
}
}
}
</script>
2. Make it global in your js/app.js file,
Vue.component('pagination', require('./components/Pagination.vue').default);
3. In the vue component, below to the data, set the pagination component like this, you can cange the offset as much you can
<pagination v-if="pagination.last_page > 1"
:pagination="pagination"
:offset="7"
#paginate="getItems()">
</pagination>
4. Set current page to 1,
data(){
return{
items: [],
pagination: {
current_page: 1,
},
}
},
5. Make a method to send the page number and collect paginated data,
getItems(){
axios.get('api/items?page='+this.pagination.current_page)
.then(response => {
this.items = response.data.data;
this.pagination = response.data.meta;
});
},
6. Make sure you return data paginated data with resource collection,
public function index(){
return new GeneralCollection(Item::with('category')->orderBy('name')->paginate(10));
}
***if you, don't have the collection file, make one , like 'GeneralCollection',
php artisan make:resource GeneralCollection
then, include it on the controllers where you want to return collected data,
use App\Http\Resources\GeneralCollection;
7. Congrats !

Resources