Pagination problems in Vue and Laravel - laravel

Here is a component I've made for my Vue app within a laravel project.
<template>
<div>
<h2 class="text-center mt-3 mb-5">Categories</h2>
<form v-on:submit.prevent="addCategory">
<div class="row">
<div class="col-2"></div>
<div class="col-md-6">
<div class="form-group">
<input type="text" class="form-control" v-model="name.name">
</div>
</div>
<div col-2>
<button class="ml-5 btn btn-primary" v-on:submit.prevent="addCategory">Add New Tag</button>
</div>
<div class="col-2"></div>
</div>
</form>
<div class="row">
<div class="col-2"></div>
<div class="col-md-8">
<br/>
<table class="table table-hover">
<thead>
<tr class="alert-info">
<td>ID</td>
<td>Category Name</td>
<td style="width: 25%">Actions</td>
</tr>
</thead>
<tbody>
<tr v-for="category in categories.data">
<td>{{ category.id }}</td>
<td>{{ category.name }}</td>
<td>
<router-link :to="{name: 'EditCategory', params: { id: category.id }}"
class="btn btn-primary btn-sm ml-auto">Edit
</router-link>
<button class="btn btn-danger btn-sm" v-on:click="deleteCategory(category.id)">Delete
</button>
</td>
</tr>
</tbody>
</table>
<vue-pagination :pagination="categories" #paginate="fetchCategories()" :offset="4"></vue-pagination>
<hr class="custom-hr-divider"/>
</div>
<div class="col-2"></div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
categories:{
total: 0,
per_page: 2,
from: 1,
to: 0,
current_page: 1
},
name: {}
}
},
created: function () {
this.fetchCategories();
},
components:{
VuePagination: require('../components/Pagination.vue')
},
methods: {
fetchCategories() {
this.axios.get('/categories/index?page=' + this.current_page)
.then(function (response) {
this.users = response.data.data;
this.pagination = response.data;
})
.catch(() => {
console.log('handle server error from here');
});
},
deleteCategory(id) {
let uri = `http://localhost:8000/categories/delete/${id}`;
this.axios.delete(uri);
this.fetchCategories();
},
addCategory() {
let uri = 'http://localhost:8000/categories';
this.axios.post(uri, this.name).then((response) => {
this.fetchCategories();
this.name = {};
})
}
}
};
</script>
Here is the Pagination.vue file I found in an online tutorial.
My app works fine without pagination but introducing this code causes the following errors.
***[Vue warn]: Failed to mount component: template or render function not defined.
found in
---> <VuePagination>
<DisplayCategory> at resources\assets\js\components\DisplayCategory.vue
<Root>***
Where do I define define the component? Ive been trying to get this to work for hours.
app.js
import Vue from 'vue';
import VueRouter from 'vue-router';
Vue.use(VueRouter);
import VueAxios from 'vue-axios';
import axios from 'axios';
Vue.use(VueAxios, axios);
import App from './App.vue';
import DisplayTag from './components/DisplayTag.vue';
import EditTag from './components/EditTag.vue';
import DisplayCategory from './components/DisplayCategory.vue';
import EditCategory from './components/EditCategory.vue';
const routes = [
{
name: 'DisplayTag',
path: '/tags',
component: DisplayTag
},
{
name: 'EditTag',
path: '/tags/edit/:id',
component: EditTag
},
{
name: 'DisplayCategory',
path: '/categories',
component: DisplayCategory
},
{
name: 'EditCategory',
path: '/categories/edit/:id',
component: EditCategory
}
];
const router = new VueRouter({ mode: 'history', routes: routes});
new Vue(Vue.util.extend({ router }, App)).$mount('#app');
Anyone with any ideas what I'm doing wrong?
Thanks in advance.

