Vue.js 3 Uncaught (in promise) TypeError: Cannot convert undefined or null to object - laravel

So, I'm trying to fetch my laravel api and already run php artisan serve. After that the error came out and I'm looking to find solution here but I have no idea what's going on.
The error shown in the console:
Uncaught (in promise) TypeError: Cannot convert undefined or null to object
The API:
{
"message": "List trash and category order by time",
"data": [
{
"id": 1,
"trash_id": 1,
"category_id": 2,
"created_at": "2022-01-01T12:41:43.000000Z",
"updated_at": "2022-01-01T12:41:43.000000Z",
"garbage": {
"id": 1,
"name": "Buku",
"weight": 1,
"created_at": "2022-01-01T12:41:19.000000Z",
"updated_at": "2022-01-01T12:41:19.000000Z"
},
"categories": {
"id": 2,
"name": "Kertas",
"price": 1800
}
}]
}
Index.vue script:
<script>
import axios from "axios";
import { onMounted, ref } from "vue";
export default {
setup() {
// reactive state
let all_trash = ref([]);
onMounted(() => {
// get data from api endpoint
axios
.get("http://127.0.0.1:8000/api/trash")
.then((result) => {
all_trash.value = result.data;
})
.catch((err) => {
console.log(err.response);
});
});
return {
all_trash,
};
},
};
</script>
Index.vue HTML:
<div v-for="(trash, index) in all_trash.data" :key="index">
<div class="card rounded shadow mb-3">
<div class="card-body">
<div class="mx-3">
<h5 class="mb-4">{{ trash.garbage.name }}</h5>
<p>
{{ trash.categories.name }}
<span class="float-end">
<button class="btn" style="color: red">Hapus</button>
</span>
</p>
</div>
</div>
</div>
</div>

Just add a condition that not to render the loop until data is not set. The data from API end point is not available on the initial dom rendered and there is not data in all_trash.data hence the error. Just add a v-if on top of the div and hopefully it should work.
<div v-if=" all_trash.data">
<div v-for="(trash, index) in all_trash.data" :key="index">
<div class="card rounded shadow mb-3">
<div class="card-body">
<div class="mx-3">
<h5 class="mb-4">{{ trash.garbage.name }}</h5>
<p>
{{ trash.categories.name }}
<span class="float-end">
<button class="btn" style="color: red">Hapus</button>
</span>
</p>
</div>
</div>
</div>
</div>

Related

Data not showing in my console.log in vue

In my vue app I have 2 methods, one method gets some data from my laravel backend and the second one needs to be able to grab it so that I can use it in that method.
What I'm struggling with is that the second method isn't grabbing the data.
Here is my code
<template>
<app-layout>
<div class="content-wrapper" style="margin-left: 0;">
<div class="content">
<div class="container">
<div class="row pt-5">
<div class="col-lg-12">
<div class="card">
<div class="card-body">
<div class="row">
<div class="col-lg-12">
Some data will show here
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</app-layout>
</template>
<script>
import AppLayout from '#/Layouts/AppLayout'
export default {
components: {
AppLayout,
},
data() {
return {
testData: ''
}
},
methods: {
firstMethod() {
axios.get('/api/get-data').then(response => {
this.testData = response.data;
});
},
secondMethod(){
console.log(this.testData);
}
},
mounted() {
this.firstMethod();
this.secondMethod();
}
}
</script>
your running both function in mount function so both run at same time and secondMethod() executed 1st at that time your this.testData is not set so you can use async and await to wait to finish firstMethod() then run secondMethod()
which will be like below code
<template>
<app-layout>
<div class="content-wrapper" style="margin-left: 0">
<div class="content">
<div class="container">
<div class="row pt-5">
<div class="col-lg-12">
<div class="card">
<div class="card-body">
<div class="row">
<div class="col-lg-12">
Some data will show here
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</app-layout>
</template>
<script>
import AppLayout from "#/Layouts/AppLayout";
export default {
components: {
AppLayout,
},
data() {
return {
testData: "",
};
},
methods: {
async firstMethod() {
const { data } = await axios.get("/api/get-data");
this.testData = data;
},
secondMethod() {
console.log(this.testData);
},
},
async mounted() {
await this.firstMethod();
this.secondMethod();
},
};
</script>
You can try calling firstMethod in created() hook instead of mounted. In my opinion you do not need a method for modifying incoming data. Use watch instead:
watch: {
// whenever question changes, this function will run
testData: function (newValue) {
// do what transformation you need here
}
}
Watch hooks run when value of variable changes, so it should run when it is assigned.
The problem that you dont see the console log is because even if you execute first the first method, it's actually executed after the second method because takes more time to be resolved.
Try the below please, inside first method i added after then to execute the second method which means that that the first method would be resolved, thus we will have the api response.
In case you continue seeing nothing from console log then there is an issue with the api endpoint.
mounted() {
this.firstMethod();
},
methods: {
firstMethod() {
axios.get('/api/get-data').then(response => {
this.testData = response.data;
this.secondMethod();
});
},
secondMethod(){
console.log(this.testData);
}
},

