I am working on this Laravel Project where I load a component activities-table with the main Vue instance data called from an API where I use Eloquent ORM Pagination. But the ActivitiesTable.data is undefined:
So, first I have my API on http://app.local/api/activities
public function getActivities()
{
$activities = Activity::orderBy('created_at', 'desc')->paginate(100);
foreach($activities as $k => $activity)
{
$data = [
'user' => isset($activity->user) ? $activity->user->name : NULL,
'avatar' => isset($activity->user) ? $activity->user->avatar : NULL,
'ip' => $activity->remote_addr,
'country' => $activity->country,
'referer' => $activity->referer,
'action' => $activity->request_uri,
'date' => $activity->created_at->toDateTimeString(),
'timeago' => $activity->created_at->diffForHumans()
];
$activities[$k]->custom_data = $data;
}
return $activities;
}
Then I have my main Vue instance method where I call the API on the file app.js:
loadActivities: function(page = 1) {
axios.get('/api/activities?page=' + page)
.then(function (response) {
app.activities = response.data;
});
},
So this is how activities seems on Vue Tool:
So, this is my component that is already registered on app.js:
<template>
<!-- Activities -->
<table id="activities">
<thead>
<tr>
...columns...
</tr>
</thead>
<tbody>
...data...
</tbody>
</table>
<!-- /Activities -->
</template>
<script>
module.exports = {
props: {
rows: {
}
},
data: function() {
return {
data: this.rows
}
},
created: function() {
},
methods: {
paginate: function(page) {
}
}
}
</script>
<style scoped>
</style>
And then here is the <activities-table> component:
<activities-table
:rows="this.activities"
></activities-table>
I get the result in the same Vue Tool:
I already tried to assign this.data = this.rows in the created hook, or try different lifecycle hooks but always is undefined or an empty array.
This is what console.log(this.data) on the Vue component displays:
What am I doing wrong? Thanks in advance.
Related
I'm stuck to get the data from the database through Axios using Vue.js. In Vue.js developer tools I can get the data from my database like this:
But when I loop using v-for like this:
<template>
<div class="flex flex-col items-center py-4">
<NewPost></NewPost>
<Post v-for="(post,i) in posts.data" :post="post" :key="i"/>
</div>
</template>
<script>
import NewPost from "../components/NewPost";
import Post from "../components/Post";
export default {
name: "Newsfeed",
components: {
NewPost,
Post
},
data: () => {
return {
posts: null
}
},
mounted() {
axios.get('/api/posts').then(res => {
this.posts = res.data
console.log(this.posts)
}).catch(error => {
console.log('Unable to fetch posts')
})
}
}
</script>
<style scoped>
</style>
The console says
"[Vue warn]: Error in render: "TypeError: Cannot read property 'data'
of null"
found in
---> at resources/js/views/Newsfeed.vue
at resources/js/components/App.vue
"
I don't understand because in Vue.js developers tools I can get the data. Is the looping wrong? Thank you!
Init that nested field with an empty array like :
data: () => {
return {
posts: {
data:[]
}
}
},
Another answer is to add the loading variable like this
In the data :
data: () => {
return {
posts: null,
loading: true
}
}
In the mounted()
mounted() {
axios.get('/api/posts').then(res => {
this.posts = res.data
this.loading = false
console.log(this.posts)
}).catch(error => {
this.loading = false
console.log('Unable to fetch posts')
})
}
enter image description hereI am making an app in Nuxt and vue using storyblok as my CMS. However, I have been receiving errors when trying to link the storyblok array to my arrays called in my template using v-for.
Here is the template:
<template>
<div>
<!-- instance header -->
<InstanceHeader title="Books" />
<div class="pageContainer">
<div class="booksInfoPost">
<div class="booksInfoPost__subHeader"><h3>Top Books</h3></div>
<div class="booksInfoPost__topBooks">
<BooksInfoPostTop
v-for="book in books"
:key ="book.id"
:bookCover="book.bookCover"
:title="book.title"
:author="book.author"
:content="book.content"
:id="book.id"
/>
</div>
<div class="booksInfoPost__subHeader"><h3>Book Titles</h3></div>
<BooksInfoPost
v-for="book in posts"
:key ="book.id"
:bookCover="book.bookCover"
:title="book.title"
:author="book.author"
:content="book.content"
:id="book.id"
/>
</div>
</div>
Here is my script:
export default {
components: {
InstanceHeader,
BooksInfoPostTop,
BookTitles,
BooksInfoPost
},
data() {
/* return {
books: [],
posts: []
} */
},
async asyncData(context) {
return {
bookTitles: context.app.$storyapi
.get("cdn/stories", { version: "draft", starts_with: 'books/book-titles'})
.then(response => {
console.log(response);
return {
posts: response.data.stories.map(bp => {
return {
id: bp.slug,
bookCover: bp.content.bookCover,
title: bp.content.title,
author: bp.content.author
};
}),
}
}),
topBooks: context.app.$storyapi
.get("cdn/stories", { version: "draft", starts_with: 'books/top-books'})
.then(response => {
console.log(response);
return {
books: response.data.stories.map(b => {
return {
id: b.slug,
bookCover: b.content.bookCover,
title: b.content.title,
author: b.content.author
};
}),
}
})
}
}
}
I noticed this error more when I tried calling two APIs from storyblok. When I called one API call I did not see this error. I have also tried using Axios but I am getting errors using that method as well. I am not the most experienced developer and If anyone can help I'll appreciate it. Thanks
export default {
components: {
InstanceHeader,
BooksInfoPostTop,
BookTitles,
BooksInfoPost
},
async asyncData(context) {
const result = {};
const mapBooks = b => {
return {
id: b.slug,
bookCover: b.content.bookCover,
title: b.content.title,
author: b.content.author
};
};
const { data } = await context.app.$storyapi
.get("cdn/stories", {
version: "draft",
starts_with: 'books/book-titles'
});
result.posts = data.stories.map(mapBooks);
const result = await context.app.$storyapi
.get("cdn/stories", {
version: "draft",
starts_with: 'books/top-books'
});
result.books = result.data.stories.map(mapBooks);
return result; // it has right property names {books:[], posts:[]}
}
}
Well as you mentioned in the comment it was a little mess before. So i tidied it up. The idea is that you need direct property names instead of nested objects. This way it should work, if it is not working check the network tab for the errors.
I'm building a key-command resource and giving VueJS a whirl while doing so. I'm a newbie but am gaining the grasp of things (slowly...).
I want to be able to search in a global search form for key commands I'm defining as actions within sections of commands (see data example below). I would like to search through all the actions to show only those that match the search criteria.
My HTML is below:
<div id="commands">
<input v-model="searchQuery" />
<div class="commands-section" v-for="item in sectionsSearched"
:key="item.id">
<h3>{{ item.section }}</h3>
<div class="commands-row" v-for="command in item.command" :key="command.action">
{{ command.action }}
</div>
</div>
</div>
My main Vue instance looks like this:
import Vue from 'vue/dist/vue.esm'
import { commands } from './data.js'
document.addEventListener('DOMContentLoaded', () => {
const element = document.getElementById("commands")
if (element != null) {
const app = new Vue({
el: element,
data: {
searchQuery: '',
commands: commands
},
computed: {
sectionsSearched() {
var self = this;
return this.commands.filter((c) => {
return c.command.filter((item) => {
console.log(item.action)
return item.action.indexOf(self.searchQuery) > -1;
});
});
},
}
});
}
});
And finally the data structure in data.js
const commands = [
{
section: "first section",
command: [
{ action: '1' },
{ action: '2' },
{ action: '3' },
],
},
{
section: "second section",
command: [
{ action: 'A' },
{ action: 'B' },
{ action: 'C' },
]
},
]
export { commands };
I'm able to output the commands using the console.log(item.action) snippet you see in the computed method called sectionsSearched.
I see no errors in the browser and the data renders correctly.
I cannot however filter by searching in real-time. I'm nearly positive it's a combination of my data structure + the computed method. Can anyone shed some insight as to what I'm doing wrong here?
I'd ideally like to keep the data as is because it's important to be sectioned off.
I'm a Rails guy who is new to this stuff so any and all feedback is welcome.
Thanks!
EDIT
I've tried the proposed solutions below but keep getting undefined in any query I pass. The functionality seems to work in most cases for something like this:
sectionsSearched() {
return this.commands.filter((c) => {
return c.command.filter((item) => {
return item.action.indexOf(this.searchQuery) > -1;
}).length > 0;
});
},
But alas nothing actually comes back. I'm scratching my head hard.
There is a issue in your sectionsSearched as it is returning the array of just commands.
See this one
sectionsSearched() {
return this.commands.reduce((r, e) => {
const command = e.command.filter(item => item.action.indexOf(this.searchQuery) > -1);
const section = e.section;
r.push({
section,
command
});
}, []);
}
const commands = [
{
section: "first section",
command: [
{ action: '1' },
{ action: '2' },
{ action: '3' },
],
},
{
section: "second section",
command: [
{ action: 'A' },
{ action: 'B' },
{ action: 'C' },
]
},
]
const element = document.getElementById("commands")
if (element != null) {
const app = new Vue({
el: element,
data: {
searchQuery: '',
commands: commands
},
computed: {
sectionsSearched() {
var self = this;
return this.commands.filter((c) => {
// the code below return an array, not a boolean
// make this.commands.filter() not work
// return c.command.filter((item) => {
// return item.action.indexOf(self.searchQuery) > -1;
// });
// to find whether there has command action equal to searchQuery
return c.command.find(item => item.action === self.searchQuery);
});
},
}
});
}
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="commands">
<input v-model="searchQuery" />
<div class="commands-section" v-for="item in sectionsSearched"
:key="item.id">
<h3>{{ item.section }}</h3>
<div class="commands-row" v-for="command in item.command" :key="command.action">
{{ command.action }}
</div>
</div>
</div>
Is that work as you wish ?
sectionsSearched() {
return this.commands.filter((c) => {
return c.command.filter((item) => {
return item.action.indexOf(this.searchQuery) > -1;
}).length > 0;
});
},
}
since filter will always return an array(empty or not) which value always is true.
I am trying to complete a CRUD application with Vuejs and Laravel. Right now I can add an article but cannot delete and I ssee this error in my console:
SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data
The html inside the template is like the following:
<div class="card card-body"v-for="article in articles" :key="article.id">
<h3>{{ article.title }}</h3>
<p>{{ article.body }}</p>
<button #click="deleteArticle(article.id)" class="btn btn-danger">Delete</button>
</div>
Then inside the script I have this:
How can I make the delete work?
<script>
export default {
data(){
return{
articles: [],
article: {
id: '',
title: '',
body: ''
},
article_id: '',
pagination: {},
edit: false
}
},
created(){
this.fetchAllArticles();
},
methods: {
fetchAllArticles(){
fetch('/api/articles').then(res => res.json()).then(res => {
this.articles = res.data;
})
.catch(err => console.log(err));
},
deleteArticle(id){
if(confirm('Are you sure?')){
fetch('api/article/${id}', {
method: 'delete'
})
.then(res => res.json())
.then(data => {
alert('Article removed');
this.fetchAllArticles();
})
.catch(err => console.log(err));
}
}
}
}
</script>
If I type my url for the delete method I can view the data as you can see in the image:
My delete controller looks like this:
public function destroy($id)
{
// Get article
$article = Article::findOrFail($id);
if($article->delete()) {
return new ArticleResource($article);
}
}
From the netwok in the browser I see this:
Thanks in advance
I have never use fetch i love using axios, but through googling i found that you have to use not those symbols '' but those `` so:
fetch(`api/article/${id}`, {
method: 'delete'
})
From what I see in your console the API address is incorrect, should be 'api/article/' + id
I get my data using axios and set my object to the new data. I also declare the object at first with empty values.
The data displays correctly in my Vue dev tools and if I log it in the console, but as soon as I display it in my HTML
<pre>{{myObj}}</pre>
It displays the old initial empty data
My code:
export default {
data(){
return {
myObj: {
foo: "",
bar: "",
loo: {},
moo: [],
}
}
},
methods: {
getData: function(){
axios.get('/my/url')
.then((response)=>{
this.myObj = response.data;
console.log("Response data: " , response.data); //**A**
console.log("'This data:' " , this.data.purchaseorder); //**B**
})
.catch((error)=>{
//ERROR
});
}
}
}
A:
Displays the actual correct data from my request
B:
Displays the actual correct data from my request
Things I tried:
I read this documentation https://v2.vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats
and I saw they said a root object can't be reactive, so I wrapped "myObj" in a container object.
myData: {
myObj: {
foo: "",
bar: "",
loo: {},
moo: [],
}
}
and I have replaced
this.myObj = response.data;
with
Vue.set(this.myData, 'myObj', response.data);
and
this.$set(this.myData, 'myObj', response.data);
Nothing works!
My main issue is that it is working perfectly on localhost! I'm guessing it has something to do with the small delay on the hosted server as opposed to local?
UPDATE
Here are the images with the real data
Vue component data (from the Vue dev tools)
Console.log data
HTML displayed data
UPDATE 2
As requested, an MCVE
<template lang="html">
<div>
<table>
<thead>
<tr>
<th>No</th>
<th>Product</th>
<th>Quantity</th>
<th>Price / mt</th>
<th>Order Total</th>
<th>Currency</th>
</tr>
</thead>
<tbody>
<tr v-for="(detail, idx) in purchaseorder.podetail">
<td></td>
<td>
<input type="text" v-model="detail.product.product_name">
</td>
<td><input type="number" v-model="detail.po_qty"></td>
<td><input type="number" v-model="detail.podetailcost.po_price"></td>
<td><input type="number" v-bind:value="parseFloat(detail.po_qty * detail.podetailcost.po_price)"></td>
<td>
<input type="text" v-model="detail.podetailcost.currency.currency_description">
</td>
</tr>
</tbody>
</table>
</div>
</template>
<script>
export default {
data(){
return { //Just to initialize the Obj
purchaseorder: {
podetail: [
{
po_qty: "",
podetailcost: [
{
po_price: "",
currency: {currency_id:"", currency_description:""},
},
],
}
},
},
props: ['po_id'],
methods: {
getData(){
axios.get('/procurement/get/editdata/'+this.po_id)
.then((response)=>{
this.purchaseorder = response.data;
console.log("Response data: " , response.data); //Displays fine
console.log("This data: " , this.purchaseorder); //Displays fine
})
.catch((error)=>{
//TODO
});
}
},
mounted(){
this.getData();
}
}
</script>
My desired result (This is a screenshot from my localhost, in the MCVE I removed a lot and just included the bare minimum. Don't judge the object, I did not create the DB, but I get the data in that format.
Have you call that getData method? It doesn't call automatically by Vue. You can use mounted or created lifecycle hooks to call that method. Somekind like this,
methods: {
getData: function(){
axios.get('/my/url')
.then((response)=>{
this.myObj = response.data;
console.log("Response data: " , response.data); //**A**
console.log("'This data:' " , this.data.purchaseorder); //**B**
})
.catch((error)=>{
//ERROR
});
}
},
mounted () {
this.getData()
}
Change the getData method to the following, without function:
methods: {
getData () {
axios.get('/my/url')
.then(response => {
this.myObj = response.data;
console.log("Response data: " , response.data); //**A**
console.log("'This data:' " , this.data.purchaseorder); //**B**
})
.catch(error => {
//ERROR
});
}
}
In this case, the context of this is bound within getData: function () { ... }