Achieved pagination by using laravel-vue-pagination
Getting started is easy to follow and I only had to change 1 line as I am using axios.
fetchCategories(page) {
if (typeof page === 'undefined') {
page = 1;
}
// Using vue-resource as an example
let uri = `http://localhost:8000/categories/index?page=`;
this.axios.get(uri + page).then((response) =>
{this.categories = response.data;})

Related

app.js:10567 [Vue warn]: Component is missing template or render function

No clue where the error coming from, hours trying to find it.
All the components the same.
Configure vue router is a nightmare.
Error:
app.js:10567 [Vue warn]: Component is missing template or render
function. at <CreateProduct onVnodeUnmounted=fn
ref=Ref< undefined > > at at at
http://localhost
http://localhost/create
router.js
import AllProduct from './components/AllProduct.vue';
import CreateProduct from './components/CreateProduct.vue';
import EditProduct from './components/EditProduct.vue';
import {createWebHistory, createRouter} from "vue-router";
const routes = [
{
name: 'home',
path: '/',
component: AllProduct
},
{
name: 'create',
path: '/create',
component: CreateProduct
},
{
name: 'edit',
path: '/edit/:id',
component: EditProduct
}
];
const router = createRouter({
history: createWebHistory(),
routes: routes,
});
export default router;
app.js
require('./bootstrap');
import { createApp,h } from 'vue';
import router from './routes';
import App from './App.vue';
import axios from 'axios'
const app = createApp({ render: () => h(App) });
app.config.globalProperties.$axios = axios;
app.use(router);
app.mount('#app');
App.vue
<template>
<div class="container">
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="collapse navbar-collapse">
<div class="navbar-nav">
<router-link to="/" class="nav-item nav-link">Products List</router-link>
<router-link to="/create" class="nav-item nav-link">Create Product</router-link>
</div>
</div>
</nav>
<router-view> </router-view>
</div>
</template>
<script>
export default {}
</script>
AllProducts.vue
<template>
<div>
<h2 class="text-center">Products List</h2>
<table class="table">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Detail</th>
<!-- <th>Actions</th> -->
</tr>
</thead>
<tbody>
<tr v-for="product in products" :key="product.id">
<td>{{ product.id }}</td>
<td>{{ product.name }}</td>
<td>{{ product.detail }}</td>
<td>
<div class="btn-group" role="group">
<router-link :to="{name: 'edit', params: { id: product.id }}" class="btn btn-success">Edit</router-link>
<button class="btn btn-danger" #click="deleteProduct(product.id)">Delete</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default {
data() {
return {
products: []
}
},
created() {
this.axios
.get('/api/products/')
.then(response => {
this.products = response.data;
});
},
methods: {
deleteProduct(id) {
this.axios
.delete(`http://localhost:8000/api/products/${id}`)
.then(response => {
let i = this.products.map(data => data.id).indexOf(id);
this.products.splice(i, 1)
});
}
}
}
</script>
if you read the red error, it says cannot read propertie of undefined (reading 'get')
That means your trying to access the get propertie of an undefined object
looking for getin your code, we find :
this.axios
.get('/api/products/')
so that means this.axios is undefined
it's undefined cause you declared it like this :
app.config.globalProperties.$axios = axios;
with a $ sign

Vue component doesn't load

I want to load a vue component after I click on the "next" button. The project is laravel + vue.
The components load if I put them direct into laravel blade(you can see them below) but if I try to open component from the component it does'nt work. I don't get eny error it just stays the same component. The route in the address bar changes to the route that it should change... I mean it's really starnge.
app.js:
require('./bootstrap');
window.Vue = require('vue').default;
import Vue from 'vue';
import VueRouter from 'vue-router';
import { routes } from './routes';
Vue.use(VueRouter);
Vue.component('productinfo-index', require('./components/productInfo/index.vue').default);
Vue.component('audit-index', require('./components/audit/index.vue').default);
Vue.component('audit-info', require('./components/audit/auditInfo.vue').default);
const router = new VueRouter({
mode: 'history',
routes: routes
});
const app = new Vue({
el: '#app',
router: router
});
Routes.js:
import ProductInfoIndex from './components/productInfo/index';
import Audit from './components/audit/index';
import AuditInfo from './components/audit/auditInfo';
export const routes = [
{
path: '/productInfo',
name: 'ProductInfoIndex',
component: ProductInfoIndex
},
{
path: '/audit',
name: 'Audit',
component: Audit
},
{
path: '/audit/info',
name: 'AuditInfo',
component: AuditInfo
}
];
Index.vue:
<template>
<div>
<!-- Page Heading -->
<div class="d-sm-flex align-items-center justify-content-between mb-4">
<h1 class="h3 mb-0 text-gray-800">Inventorizacija</h1>
</div>
<div class="row">
<div class="card mx-auto">
<div class="card-header">
<router-link
:to="{name: 'AuditInfo'}"
class="btn btn-primary mb-2"
>Next</router-link>
</div>
<!-- <div v-if="showMessage">
<div class="alert alert-success">{{ message }}</div>
</div> -->
<div class="card-body">
<table class="table">
<thead>
<tr>
<th scope="col">Pavadinimas</th>
<th scope="col">Pradėta</th>
</tr>
</thead>
</div>
</div>
</div>
</div>
</template>
AuditInfo.vue(the component that should be loaded):
<template>
<div class="row">
<div class="card mx-auto">
<div class="card-header">
<router-link
:to="{name: 'Audit'}"
class="btn btn-primary mb-2"
>Back</router-link>
</div>
<div class="card-body">
<table class="table">
<thead>
<tr>
<th scope="col">Pavadinimas</th>
<th scope="col">Pradėta</th>
</tr>
</thead>
<tbody>
<tr>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</template>
You should put <router-view /> somewhere in the index.vue for rendering router component.

