Loading JSON data in Datatable takes too long - laravel

I'm using Laravel and Vue for a project I'm working on. Within my dashboard I'm using BootstrapVue's datatable named B-table, which receives JSON data from my API.
The API currently only returns 1 user, however it takes quite some time to load it even though it's just 1 row. I created this GIF to show you it's loading time when refreshing the webpage:
I'm using Axios to receive data from my API, I'm very curious what's causing it to be this slow. Here is my code:
<template>
<div>
<div id="main-wrapper" class="container">
<div class="row">
<div class="col-md-12">
<hr>
<b-table busy.sync="true" show-empty striped hover responsive :items="users" :fields="fields" :filter="filter" :current-page="currentPage" :per-page="perPage" #refreshed="verfris">
<template slot="actions" slot-scope="data">
<a class="icon" href="#"><i class="fas fa-eye"></i></a>
<a class="icon" href="#"><i class="fas fa-pencil-alt"></i></a>
<a class="icon"><i class="fas fa-trash"></i></a>
</template>
</b-table>
<b-pagination :total-rows="totalRows" :per-page="perPage" v-model="currentPage" class="my-0 pagination-sm" />
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
users: [],
filter: null,
currentPage: 1,
perPage: 10,
totalRows: null,
selectedID: null,
fields: [
{
key: 'id',
sortable: true
},
{
key: 'name',
sortable: true
},
{
key: 'email',
sortable: true
},
{
key: 'actions'
}
],
}
},
mounted() {
this.getResults();
},
methods: {
// Our method to GET results from a Laravel endpoint
getResults(ctx, callback) {
axios.get('/api/users')
.then(response => {
this.users = response.data;
this.totalRows = response.data.length;
return this.users;
});
}
},
}
</script>
And the JSON data my API returns:
[
{
"id": 1,
"name": "test",
"email": "user#user.nl",
"email_verified_at": null,
"created_at": "2018-09-28 16:04:36",
"updated_at": "2018-09-28 16:04:36"
}
]
What can I do to solve this loadtime issue?
UPDATE: I'm hosting it on Ubuntu within VirtualBox, maybe it would be important to any of you guys.
UPDATE response time of the request to my API:

From your comment in the post, I guess you already figured out what wrong? i.e. Something is funky about your VirtualBox setup?
I cloned your app and it only took 88ms to load on my Mac.
BTW, your code does not run out of the box. Had to fix and bypass a couple things to get it to run. I would also suggest you install laravel-debugbar to help with debug in laravel.

Related

Post request by axios (VueJS) in laravel giving 500 error

I am trying to make a post request via axios but getting this error: app.js:285 POST http://127.0.0.1:8000/concerts/1/orders 500 (Internal Server Error)
The order is being processed though (I see it coming is Stripe and database). Another problem is that redirect is not happening as window.location =/orders/${response.data.confirmation_number}; I just stay on the same page.
Any ideas what could go wrong here?
<template>
<div>
<div class="row middle-xs">
<div class="col col-xs-6">
{{ csrf_token}}
<div class="form-group m-xs-b-4">
<label class="form-label">
Price
</label>
<span class='form-control-static '>
${{ priceInDollars }}
</span>
</div>
</div>
<div class="col col-xs-6">
<div class="form-group m-xs-b-4">
<label class="form-label">
Qty
</label>
<input type="number" v-model="quantity" class="form-control">
</div>
</div>
</div>
<div class="text-right">
<button class="btn btn-primary btn-block"
#click="openStripe"
:class="{ 'btn-loading': processing }"
:disabled="processing"
>
Buy Tickets
</button>
</div>
</div>
This is script part:
<script>
export default {
props: [
'price',
'concertTitle',
'concertId',
],
data() {
return {
quantity: 1,
stripeHandler: null,
processing: false,
}
},
computed: {
description() {
if (this.quantity > 1) {
return `${this.quantity} tickets to ${this.concertTitle}`
}
return `One ticket to ${this.concertTitle}`
},
totalPrice() {
return this.quantity * this.price
},
priceInDollars() {
return (this.price / 100).toFixed(2)
},
totalPriceInDollars() {
return (this.totalPrice / 100).toFixed(2)
},
},
methods: {
initStripe() {
const handler = StripeCheckout.configure({
key: App.stripePublicKey
})
window.addEventListener('popstate', () => {
handler.close()
})
return handler
},
openStripe(callback) {
this.stripeHandler.open({
name: 'TicketBeast',
description: this.description,
currency: "usd",
allowRememberMe: false,
panelLabel: 'Pay {{amount}}',
amount: this.totalPrice,
// image: '/img/checkout-icon.png',
token: this.purchaseTickets,
})
},
purchaseTickets(token) {
this.processing = true
axios.post(`/concerts/${this.concertId}/orders`, {
email: token.email,
ticket_quantity: this.quantity,
payment_token: token.id,
}).then(response => {
window.location =`/orders/${response.data.confirmation_number}`;
console.log('Charge succeeded.')
}).catch(response => {
this.processing = false
})
}
},
created() {
this.stripeHandler = this.initStripe()
}
}
You have to go and look under the Network tab if you are using Chrome browser, you can see the failed request response
The issue turns out to be Mailer. In .env file, along with Mailtrap credentials you must provide sender email and they don't tell you that :( This also somehow prevented the redirect. In case that helps someone.

