What causes the duplicated key error in this Vue.js application? - javascript

I am working on a small Todo App with Vue 3.
In App.vue I have the 3 child components and the methods:
<template>
<div id="app">
<Header/>
<List/>
<Footer/>
</div>
</template>
<script>
import axios from 'axios'
import Header from './components/Header.vue'
import List from './components/List.vue'
import Footer from './components/Footer.vue'
export default {
name: 'App',
components: {
Header,
List,
Footer
},
props: ['todos'],
data() {
return {
url: "https://jsonplaceholder.typicode.com/todos?&_limit=5",
dataIsLoaded: false,
isValidInput: true,
todos: [],
unsolvedTodos: [],
newTitle: "",
}
}
}
</script>
In List.vue I have:
<template>
<div>
<h1>List</h1>
<ul class="todo-list" v-if="dataIsLoaded">
<TodoItem v-for="todo in todos.slice().reverse()" :key="todo.id" v-bind:class="{done: todo.completed}" :todo="todo" />
</ul>
</div>
</template>
<script>
import TodoItem from "./TodoItem.vue";
export default {
name: 'List',
components: { TodoItem },
props: ['todos']
}
</script>
In TodoItem.vue:
<template>
<li>
<input type="checkbox" :checked="todo.completed" #change="toggleTodo(todo)" />
<span class="title">{{todo.title}}</span>
<button #click="deleteTodo(todo.id)">
<i class="fa fa-trash" aria-hidden="true"></i>
</button>
</li>
</template>
<script>
export default {
name: 'TodoItem',
props: ['todo']
}
</script>
The todo list's <h1> heading is displayed, but the unordered list of todos is not.
Introducing props: ['todos'] in App.vue throws the error Duplicated key 'todos'.
What am I missing?

On your App.vue you defined props['todos'] and at the same time todos is defined in your data(). That might be the one causing the error.

Looks like you forgot to define dataIsLoaded in List.vue. It either needs to be defined in data or be a computed property.

Related

Vue 3 components not importing other vue components

My Vue component navbar.vue does not import other components.
This is Vue 3 with the Option API.
All of the components are imported by index.js in the component folder
import navbar from '#/components/navbar.vue'
import btn from '#/components/btn.vue'
export {
navbar,
btn
}
This is the navbar.vue:
<template>
<header class="navbar">
<ul>
<li>
<router-link to="/">Home</router-link>
</li>
<li>
<btn></btn>
</li>
</ul>
</header>
</template>
<script>
import { btn } from '#/components'
export default {
name: 'navbar',
components: {
btn
}
}
</script>
This is the btn.vue components:
<template>
<button class="btn" #click="$emit('click')">
<slot/>
</button>
</template>
<script>
export default {
name: 'btn'
}
</script>
Your index.js exported navbar and btn. And also you have already exported another component from navbar.vue in same file level. So make a separate folder for navbar and btn or change the export name.
I am new to frameworks. Hope this will work.

communication between components in vuejs. (search field)