Vue.js + vue-router + axios + Laravel. Can't retrieve values by id

Why can't get values via axios?
I get correct ID of tasks, can show it in Blade but can't get in Vue.
Where is mistake?
id = 4 - is id of task
When i try http://cosys.loc/api/task/4 - is ok, i get json of task with ID 4.
This is link for example: <router-link :to="{ name: 'TaskShow', params: { id: 4 }}">Show details</router-link>
TaskDetails.vue:
<template>
<div class="container-fluid my-3">
<h1>ID from URL: #{{ this.$route.params.id }}</h1>
<h2>taskId: {{ taskId }}</h2>
<p class="lead">{{ this.$route.tasks }}</p>
<ul>
<li>{{ tasks.id }}</li>
<li>{{ tasks.title }}</li>
<li>{{ tasks }}</li>
</ul>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: "TaskDetails",
data() {
return {
tasks: {},
taskId: this.$route.params.id,
loading: true,
errored: false,
}
},
mounted() {
axios
.get(`/api/task/${this.$route.params.id}`)
.then(response => (this.tasks = response.data.tasks), (console.log(`${this.$route.params.id}`)))
.catch(error => console.log(error))
.finally(() => (this.loading = false));
},
}
</script>
<style scoped>
</style>
When i open url http://cosys.loc/tasks/show/4 get values of tasks with ID 1.
Why, how to solve this?
Tnx,
Anton
I put together an example with two components and a router to show how to implement.
Router
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter);
import Home from '#/components/stackoverflow/router-params-example/Home'
import Detail from '#/components/stackoverflow/router-params-example/Detail'
const routes = [
{
path: '/',
name: 'home',
component: Home
},
{
path: '/detail/:id',
name: 'detail',
component: Detail,
props: true
}
]
export default new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
Home
<template>
<div class="parent">
<h4>Home</h4>
<div class="row">
<div class="col-md-6">
<router-link class="btn btn-primary" :to="{ name: 'detail', params: { id: detailId } }">Show Detail</router-link>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
detailId: 1
}
}
}
</script>
Detail
<template>
<div class="child">
<h4>Detail</h4>
<div class="row">
<div class="col-md-6">
<table class="table table-bordered">
<thead>
<tr>
<th>ID</th>
<th>NAME</th>
<th>USER NAME</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{ user.id }}</td>
<td>{{ user.name }}</td>
<td>{{ user.username }}</td>
</tr>
</tbody>
</table>
<router-link class="btn btn-secondary" :to="{ name: 'home' }">Back</router-link>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
props: {
id: {
type: Number,
required: true
}
},
data() {
return {
user: {}
}
},
methods: {
getUser() {
axios.get('https://jsonplaceholder.typicode.com/users/' + this.id)
.then( response => this.user = response.data )
.catch( error => console.log(error) )
}
},
created() {
this.getUser();
}
}
</script>

Vue js select box giving a couple of errors