How to correctly get object length in JS

This data is coming from the controller, and I have it in the mounted and in the data like so
data() {
return {
forum: [],
}
},
mounted() {
if (this.$page.forum) {
this.forum = this.$page.forum;
}
}
I have this data, it's very long so I won't be posting it all but there should be 13 comments total.
{
"id":1,
"theme":"werwer",
"description":"werwer",
"user_id":1,
"anonymous":0,
"start_date":"2019-12-01 06:00:00",
"end_date":"2019-12-20 12:00:00",
"created_at":"2019-12-04 12:00:50",
"updated_at":"2019-12-09 08:15:47",
"user":{
"id":1,
"name":"sadmin",
"card":"1111",
"scard":"123",
"user_type_id":1,
"email":"sadmin#gmail.com",
"created_at":"2019-12-04 12:00:14",
"updated_at":"2019-12-04 12:00:14"
},
"comments":[
{
"id":5,
"user_id":1,
"discussion_forum_id":1,
"parent_id":3,
"comment":"Este comentario ha sido eliminado.",
"comment_time":"2019-12-09 08:58:10",
"deleted":1,
"created_at":"2019-12-04 12:09:19",
"updated_at":"2019-12-09 08:58:10",
"user":{
"id":1,
"name":"sadmin",
"card":"1111",
"scard":"123",
"user_type_id":1,
"email":"sadmin#gmail.com",
"created_at":"2019-12-04 12:00:14",
"updated_at":"2019-12-04 12:00:14"
},
"replies":[
{
"id":6,
"user_id":1,
"discussion_forum_id":1,
"parent_id":5,
"comment":"reply to reply",
"comment_time":"2019-12-04 12:15:19",
"deleted":0,
"created_at":"2019-12-04 12:15:19",
"updated_at":"2019-12-04 12:15:19",
"user":{
"id":1,
"name":"sadmin",
"card":"1111",
"scard":"123",
"user_type_id":1,
"email":"sadmin#gmail.com",
"created_at":"2019-12-04 12:00:14",
"updated_at":"2019-12-04 12:00:14"
}
}
]
},
]
}
I want to get the comments length so I tried {{forum.comments.length}} and am using it like this
<div v-if="forum.comments.length === 0">
<el-card>
<p>
No comments!
</p>
</el-card>
</div>
<div v-else>
<div v-for="comment in forum.comments">
<el-card>
//show comments
</el-card>
</div>
</div>
How ever I get these errors
Error in render: "TypeError: Cannot read property 'length' of undefined"
Cannot read property 'length' of undefined at <div v-if="forum.comments.length === 0">
The code itself works, and does the expected but still gets those 2 errors always. What is the correct way to do this and get rid of these errors?
Assuming the explanation is your data is loaded asynchronously and not available at component rendering.
The solution is to wrap your template into a whole check for forum.comments existence to avoid rendering the block before data is loaded. For instance :
<template v-if="forum && Array.isArray(forum.comments)">
<div v-if="forum.comments.length === 0">
<el-card>
<p>
No comments!
</p>
</el-card>
</div>
<div v-else>
<div v-for="comment in forum.comments">
<el-card>
//show comments
</el-card>
</div>
</div>
</template>

How to empty input fields from a pop-up window after submitting - Vue - laravel?

