Vuejs v-for not working with my data, is my data not formatted correctly? - vue.js

Not sure why the data is showing up as undefined. Is the data not formatted correctly? I've tried several ways to adjust it and nest it differently but nothing is working out so far.
<template>
<main>
<AboutMeComponent v-for="i in about" :key="i.posts.id" :title="i.posts.title" :body="i.posts.body" :skills="i.posts.skills"
:img="i.images.img" />
</main>
</template>
<script>
import AboutMeComponent from '../components/AboutMeComponent.vue';
export default {
data() {
return {
about: {
posts: [
{ id: 1, title: "About Me", body: `Body 1` },
{ id: 2, title: "Skills" },
{ id: 3, title: "Hobbies", body: "Body 3" },
],
images: [
{ img: 'figma.png'},
{ img: 'figma.png'},
{ img: 'figma.png'},
]
}
}
},
components: {
AboutMeComponent
}
}
</script>

I think you are need to set the v-for loop on the about.posts, not on the about. Also, if the img is used for the post, I would suggest you keep them with the post, instead of separated.
So your data will be:
data() {
return {
about: {
posts: [
{ id: 1, title: 'About Me', body: 'Body 1', img: 'figma.png' },
{ id: 2, title: 'Skills', body: 'Body 2', img: 'figma.png' },
{ id: 3, title: 'Hobbies', body: 'Body 3', img: 'figma.png' },
],
}
}
},
And now you can loop over about.posts:
<AboutMeComponent v-for="post in about.posts" :key="post.id" :title="post.title" :body="post.body" :img="post.img" />
I left skills out, as they are not in your data.
Just a small suggestion: using variable names as i makes your code harder to understand. If you had used about in about, you would have felt that this was not what you wanted, because you wanted something to do with a post in posts.
Hope this helps.