I'm doing a site that uses laravel and vue js. The error I'm getting is this
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "selected_parent"
and this
[Vue warn]: Error in v-on handler (Promise/async): "Error: Request failed with status code 404"
I can't see where I'm going wrong.
Here is my product.blade.php
#extends('layouts.public')
#section('content')
<div class="content_wrapper">
#foreach($single_product as $product)
<div class="row single_product_wrapper">
<div class="col-lg-8 col-md-12-col-sm-12 product_details">
#foreach($parent_product as $parent)
<h1>
{{ $parent->title }}
</h1>
<table style="width: 100%; height: 95px;" border="2" cellspacing="5" cellpadding="5">
<tbody>
<tr style="text-align: center;">
<td>
<strong>Code</strong>
</td>
<td>
<strong>Description</strong>
</td>
<td>
<strong>Price</strong>
</td>
</tr>
<tr style="text-align: center;">
<td>
{{ $parent->code }}
</td>
<td>
{{ $parent->description }}
</td>
<td>
{{ $parent->price }}
</td>
</tr>
</tbody>
</table>
#endforeach
<!-- BEGIN ADD TO CART FORM -->
<div id="app">
#foreach($parent_product as $parent)
<code-selection :products="{{ $parent_product }}" :children="{{ $parent->parent }}"></code-selection>
#endforeach
</div>
<!-- END ADD TO CART FORM -->
</div>
</div>
#endforeach
</div>
#stop
and this is my vue
<template>
<div>
<form #submit.prevent="submit">
<div class="row">
<div class="col-lg-12 code_select">
<select name="code" id="code" class="form-control mb-2 mt-10" v-model="selected_parent" required>
<option :value="selected_parent">Please select your code</option>
<option v-for="product in products" :value="product.id">
{{ product.code }}
</option>
<option v-for="child in children" :value="child.id">
{{ child.code }}
</option>
</select>
</div>
</div>
<input type="submit" class="btn btn-dark btn-lg btn-block" value="Add To Cart">
</form>
</div>
</template>
<script>
import axios from 'axios'
export default {
props: [
'products',
'children',
'selected_parent'
],
mounted() {
console.log('Component mounted.')
},
methods: {
submit(){
var formData = new FormData();
formData.append('code', this.selected_parent);
return axios.post('/add-to-cart/'+this.selected_parent, formData)
.then(
function(response)
{
console.log(response.data.redirect);
window.location = response.data.redirect;
}
);
},
},
}
</script>
So what I would like to happen is, when the user selects a code and hits the Add To Cart button they will then get taken to the cart page, but right now
that isn't happening when I select the code and hit the button nothing happens and I get the errors that I said in my console.
If there is anything else you need to know please let me know
The answer is simple, you should break the direct prop mutation by assigning the value to some local component variables(could be data property, computed with getters, setters, or watchers).
Here's a simple solution using the watcher.
<template>
<input
v-model="input"
#input="updateInput" />
</template>
<script>
export default {
props: {
value: {
type: String,
default: '',
},
},
data() {
return {
input: '',
};
},
watch: {
value: {
handler(after) {
this.input = after;
},
immediate: true,
},
},
methods: {
updateInput() {
this.$emit('input', this.input);
},
},
};
</script>
It's what I use to create any data input components and it works just fine. Any new variables sent by parent v-model will be watched and assigned to the input variable and once the input is received, catch that action and emit input to parent suggesting that data is input from the form element.
And for the second part, when you receive the new url from redirect, simply replace the location href like this:
return axios.post('/add-to-cart/'+this.selected_parent, formData)
.then((response) => {
window.location.href = response.data.redirect;
})
.catch((error) => {
console.log(error);
})
);

Child component mounts faster than parent