How to bind formControlName value with *ngFor in Angular

Response Json from back-end
{
"INCOME": {
"TOURIST": [{
"vehicleNumber": "TN 47 RG 4567",
"details": [{
"id": 01,
"amount": 100
},{
"id": 02,
"amount": 200
}]
},
{
"vehicleNumber": "TN 47 RG 9876",
"details": [{
"id": 03,
"amount": 300
},{
"id": 04,
"amount": 400
}]
}
]
}
}
is assigned to this.tesTourist = this.objectValue.INCOME.TOURIST;
component.html
<form [formGroup]="accountsForm" autocomplete="off">
<div class="row">
<div *ngFor="let obj of tesTourist">
<div *ngFor="let item of obj.details">
<label class="text-left"> {{obj.vehicleNumber}} :</label>
<input type="text" formControlname="incomeTourist" value="{{item.amount}}">
</div>
</div>
</div>
</form>
component.ts
this.accountsForm = this.fb.group({
incomeTourist:[],
});
Let as take obj.details.length is 2. It displays 2 input box in page with the value amount binded(item.amount). While changing the amount in any one of the input field that could not bind the amount in incomeTourist. It constantly shows null as below.
Result of <pre><code>{{accountsForm?.value | json}}</code></pre>
{
"incomeTourist": null
}
You form structure should be like below.
this.accountsForm = this.fb.group({
testTourist: this.fb.array([]),
});
After that create 2 method to create testTourist and incomeTourist form array.
createTestTourist() {
return this.formBuilder.group({
details: this.fb.array([])
});
}
createDetails() {
return this.formBuilder.group({
amount: [],
vehicleNumber: []
});
}
And after that loop over the testTourist object in ngOnInt method.
this.testTourists.forEach(testTourist => {
const fb = this.createTestTourist();
testTourist.details.forEach((detail, i) => {
const cfb = this.createDetails();
cfb.get('amount').setValue(detail.amount);
cfb.get('vehicleNumber').setValue(detail.vehicleNumber);
(fb.controls.details as FormArray).push(cfb);
});
(this.accountsForm.get('testTourist') as FormArray).push(fb);
});
after this in html you should write below code.
<form [formGroup]="accountsForm" autocomplete="off">
<div class="row">
<div *ngFor="let obj of testTourists; let i= index" formArrayName="testTourist">
<ng-container [formGroupName]="i">
<div *ngFor="let item of obj.details; let j= index" formArrayName="details">
<ng-container [formGroupName]="j">
<label class="text-left"> {{item.vehicleNumber}} :</label>
<input type="text" formControlName="amount">
</ng-container>
</div>
</ng-container>
</div>
</div>
</form>
After this if you do any changes it will reflect in form object. You can console the form object.
console.log(this.accountsForm.value)
you will get expect same object structure as you are providing. And you will get updated values.

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,
},

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>

Attempting to load a Vue component and populate it with Ajax json content in Laravel