Few Observations :
As each img in the images array corresponds to each object in the posts array. Merge that into a single one and then use v-for loop for about.posts.
As few properties are missing. Use Optional chaining (?.) operator so that you can read the value of a property located deep within a chain of connected objects without having to check that each reference in the chain is valid.
Live Demo :
Vue.component('aboutmecomponent', {
props: ['key', 'title', 'body', 'skills', 'img'],
template: '<h3>{{ title }}</h3>'
});
var app = new Vue({
el: '#app',
data: {
about: {
posts: [
{ id: 1, title: "About Me", body: "Body 1" },
{ id: 2, title: "Skills" },
{ id: 3, title: "Hobbies", body: "Body 3" },
],
images: [
{ img: 'figma.png'},
{ img: 'figma.png'},
{ img: 'figma.png'},
]
}
},
mounted() {
this.about.posts = this.about.posts.map((obj, index) => {
const newObj = {
...obj,
...this.about.images[index] }
return newObj;
});
delete this.about.images;
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<AboutMeComponent
v-for="item in about.posts"
:key="item?.id"
:title="item?.title"
:body="item?.body"
:skills="item?.skills"
:img="item?.img" >
</AboutMeComponent>
</div>

Related

Hey! I want to implement some anchor links in vue with no library used

Here is my code below. I know there is some specific library like vue-scrollto but I want to resolve this task with no library.
I want to add isViewed true only in that certain link. But the problem is that when I clicked on links each object of that link has isViewed true instead of only one.I want it changes dynamically.
I would appreciate any help.
<template>
<div class="anchors">
<ul class="list">
<li class="item" v-for="(link, index) in anchorLinks" :key="link.id">
<a #click="goToSection(link, link.sectionId, link.id, index)" class="anchor__item-link"></a>
</li>
</ul>
</div>
</template>
export default {
name: "AnchorLinks",
data(){
return{
anchorLinks: [
{
id: 1,
sectionId: "work",
},
{
id: 2,
sectionId: "service",
},
{
id: 3,
sectionId: "partner",
},
{
id: 4,
sectionId: "partner",
},
{
id: 5,
sectionId: "partner",
},
{
id: 6,
sectionId: "partner",
},
{
id: 6,
sectionId: "partner",
}
]
}
},
methods: {
goToSection(link, sectionId, id, index){
const element = document.getElementById(sectionId);
if (element) {
link.isViewed = true;
window.scrollTo({
top: element.offsetTop,
behavior: 'smooth'
});
console.log(id)
console.log(index);
console.log(this.anchorLinks)
}
}
}
}
You should ensure that each anchorLink initially has an isViewed property,
The documentation states:
Vue cannot detect property addition or deletion
<template>
<div class="anchors">
<ul class="list">
<li class="item" v-for="(link, index) in anchorLinks" :key="link.id">
<a
#click="goToSection(link, link.sectionId, link.id, index)"
class="anchor__item-link"
></a>
</li>
</ul>
</div>
</template>
export default {
name: 'AnchorLinks',
data() {
return {
anchorLinks: [
{
id: 1,
sectionId: 'work',
isViewed: false,
},
{
id: 2,
sectionId: 'service',
isViewed: false,
},
{
id: 3,
sectionId: 'partner',
isViewed: false,
},
{
id: 4,
sectionId: 'partner',
isViewed: false,
},
{
id: 5,
sectionId: 'partner',
isViewed: false,
},
{
id: 6,
sectionId: 'partner',
isViewed: false,
},
{
id: 6,
sectionId: 'partner',
isViewed: false,
},
],
};
},
methods: {
goToSection(link, sectionId, id, index) {
const element = document.getElementById(sectionId);
if (element) {
link.isViewed = true;
window.scrollTo({
top: element.offsetTop,
behavior: 'smooth',
});
console.log(id);
console.log(index);
console.log(this.anchorLinks);
}
},
},
};
Without reinventing the wheel here, you basically need to remove the previous link.isViewed property each time you click a link.
One straightforward way to do this is to store another variable containing the active link, that you can update each time you click a new link. See simple example below (ignoring excess / cruft).
As an aside, I would consider not updating the isViewed variable at all and just storing the active link (similar to below) for reference wherever you need it.
<ul>
<li v-for="link in anchorLinks">
<a #click="goToSection(link)"></a>
</li>
</ul>
export default {
name: "AnchorLinks",
data() {
return {
activeLink: null,
// you're using an array here, lean on the index
anchorLinks: [
{ id: "work" },
{ id: "service" },
{ id: "partner" },
],
};
},
methods: {
goToSection(link) {
// remove the previous `isViewed` property
if (this.activeLink) {
delete this.activeLink.isViewed;
}
// update the active link
this.activeLink = link;
const el = document.getElementById(link.id);
if (el) {
// now this will be the only link with `isViewed = true`
link.isViewed = true;
window.scrollTo({
top: el.offsetTop,
behavior: 'smooth'
});
}
}
}
}
From what I can tell and from what you have given, I think you need to set the ids of each anchor link.
<a
:id="link.sectionId"
#click="goToSection(link, link.sectionId, link.id, index)"
class="anchor__item-link"
></a>

how to pass i18n data $t as prop to a component

in a normal way with out translation but i want to translate the two object array and bind into a component
<InfoNews
v-for="infonew in infonews"
:id="infonew.id"
:title="infonew.title"
:content="infonew.content"
/>
data() {
return {
infonews: [
{
id: "01",
title: "what we do",
content:"industke aecimen book. ",
},
{
id: "02",
title: "our mission",
content:"ggdddg",
},
],
};
Make infonews a computed property. The title and content of each should be the translation keys.
export default {
computed: {
infonews() {
return [
{
id: "01",
title: this.$t("what we do"),
content: this.$t("industke aecimen book"),
},
{
id: "02",
title: this.$t("our mission"),
content: this.$t("ggdddg"),
},
]
};
}
}

What does todo.text refer to?

I am trying to learn vue.js and have came across this line of code.
Vue.component('todo-item', {
props: ['todo'],
template: '<li>{{ todo.text }}</li>'
})
var app7 = new Vue({
el: '#app-7',
data: {
groceryList: [
{ id: 0, text: 'Vegetables' },
{ id: 1, text: 'Cheese' },
{ id: 2, text: 'Whatever else humans are supposed to eat' }
]
}
})
I could not figure out what does todo.text here refer to, since I do not see a todo object with text as one of its objects. Sorry for the noob question! Thanks!

How to get data to Quasar tree node from local JSON file

I am very new to Quasar, I want to display Quasar tree nodes from a Local JSON file and with on click of a particular node item I want to show some text.
As of now I am using Quasar tree like this.
<template>
<q-page class="flex fixed-left">
<q-tree
:nodes="simple"
node-key="label"
no-connectors
:expanded.sync="expanded"
text-color="blue"
></q-tree>
</q-page>
</template>
<style>
</style>
<script>
export default {
name: 'HelloWorld',
data () {
return {
expanded: [ 'Resources' ],
simple: [
{
label: 'Resources',
children: [
{
label: 'Projects',
children: [
{
label: 'Data sets' ,
children: [
{
label: 'Items',
}
]
},
{ label: 'Good recipe' }
]
},
{
label: 'Good service (disabled node with icon)',
icon: 'room_service',
disabled: true,
children: [
{ label: 'Prompt attention' },
{ label: 'Professional waiter' }
]
},
{
label: 'Pleasant surroundings (with icon)',
icon: 'photo',
children: [
{
label: 'Happy atmosphere (with image)',
img: 'https://cdn.quasar.dev/img/logo_calendar_128px.png'
},
{ label: 'Good table presentation' },
{ label: 'Pleasing decor' }
]
}
]
}
]
}
}
}
</script>
It was coming like this
Now I wanted to get these nodes from a local JSON file and on click on Items should display some text .
Please help me with this.
To import JSON in ECMAScript:
If you don't have a lot of JSON files and you have the permission to modify and convert them into JS files, you can follow this answer: https://stackoverflow.com/a/39855320/3241933
Then you should import the vanilla JS object into data().
Example solution:
<template>
<q-page class="flex fixed-left">
<q-tree
:nodes="simple"
node-key="label"
no-connectors
:expanded.sync="expanded"
text-color="blue"
></q-tree>
</q-page>
</template>
<style>
</style>
<script>
import jsonData from './data.js'
export default {
name: 'HelloWorld',
data () {
return {
expanded: [ 'Resources' ],
simple: jsonData
}
}
}
</script>
Make sure that your import is an array of object.

Two-Way Binding in Component Scope

Suppose I'm trying to make a simple questionnaire, where the user answers a list of questions.
new Vue(
{
el: "#app",
data:
{
questions:
[
{
id: 1,
name: "What is your favorite color?",
selectedId: 2,
choices:
[
{ id: 1, name: "red" },
{ id: 2, name: "green" },
{ id: 3, name: "blue" },
]
},
...
]
}
});
How do I go about making a question component with two-way binding. That is, if the user swaps their favorite color from green to red, by clicking on the respective input, the selectedId will automatically update. I'm not very clear on how v-model works within a component. Does it only have access to the components data? Also, I don't understand the difference between props/data.
There are lots of ways you can approach this, here's my attempt:
let id = 0;
Vue.component('question', {
template: '#question',
props: ['question'],
data() {
return {
radioGroup: `question-${id++}`,
};
},
methods: {
onChange(choice) {
this.question.selectedId = choice.id;
},
isChoiceSelected(choice) {
return this.question.selectedId === choice.id;
},
},
});
new Vue({
el: '#app',
data: {
questions: [
{
title: 'What is your favorite color?',
selectedId: null,
choices: [
{ id: 1, text: 'Red' },
{ id: 2, text: 'Green' },
{ id: 3, text: 'Blue' },
],
},
{
title: 'What is your favorite food?',
selectedId: null,
choices: [
{ id: 1, text: 'Beans' },
{ id: 2, text: 'Pizza' },
{ id: 3, text: 'Something else' },
],
},
],
},
});
.question {
margin: 20px 0;
}
<script src="https://rawgit.com/yyx990803/vue/master/dist/vue.js"></script>
<div id="app">
<question v-for="question of questions" :question="question"></question>
</div>
<template id="question">
<div class="question">
<div>{{ question.title }}</div>
<div v-for="choice of question.choices">
<input type="radio" :name="radioGroup" :checked="isChoiceSelected(choice)" #change="onChange(choice)"> {{ choice.text }}
</div>
<div>selectedId: {{ question.selectedId }}</div>
</div>
</template>