I use Laravel and Vue. I have two components: parent and child.
Parent:
<template>
<div>
<sport-sites-add :applications-all-list="applicationsAll"></sport-sites-add>
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Application id</th>
<th scope="col">Name</th>
<th scope="col">Description</th>
<th scope="col">Picture</th>
<th scope="col">URL</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<tr v-for="sportSite in sportSites">
<th scope="row">{{ sportSite.id }}</th>
<td>
<template v-for="application in sportSite.applications">
id {{ application.id }} => {{ application.name }} <br>
</template>
</td>
<td>{{ sportSite.name }}</td>
<td>{{ sportSite.description }}</td>
<td>
<img style="width: 100px; height: 100px;" :src="sportSite.image" >
</td>
<td>
<a :href="sportSite.url" target="_blank">{{ sportSite.url }}</a>
</td>
<td>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
import { EventBus } from '../../app';
export default {
name: "SportSitesTable",
mounted(){
this.loadTable();
this.getApplications();
},
methods:{
loadTable: function () {
window.axios.get('/sport_sites_all')
.then(resp => {
this.sportSites = resp.data.data;
}).catch(err => {
console.error(err);
});
},
getApplications: function () {
window.axios.get('/applications/all')
.then(resp => {
this.applicationsAll = resp.data.applications.data;
}).catch(err => {
console.error(err);
});
}
},
data(){
return {
sportSites: [],
applicationsAll: [],
}
},
}
</script>
Child:
<template>
<div>
<button type="button" class="btn btn-primary my-2" data-toggle="modal" data-target="#sportSiteAdd">
Add
</button>
<div class="modal fade" id="sportSiteAdd" tabindex="-1" role="dialog" aria-labelledby="sportSiteAddLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="sportSiteAddLabel">Add sport site</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<ul class="alert-danger">
<li v-for="error in errors">
{{ error[0] }}
</li>
</ul>
<form>
<div class="form-group">
<label for="name">Title</label>
<input type="text" class="form-control" id="name" name="name" v-model="formFields.name">
</div>
<div class="form-group">
<label for="image">Picture</label>
<input type="text" class="form-control" id="image" name="image" v-model="formFields.image">
</div>
<div class="form-group">
<label for="url">URL</label>
<input type="text" class="form-control" id="url" name="url" v-model="formFields.url">
</div>
<div class="form-group">
<label for="description">Description</label>
<textarea class="form-control" id="description" name="description" v-model="formFields.description"></textarea>
</div>
<div>
<label class="typo__label">Applications </label>
<multiselect v-model="formFields.applications"
tag-placeholder="Applications"
placeholder="Search"
label="name"
track-by="id"
:options="applications"
:multiple="true"
:taggable="true">
</multiselect>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" v-on:click="submit">Save</button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { EventBus } from '../../app';
import Multiselect from 'vue-multiselect'
export default {
name: "SportSitesAdd",
props: ['applicationsAllList'],
methods:{
submit: function (e) {
window.axios.post('/sport_site/add/', this.formFields)
.then(res => {
console.log('Saved!');
$('#sportSiteAdd').modal('hide');
this.formFields.name = '';
this.formFields.image = '';
this.formFields.url = '';
this.formFields.description = '';
EventBus.$emit('reloadApplicationsTable');
}).catch(err => {
if(err.response.status === 422){
this.errors = err.response.data.errors || [];
}
console.error('Error of saving!');
});
},
},
data(){
return {
formFields: {
name: '',
image: '',
url: '',
description: '',
applications: this.applicationsAllList,
},
errors: [],
}
},
components: {
Multiselect
},
}
</script>
The parent component is a table. Child component is a form for the table. I pass a data from the parent to the child via props:
<sport-sites-add :applications-all-list="applicationsAll"></sport-sites-add>
In the child component I have a plugin for creating a multiple select. The plugin requires 'options' and 'values' collections. It's very simple, documentation with my case is here https://vue-multiselect.js.org/#sub-tagging. At the result I want to see the following: all items on the select are selected. But I have just the empty collection during mounting of the child component. I have available items on the 'select' but I dont know how I can make it selected by default. Obviously, I need to copy the applicationsAllList into local data() of child component and use it. But it not available during mounted and beforeMounted.
console.log tells me that the child is faster.
you're missing #tag function & v-model, in this case, must be array, You need to use applicationsAllList props directly on options
<multiselect v-model="formFields.value"
tag-placeholder="Applications"
placeholder="Search"
label="name"
track-by="id"
:options="applicationsAllList"
:multiple="true"
#tag="addTag"
:taggable="true">
</multiselect>
in methods add addTag function and add value as array
data() {
return {
formFields: {
name: '',
value: [],
image: '',
url: '',
description: '',
},
errors: [],
}
},
methods: {
addTag (newTag) {
const tag = {
name: newTag,
code: newTag.substring(0, 2) + Math.floor((Math.random() * 10000000))
}
this.options.push(tag)
this.value.push(tag)
}
}

Resources