I am new to Laravel and a complete noob to Vue. I searched many other Laravel/Vue posts but none seemed to be similar enough to get me to a solution. When attempting to load this component on my view I receive the following error
app.js:32654 [Vue warn]: Property or method "features" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
Please let me know what I am missing.
chrome.google.com/webstore/detail/vuejs-devtools/ shows that Vue is loaded
I would like to load data from an ajax call to my vue component. that can be updated on the fly by event handler
App.js
window.Vue = require('vue');
Vue.component('Example', require('./components/Example.vue'));
const app = new Vue({
el : '#app',
});
$(document).ready(function() {
$.ajaxSetup({
headers : {
'X-CSRF-TOKEN' : $('meta[name="csrf-token"]').attr('content')
}
});
})
Example.vue
<template>
<div class="container projects-container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Example Component</div>
<div class="panel-body">
<h1>I'm an example component!</h1>
<ul class="list-group">
<li class="list-group-item" v-for="feature in features">
{{ feature.properties.name }}
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
mounted() {
console.log('Component mounted.')
},
}
</script>
bladefile
<head>
<meta name="csrf-token" content="INMA4kLlG32gfhf4Z3BBGIFxitrVCWzzqgqPfooEj">
// and yes Vue is loaded
</head>
<body>
<div id="app">
<example></example>
</div>
...
<script>
Vue.component('example',{
template: 'Example',
})
//returns the JSON listed below
someOtherObject.addListener('click', function(e) {
$.ajax({
url:json,
method:'GET',
success:function(msg){
app.data = JSON.parse(msg);
}
})
})
</script>
JSON
{
"type":"FeatureCollection",
"features":[
{
"type":"Feature",
"geometry":{
"type":"Point",
"coordinates":[
-117.155083,
33.569672
]
},
"properties":{
"heading":null,
"face":"South",
"status":"1",
"name":"MEADOWLARK ",
"type":"Single Family Home",
"client_id":"26",
"client_name":"Pulte Homes",
"city_name":"French Valley"
}
},
{
"type":"Feature",
"geometry":{
"type":"Point",
"coordinates":[
-117.151390,
33.543981
]
},
"properties":{
"heading":null,
"face":"South",
"status":"1",
"name":"Testing Project",
"type":"Single Family Home",
"client_id":"83",
"client_name":"Testing Account",
"city_name":"Temecula Valley"
}
},
{
"type":"Feature",
"geometry":{
"type":"Point",
"coordinates":[
-117.223720,
33.571522
]
},
"properties":{
"heading":null,
"face":"South",
"status":"1",
"name":"Oak Ridge",
"type":"Single Family Home",
"client_id":"98",
"client_name":"Woodside 05S LP",
"city_name":"Beaumont"
}
}
]
}
The features array must be declared either as a prop or part of the component data.
As a prop:
<template>
<div class="container projects-container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Example Component</div>
<div class="panel-body">
<h1>I'm an example component!</h1>
<ul class="list-group">
<li class="list-group-item" v-for="feature in features">
{{ feature.properties.name }}
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
mounted() {
console.log('Component mounted.')
},
props: ['features']
}
</script>
As component data:
<template>
<div class="container projects-container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<div class="panel panel-default">
<div class="panel-heading">Example Component</div>
<div class="panel-body">
<h1>I'm an example component!</h1>
<ul class="list-group">
<li class="list-group-item" v-for="feature in features">
{{ feature.properties.name }}
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
mounted() {
console.log('Component mounted.')
},
data: function() {
return {features: []}
}
}
</script>
If you use a prop, you'll need to bind the prop in the template. If you use component data, you'll need to update the success callback function in the AJAX request to correctly update the data of the component rather than the app as a whole.
Also, beware of the reactivity caveats for loading new data, as outlined in the docs.
EDIT: Full example solution using component data
Here's a working example using a simplified version of your example component. Note that to do this I used the ref attribute so that the component could be addressed directly. Using refs is described in the docs as an "escape hatch" that should only be used when strictly necessary. A better pattern might be an external store, such as Vuex.
Vue.component( "example", {
template: '<div><h1>Example Component!</h1><ul v-if="features.length"><li v-for="feature in features">{{ feature.name }}</li></ul></div>',
data: function() {
return { features : [] }
}
});
var app = new Vue( { el: '#app' });
//A function to represent the AJAX callback
var doMockAjax = function() {
var mockData = [{name: 'One'},{name: 'Two'},{name: 'Three'}];
app.$refs.example.features = mockData;
}
document.getElementById('load-data').addEventListener('click', function(e) {
//This simulates the ajax callback by populating the array asynchronously
setTimeout(doMockAjax,1000);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<div id="app">
<example ref="example"></example>
</div>
<button id="load-data">Load</button>

Resources