My page exist of a table where I can add new rows. If you want to add a new row a pop-up window appear where the new values can be added.
This new data is then saved to the database after submitting. If I again want to add a new row the input fields, they should be cleared.
The method I use, is working but isn't very clear.
Note: My code shows only a part of the input fields, to make it more clear. My pop-up window actually contains 20 input fields.
I would like to clear them all at once instead of clearing them one by one (like I am doing now).
Because I am already doing this for defining the v-model, pushing the new data to the database directly on the page and via post axios request.
Is there a cleaner way to do this?
Thanks for any input you could give me.
This is my code:
html part
<div class="col-2 md-2">
<button class="btn btn-success btn-sx" #click="showModal('add')">Add New</button>
<b-modal :ref="'add'" hide-footer title="Add new" size="lg">
<div class="row" >
<div class="col-4">
<b-form-group label="Category">
<b-form-input type="text" v-model="newCategory"></b-form-input>
</b-form-group>
</div>
<div class="col-4">
<b-form-group label="Name">
<b-form-input type="text" v-model="newName" placeholder="cd4"></b-form-input>
</b-form-group>
</div>
<div class="col-4">
<b-form-group label="Amount">
<b-form-input type="number" v-model="newAmount" ></b-form-input>
</b-form-group>
</div>
</div>
<div class="row" >
<div class="col-8">
</div>
<div class="col-4">
<div class="mt-2">
<b-button #click="hideModal('add')">Close</b-button>
<b-button #click="storeAntibody(antibodies.item)" variant="success">Save New Antibody</b-button>
</div>
</div>
</div>
</b-modal>
</div>
js part
<script>
import { async } from 'q';
export default {
props: ['speciedata'],
data() {
return {
species: this.speciedata,
newCategory: '',
newName: '',
newAmount:'',
}
},
computed: {
},
mounted () {
},
methods: {
showModal: function() {
this.$refs["add"].show()
},
hideModal: function(id, expId) {
this.$refs['add'].hide()
},
addRow: function(){
this.species.push({
category: this.newCategory,
name: this.newName,
amount: this.newAmount,
})
},
storeSpecie: async function() {
axios.post('/specie/store', {
category: this.newCategory,
name: this.newName,
amount: this.newAmount,
})
.then(this.addRow())
// Clear input
.then(
this.newName = '',
this.newCategory = '',
this.newAmount = '',
)
.then(this.hideModal('add'))
},
}
}
</script>
in your data of vuejs app , you have to set one object for displaying modal data like modalData then to reset data you can create one function and set default value by checking type of value using loop through modalData object keys
var app = new Vue({
el: '#app',
data: {
message:"Hi there",
modalData:{
key1:"value1",
key2:"value2",
key3:"value3",
key4:5,
key5:true,
key6:"val6"
}
},
methods: {
resetModalData: function(){
let stringDefault="";
let numberDefault=0;
let booleanDefault=false;
Object.keys(this.modalData).forEach(key => {
if(typeof(this.modalData[key])==="number"){
this.modalData[key]=numberDefault;
}else if(typeof(this.modalData[key])==="boolean") {
this.modalData[key]=booleanDefault;
}else{
// default type string
this.modalData[key]=stringDefault;
}
});
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app">
{{modalData}}
<br/>
<button #click="resetModalData">Reset Modal Data</button>
</div>
update : in your case :
data:{
species: this.speciedata,
modalData:{
newCategory: '',
newName: '',
newAmount:''
}
},
and after storing data :
storeSpecie: async function() {
axios.post('/specie/store', {
category: this.newCategory,
name: this.newName,
amount: this.newAmount,
})
.then(()=>{
this.addRow();
this.resetModalData();
this.hideModal('add')
}
},
In native Javascript you get the reset() method.
Here is how it is used :
document.getElementById("myForm").reset();
It will clear every input in the form.

Laravel router-link works only the first time

I am trying to fetch results from database in News.vue, and display them in Topnews.vue. I have two links fetched. When I click link1, it shows up the Topnews.vue template with everything working as intended, however, if i click link2, nothing happens, except for that the URL changes, but the template does not show up the result. If i refresh the page and click link2 or click on the navbar, then link2, it shows up, and same, clicking then link1, changes the URL, but doesnt show up. I'm really stuck on that and I'd be really glad if you help me out on that issue. Hope you understand.
News.vue
<template id="news">
<div class="col-sm-5">
<div class="cars" v-for="row in filteredNews" >
<div class="name" >
<p class="desc_top_time">{{row.created_at}}</p>
<span class="last_p"> {{row.category}}</span>
<h3 style="margin-bottom:-4px; font-size: 16px;">
<router-link class="btn btn-primary" v-bind:to="{name: 'Topnews', params: {id: row.id} }">{{row.title}}</router-link></h3>
</div></div></div>
</template>
<script>
export default {
data: function() {
return {
news: [],
}
},
created: function() {
let uri = '/news';
Axios.get(uri).then((response) => {
this.news = response.data;
});
},
computed: {
filteredNews: function() {
if (this.news.length) {
return this.news;
}
}
}
}
</script>
Topnews.vue
<template id="topnews1">
<div class="col-sm-7">
<div class="cars">
<img :src="topnews.thumb" class="img-responsive" width=100%/>
<div class="name" ><h3>{{ topnews.title }}</h3>
<p>
<br>{{ topnews.info }}<br/>
</p>
</div></div></div>
</template>
<script>
export default {
data:function(){
return {topnews: {title: '', thumb: '', info: ''}}
},
created:function() {
let uri = '/news/'+this.$route.params.id;
Axios.get(uri).then((response) => {
this.topnews = response.data;
});
}
}
</script>
Like GoogleMac said Vue will reuse the same component whenever possible. Since the route for both IDs use the same component Vue will not recreate it, so the created() method is only being called on the first page. You'll need to use the routers beforeRouteUpdate to capture the route change and update the data.
in TopNews.vue:
export default {
data:function(){
return {topnews: {title: '', thumb: '', info: ''}}
},
beforeRouteEnter:function(to, from, next) {
let uri = '/news/'+ to.params.id;
Axios.get(uri).then((response) => {
next(vm => {
vm.setData(response.data)
})
});
},
beforeRouteUpdate: function(to, from, next) {
let uri = '/news/'+ to.params.id;
Axios.get(uri).then((response) => {
this.setData(response.data);
next();
});
},
methods: {
setData(data) {
this.topnews = data
}
}
}
If you click a link referring to the page you are on, nothing will change. Vue Router is smart enough to not make any changes.
My guess is that the IDs are messed up. If you are using Vue devtools you will be able to easily see what data is in each link. Are they as you expect.

Meteor: use dynamic parameters in a template call

In my Meteor app i have the TAPi18n package and aldeed:autoform. Im trying to make a form validation using i18n for the placeholder field but i don't know how to do it.
So, I was wondering if it is possible to pass a dynamic parameter (that's how I call it) to a template, using Spacebars. I mean, without using helpers in the JS files, something like this:
<template name="Publish">
<div class="col-md-8 col-lg-8 col-md-offset-2 col-lg-offset-2 well">
{{#autoForm collection="Publications" id="insertPublicationForm" type="insert"}}
<fieldset>
<legend>{{_ "publish_title"}}</legend>
<div class="col-md-12 col-lg-12">
<div class="form-group">
{{> afFieldInput name='name' placeholder={{_ "name"}} }}
</div>
<div class="form-group">
{{> afFieldInput name='description' placeholder={{_ "description"}} }}
</div>
<div class="form-group">
{{> afFieldInput name='price' placeholder={{_ "price"}} }}
</div>
</div>
<div class="form-group">
<button type="submit" id="add_publication" class="btn btn-success center-block">{{_ "publish"}}</button>
</div>
</fieldset>
{{/autoForm}}
</div>
</template>
I could register a helper for every translation but i don't like too much the idea.
I also know that i could use the label field in the SimpleSchema, like this:
Meteor.startup(function() {
Publications.attachSchema(new SimpleSchema({
name: {
type: String,
label: TAPi18n.__("name"),
max: 200
},
description: {
type: String,
label: TAPi18n.__("description")
},
price: {
type: Number,
label: TAPi18n.__("price"),
min: 0
}
}));
});
And then use the afQuickField template instead of afFieldInput.
But i don't want to use a label, i want to use the placeholder of the input.
There is any way to do this?
Well, i don't know why i didn't see it before, but i can do this in the SimpleSchema:
Meteor.startup(function() {
Publications.attachSchema(new SimpleSchema({
name: {
type: String,
label: TAPi18n.__("name"),
max: 200,
autoform: {
afFieldInput: {
placeholder: TAPi18n.__("name")
}
}
},
description: {
type: String,
autoform: {
afFieldInput: {
placeholder: TAPi18n.__("description")
}
}
},
price: {
type: Number,
min: 0,
autoform: {
afFieldInput: {
placeholder: TAPi18n.__("price")
}
}
}
}));
});
That way i can use i18n in the placeholder without making tons of helpers.
Sorry if I made someone waste time on this.

Resources