vue.js 2 list of objects in component - vue.js

I'm very new to js frontend frameworks, and I'm trying to learn some vue.js.
In particular I'm trying to render an array of Note objects that have an id, description and date attributes.
I'm trying to do all of this in a component :)
My ul element looks like
<ul class="list-group">
<li
is="note-item"
v-for="note in notesList"
v-bind:title="note.description"
:key="note.id"
></li>
</ul>
And my Vue code looks like:
Some notes:
I run on page load vue.updateNoteList which calls vue.loadFirstNote.
Vue.component('note-item', {
template: '\
<li>\
{{ note.description }}\
<button v-on:click="$emit(\'remove\')">X</button>\
</li>\
',
props: ['notesList']
});
const vm = new Vue({
el: '#main-content',
data: {
input: '',
notesList: [{ }]
},
methods: {
updateNoteList: function (callback) {
const that = this;
Note.all((notes) => {
that.notesList = notes;
return callback();
});
},
loadFirstNote: function () {
if (this.notesList.length > 0) {
this.note = this.notesList[0];
}
}
});
I've been trying to get this working all day, and I'm getting nowhere. I'm getting the following console errors. Any help would be appreciated.
vue.common.js?e881:519 [Vue warn]: Property or method "note" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
vue.common.js?e881:519 [Vue warn]: Error when rendering component <note-item>:
vue.common.js?e881:2961 Uncaught TypeError: Cannot read property 'description' of undefined

I can see two errors in your code
you are trying to use note in your component, but you have not passed it as props in that component, you have notesList which you dont use in view.
You have use \ in $emit, which is not required
Following are these fixes:
HTML:
<ul class="list-group">
<li
is="note-item"
v-for="note in notesList"
v-bind:title="note.description"
:key="note.id"
:note="note"
></li>
</ul>
JS:
Vue.component('note-item', {
template: '\
<li>\
{{ note.description }}\
<button v-on:click="$emit('remove')">X</button>\
</li>\
',
props: ['note']
});
const vm = new Vue({
el: '#main-content',
data: {
input: '',
notesList: [{ }]
},
methods: {
updateNoteList: function (callback) {
const that = this;
Note.all((notes) => {
that.notesList = notes;
return callback();
});
},
loadFirstNote: function () {
if (this.notesList.length > 0) {
this.note = this.notesList[0];
}
}
});

Related

DOM loading computed property before GET request is finished, how to delay?

I am working on a single page app (Vue-Cli). The DOM Is looking for a value before Vue is done with a GET request. Although the app/data loads fine, the browser gives me an annoying console error: Error in render: "TypeError: Cannot read property 'branch' of undefined".
Here's my Template/HTML code:
<template>
<div>
{{ branch[0].branch }}
</div>
</template>
Vue instance:
data() {
return {
branches: [],
issue: ''
}
},
async created() {
await this.getIssue()
await this.getBranches()
},
methods: {
async getIssue() {
this.issue = (await axios.getIssue(this.$route.params.id)).data[0]
},
async getBranches() {
this.branches = (await axios.getBranches()).data
}
},
computed: {
branch() {
return this.branches.filter(
branch => this.issue.branch === branch.branch_id
)
}
}
How would I correctly "wait" for the BRANCHES to finish loading and THEN filter the branches array and place it in the DOM?
Just use conditional rendering using v-if:
<div v-if="branch.length">
{{ branch[0].branch }}
</div>
https://v2.vuejs.org/v2/guide/conditional.html
Alternatively, use a ternary expression:
<div>
{{ branch.length ? branch[0].branch : '' }}
</div>

Fetch data in component on initiation using parameters from Vuex store

I am new to Vue and am trying to build a simple movie app, fetching data from an API and rendering the results. I want to have an incremental search feature. I have an input field in my navbar and when the user types, I want to redirect from the dashboard view to the search results view. I am unsure of how to pass the query params from the navbar to the search results view.
Here is my App.vue component
<template>
<div id="app">
<Navbar></Navbar>
<router-view/>
</div>
</template>
<script>
import Navbar from './components/Navbar.vue'
export default {
name: 'App',
components: {
Navbar
},
}
</script>
And here is my navbar component where I have the input field
<template>
<nav class="navbar">
<h1 class="logo" v-on:click="goToHome">Movie App</h1>
<input class="search-input" v-on:keyup="showResults" v-model="query" type="text" placeholder="Search..."/>
</nav>
</template>
<script>
import router from '../router/index'
export default {
data: function () {
return {
query: this.query
}
},
methods: {
goToHome () {
router.push({name: 'Dashboard'})
},
showResults () {
//here on each key press I want to narrow my results in the SearchedMovies component
}
}
}
</script>
If I use router.push to the SearchedMovies component then I am only able to pass the query as a parameter once. I thought about using Vuex to store the query and then access it from the SearchedMovies component, but surely there is a better way of doing it?
I also read about using $emit but since my parent contains all the routes, I'm not sure how to go about this.
You don't need to redirect user anywhere. I've made a small demo to show how one might do it. I used this navbar component as you described and emit an event from it:
const movies = {
data: [
{
id: 0,
title: 'Eraserhead',
},
{
id: 1,
title: 'Erazerhead',
},
{
id: 2,
title: 'Videodrome',
},
{
id: 3,
title: 'Videobrome',
},
{
id: 4,
title: 'Cube',
},
]
};
Vue.component('navbar', {
template: '<input v-model="filter" #input="onInput" placeholder="search">',
data() {
return {
filter: '',
};
},
methods: {
onInput() {
this.$emit('filter', this.filter);
}
}
});
// this is just a request imitation.
// just waiting for a second until we get a response
// from the datasample
function request(title) {
return new Promise((fulfill) => {
toReturn = movies.data.filter(movie => movie.title.toLowerCase().indexOf(title.toLowerCase()) !== -1)
setTimeout(() => fulfill(toReturn), 1000);
});
}
new Vue({
el: '#app',
data: {
movies: undefined,
loading: false,
filter: '',
lastValue: '',
},
methods: {
filterList(payload) {
// a timeout to prevent
// instant request on every input interaction
this.lastValue = payload;
setTimeout(() => this.makeRequest(), 1000);
},
makeRequest() {
if (this.loading) {
return;
}
this.loading = true;
request(this.lastValue).then((response) => {
this.movies = response;
this.loading = false;
});
}
},
mounted() {
this.makeRequest('');
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<navbar v-on:filter="filterList"></navbar>
<ul v-if="!loading">
<li v-for="movie in movies" :key="movie.id">{{ movie.title }}</li>
</ul>
<p v-else>Loading...</p>
</div>
Also jsfiddle: https://jsfiddle.net/oniondomes/rsyys3rp/
If you have any problem to understand the code above let me know.
EDIT: Fixed some bugs and added a couple of comments
EDIT2(after the comment below):
Here's what you can do. Every time user inputs something inside a navbar you call a function:
// template
<navbar v-on:input-inside-nav-bar="atInputInsideNavBar"></navbar>
// script
methods: {
atInputInsideNavBar(userInput) {
this.$router.push({
path: '/filtred-items',
params: {
value: userInput
}
})
}
}
Then inside you 'searched movies' page component you can access this value so:
this.$route.params.value // returns userInput from root component

Vue.js data: undefined

I am new to Vue.js.
Please advice me.
I get comments: undefined so comments are not displaying.
xhr is normal with 200.
Thank you
Thank you
Thank you
Thank you
Thank you
<template>
<div>
<ul class="media-list">
<li class="media" v-for="comment in comments">
{{ $comment.body }}
</li>
</ul>
</div>
</template>
<script>
export default {
data () {
return {
comments: []
}
},
props: {
postid: null
},
methods: {
getComments () {
this.$http.get('/blog/' + this.postid + '/comments').then((response) => {
this.comments = response.json().data;
});
}
},
mounted () {
this.getComments();
}
}
Basically there are two problems:
$comment don't exist
You have no data on response.json().data, that's why you get a undefined
I used a different API just to test it (as I don't have access to yours).
TEMPLATE
<div id="app">
<ul class="media-list">
<li class="media" v-for="comment in comments">
{{ comment.familyName + ', ' + comment.givenName }}
</li>
</ul>
</div>
SCRIPT
new Vue({
el: '#app',
data () {
return {
comments: []
}
},
props: {
postid: null
},
methods: {
getComments () {
this.$http.get('//ergast.com/api/f1/drivers.json').then((response) => {
this.comments = response.body.MRData.DriverTable.Drivers;
});
}
},
mounted () {
this.getComments();
}
});
Check out a working example here
this.comments = response.json().data;
console.log(this.comments) ;
to see what you get ;
you define comments=Array ;
maybe you get the response.json().data is not a Array;
Try using vm instead of this. In API response make sure what you are getting using console.log(). If response is already in json do not use response.json(). In HTML change $comment.body to comment.body. Make sure you have the body key in comments[] array.
<template>
<div>
<ul class="media-list">
<li class="media" v-for="comment in comments">
{{ comment.body }}
</li>
</ul>
</div>
</template>
<script>
export default {
data () {
return {
comments: [],
postid: null
}
},
props: {
},
methods: {
getComments () {
let vm = this;
vm.$http.get('/blog/' + vm.postid +
'/comments').then((response) => {
console.log(response)
vm.comments = response.data;
});
}
},
mounted () {
let vm = this;
vm.getComments();
}
}
}
:
My suggestion is to properly use try-catch statements.
I have found this is the safest and proper way to manage cases where variable could take undefined or null values, instead of trying to "if" everything.
try {
val = localStorage.getItem('accesstoken')
} catch (error) {
alert(error)
}
Take care!

I am new to vue js ... can't display loaded data from a php file

Template part :
`<div class="col-sm-6" >
<ul class="list-group" id="pos">
<li class="list-group-item" v-for="(itm, index) in items">
<strong>{{index}} : {{ itm.sub }}</strong> - {{ itm.price }}
</li>
</ul>
</div>`
Script part :
new Vue({
el: '#pos',
http: {
root: '/vuetest',
headers: {
Authorization: 'Basic YXBpOnBhc3N3b3Jk'
}
},
data: {
items: []
},
created:function () {
this.$http.get('index.php').then(function(resp,status,request){
this.items = resp.data;
console.log(this.items); // got data but not displayed in browser
}.bind(this));
}
});
My Source files are from cdnjs.cloudeflare.com:
vue.js (2.1.4)
vue-resource.js (1.0.3).
Well, this isn't that...
created:function () {
this.$http.get('index.php').then(function(resp,status,request){
this.items = resp.data; // this is referring to the current function, and not to vue's scope.
console.log(this.items); // got data but not displayed in browser
}.bind(this));
}
So the correct use is:
created:function () {
var self = this
this.$http.get('index.php').then(function (resp,status,request) {
self.items = resp.data;
console.log(this.items);
});
}
As MU commented above, better you to use Arrows functions, so the "this" will have the scope you want.
mounted () {
}
Then you can access all your Data properties normally with "this".

How to display data in template from Vue.extend?

Here is my code:
var guestContent = Vue.extend({
template: `
<p>Guest content</p>
`,
data: function () {
return {
qs: getQuestionsContent(); // here I would like to get in qs data from function
}
}
});
Here is my App.js:
App = new Vue ({ /
el: '#app',
data: {
topMenuView: "guestmenu",
contentView: "guestcontent",
}
});
I need to display qs instead of Guest content and do for example v-for or some kind of other iteration. How can I do it?
Why:
var guestContent = Vue.extend({
template: `
<p>Guest content</p>
<p>{{foo}}</p> // foo not display
`,
data: function () {
foo: "dddddddddd";
}
});
Why is this not working?
Try something like this:
html
<body>
<guest-content></guest-content>
</body>
<template id="guest-content-template">
Guest Content
<ul>
<li v-for="question in questions">
{{question}}
</li>
</ul>
</template>
js
var guestContent = Vue.extend({
template: "#guest-content-template",
data: function (){
return {
questions: []
}
},
ready(){
this.getQuestionsContent();
},
methods:{
getQuestionsContent(){
this.questions = [
"Why?",
"What?",
"When?"
];
}
}
});
Vue.component('guest-content',guestContent);
new Vue({
el:'body'
});
Anything in a {{}} is assumed to be within the current Vue scope, so you just use foo or questions not guestContent.foo.
The data attribute is a function that has to return the data for the app. If you don't return anything, the component wont have any data.