well i'm new to vue js and developing an application with a search function.
This is my component where the search results will be rendered.
<script>
import RecipeItem from "../recipe/RecipeItem";
import { baseApiUrl } from "#/global";
import axios from "axios";
import PageTitle from "../template/PageTitle";
export default {
name: "Search",
components: { PageTitle, RecipeItem },
data() {
return {
recipes: [],
recipe: {},
search: '',
}
},
methods: {
getRecipes() {
const url = `${baseApiUrl}/search?search=${this.search}`;
axios(url).then((res) => {
this.recipes = res.data;
});
}
},
watch: {
search() {
const route = {
name: 'searchRecipes'
}
if(this.search !== '') {
route.query = {
search: this.search
}
}
},
'$route.query.search': {
immediate: true,
handler(value) {
this.search = value
}
}
},
};
</script>
<template>
<div class="recipes-by-category">
<form class="search">
<router-link :to="{ path: '/search', query: { search: search }}">
<input v-model="search" #keyup.enter="getRecipes()" placeholder="Search recipe" />
<button type="submit">
<font-icon class="icon" :icon="['fas', 'search']"></font-icon>
</button>
</router-link>
</form>
<div class="result-search">
<ul>
<li v-for="(recipe, i) in recipes" :key="i">
<RecipeItem :recipe="recipe" />
</li>
</ul>
</div>
</div>
</template>
ok so far it does what it should do, searches and prints the result on the screen.
But as you can see I created the search field inside it and I want to take it out of the search result component and insert it in my header component which makes more sense for it to be there.
but I'm not able to render the result in my search result component with my search field in the header component.
but I'm not able to render the result in my search result component with my search field in the header component.
Header component
<template>
<header class="header">
<form class="search">
<input v-model="search" #keyup.enter="getRecipes()" placeholder="Search recipe" />
<router-link :to="{ path: '/search', query: { search: this.search }}">
<button type="submit">
<font-icon class="icon" :icon="['fas', 'search']"></font-icon>
</button>
</router-link>
</form>
<div class="menu">
<ul class="menu-links">
<div class="item-home">
<li><router-link to="/">Home</router-link></li>
</div>
<div class="item-recipe">
<li>
<router-link to="/"
>Recipes
<font-icon class="icon" :icon="['fa', 'chevron-down']"></font-icon>
</router-link>
<Dropdown class="mega-menu" title="Recipes" />
</li>
</div>
<div class="item-login">
<li>
<router-link to="/auth" v-if="hideUserDropdown">Login</router-link>
<Userdropdown class="user" v-if="!hideUserDropdown" />
</li>
</div>
</ul>
</div>
</header>
</template>
Result component
<template>
<div class="recipes-by-category">
<div class="result-search">
<ul>
<li v-for="(recipe, i) in recipes" :key="i">
<RecipeItem :recipe="recipe" />
</li>
</ul>
</div>
</div>
</template>
Keep the state variables in whatever parent component is common to both the header component and the results component. For example, if you have a Layout component something like this:
<!-- this is the layout component -->
<template>
<HeaderWithSearch v-on:newResults="someFuncToUpdateState" />
<ResultsComponent v-bind:results="resultsState" />
</template>
<!-- state and function to update state are in a script here... -->
When the search bar returns results you need to pass that data "up" to the parent component with an $emit call, then the parent component can then pass that state back down to the results component using normal props.
Check out this documentation: https://v2.vuejs.org/v2/guide/components-custom-events.html
Be sure to pay special attention to the .sync part of the documentation and determine if that's something you need to implement as well.
Unless you want to use a more complicated state management library like vuex (which shouldn't be necessary in this case) you can just keep state in a common parent and use $emit to pass up and props to pass down.

BlogCards Component doesn't show up on vue

I have a web app which shows the blog posts in a grid. But the BlogCards component in the Home.vue just outputs nothing, whereas it should output the blogs in a grid format. All the datas are stored in firebase. If I go to /blogs, I can see the blogs in grid format, but it doesn't work on the Home.vue. It also spits out the Vue Warn: property or method "blogPostsCards" is not defined on the instance but referenced during render.
I took this code from this tutorial at 5:31:05 minute mark.
Any solution to this problem.
Home.vue
<template>
<div class="home">
<BlogPost :post="post" v-for="(post, index) in blogPostsFeed" :key="index" />
<div class="blog-card-wrap">
<div class="container">
<h3>View more recent blogs</h3>
<div class="blog-cards">
<BlogCards :post="post" v-for="(post, index) in blogPostsCard" :key="index" />
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import BlogPost from '../components/BlogPost.vue'
import BlogCards from '../components/BlogCards.vue'
export default {
name: "Home",
components: {
BlogPost,
BlogCards,
Arrow
},
computed : {
blogPostsCards() {
return this.$store.getters.blogPostsCards;
},
blogPostsFeed() {
return this.$store.getters.blogPostsFeed;
},
}
};
</script>
BlogCards.vue
<template>
<div class="blog-card">
<img :src="post.blogCoverPhoto" alt="">
<div class="info">
<h4>{{ post.blogTitle }}</h4>
<h6>Posted on: {{ new Date(post.blogDate).toLocaleString('en-us', {dateStyle: "long"})}}</h6>
<router-link class="link" to="#" >
View Post <Arrow class="arrow" />
</router-link>
</div>
</div>
</template>
<script>
export default {
name: "blogCard",
props: ["post"],
computed: {
editPost() {
return this.$store.state.editPost
},
}
}
</script>
And getter function in store/index.js
getters:{
blogPostsFeed(state){
return state.blogPosts.slice(0,2);
},
blogPostsCards(state) {
return state.blogPosts.slice(2,6);
},
},
<BlogCards :post="post" v-for="(post, index) in blogPostsCard" :key="index" />
In your Home.vue >> change blogPostsCard to blogPostsCards because you use blogPostsCards in your computed so it gives you that error.

How to show newly data added?

I want to ask why the newly added data does not display in the template, but it does show the newly added data in using console.log (see the ViewPost.vue).
This is the result after I add new post: result.png. Someone knows how to achieve this?
Here is my parent component:
<template>
<section>
//more codes here
<ViewPost v-for="data in postData" :key="data.post_id" :data="data" />
</section>
</template>
<script>
import { mapState } from 'vuex';
export default {
components: {ViewPost},
computed: {
...mapState({
postData: state => state.post.datas,
}),
}
//more codes here
};
</script>
And below is the ViewPost.vue
<template>
<span>
<div class="card mb-10">
<h1>body</h1>
{{ data.post_body }}
</div>
</span>
</template>
<script>
export default {
props: {
data: {},
},
created() {
console.log(this.data);
},
};
index as a key may work fine.
<template>
<section>
//more codes here
<ViewPost v-for="(data,index) in postData" :key="index" :data="data" />
</section>
</template>
<script>
import { mapState } from 'vuex';
export default {
components: {ViewPost},
computed: {
...mapState({
postData: state => state.post.datas,
}),
}
//more codes here
};
</script>
ViewPost.vue
<template>
<span>
<div class="card mb-10">
<h1>body</h1>
{{ data.post_body || data.body }}
</div>
</span>
</template>
<script>
export default {
props: ['data']
created() {
console.log(this.data);
},
};

Vue.js: List does not update when using child component

I'm having an issue when using a child component, a list does not update based on a prop passed into it.
If the comments array data changes, the list will not update when it uses a child component <comment></comment>.
TopHeader template:
<template>
<ul v-for="comment in comments">
// If don't use a child component, it updates whenever `comments` array changes:
<li>
<div>
/r/{{comment.data.subreddit}} ·
{{comment.data.score}}
</div>
<div class="comment" v-html="comment.data.body"></div>
<hr>
</li>
</ul>
</template>
TopHeader component:
import Comment from 'components/Comment'
export default {
name: 'top-header',
components: {
Comment
},
data () {
return {
username: '',
comments: []
}
},
methods: {
fetchData: function(username){
var vm = this;
this.$http.get(`https://www.reddit.com/user/${username}/comments.json?jsonp=`)
.then(function(response){
vm.$set(vm, 'comments', response.body.data.children);
});
}
}
}
However, if I use a child component it does not update.
Modified TopHeader template:
<template>
<ul v-for="comment in comments">
// If I instead use a component with prop data, it does not update
<comment :data="comment.data"></comment>
</ul>
</template>
Comment child template:
<template>
<li>
<div>
/r/{{subreddit}} ·
{{score}}
</div>
<div class="comment" v-html="body"></div>
<hr>
</li>
</template>
Comment child component:
export default {
name: 'comment',
props: ['data'],
data() {
return {
body: this.data.body,
subreddit: this.data.subreddit,
score: this.data.score,
}
}
}
Move the v-for statement to the component <comment>. With the v-for on the ul-tag you would repeat the ul tag. See http://codepen.io/tuelsch/pen/YNOqYR again for an example.
<div id="app">
<button v-on:click="fetch">Fetch data</button>
<ul>
<comment v-for="comment in comments" v-bind:comment="comment" />
</ul>
</div>

Categories