nuxt content link using slug not found - javascript

I am trying nuxt content module. I created a directory called "articles" inside content folder cotaining 2 md files with some dummy data. then, inside pages I have a folder called "tutorials" which contains "_slug.vue" and index.vue
the index page shows only the title of the articles and it works fine. every title links to
the actual article using path/slug. the problem is that I get page not found. here is my code:
index.vue:
<template>
<div>
<article v-for="article of articles" :key="article.id">
<nuxt-link :to="`/tutorials/${article.slug}`">
{{ article.slug }}
</nuxt-link>
</article>
</div>
</template>
<script>
export default {
async asyncData({ $content, params }) {
const articles = await $content('articles', params.slug)
.only(['title', 'slug'])
.sortBy('createdAt', 'asc')
.fetch()
return {
articles,
}
},
}
</script>
_slug.vue:
<template>
<div>
<nuxt-content :document="article" />
</div>
</template>
<script>
export default {
async asyncData({ $content, params }) {
const article = await $content('articles', params.slug).fetch()
return {
article,
}
},
}
</script>
<style></style>
thank you.

Related

When moving between Dynamic Pages with NuxtLink, the data of store cannot be retrieved

A demo is provided below.
stackblitz
When I move from the top page to a post page, the correct content is displayed, but when I move from a post page to another post page, the correct content is not displayed.
However, reloading will display the correct content.
Could you please show us how to display the correct content after transitioning from one posting page to another?
The code for the submission page is as follows.
// pages/post/_id.vue
<template>
<div></div>
</template>
<script>
import { fetchPosts } from '../../lib/post';
export default {
name: 'Post',
layout: 'post/index',
async asyncData({ route, store }) {
const posts = await fetchPosts();
const post = posts.find(({ id }) => id === route.params.id);
store.dispatch('setPost', post);
store.dispatch('setPosts', posts);
},
};
</script>
// layouts/post/index.vue
<template>
<div>
<h1 v-if="post">{{ post.title }}</h1>
<p v-if="post">{{ post.title }} page</p>
<ul>
<li v-for="post in posts" :key="post.id">
<NuxtLink :to="'/post/' + post.id">
{{ post.title }}
</NuxtLink>
</li>
</ul>
<NuxtLink to="/">Top</NuxtLink>
</div>
</template>
<script>
export default {
data() {
return {
post: null,
posts: [],
};
},
created() {
this.post = this.$store.getters['post'].post;
this.posts = this.$store.getters['posts'].posts;
},
};
</script>
The process flow is as follows
pages retrieves data from the server and dispatches it to the store
laytous retrieves data from the store and displays the data in laytous
I know that the use of pages and layouts is not common, but the project I am currently working on specifies this usage and I cannot change this usage.
That's because the layout was already rendered when the route changes (when the route changes, the layout/component used is already created, so the hook created is not called again), a simple fix would be to add a watch for the route:
...
watch: {
'$route': function (value) {
this.post = this.$store.getters['post'].post;
this.posts = this.$store.getters['posts'].posts;
}
},
...

AsyncData not being called in nuxt

I want to set category data at asyncData() hook. But MainHeader Page Component never calls asyncData even if it is placed in a page. Can you explain why MainHeader Page Component does not call asyncData?
MainHeader is placed inside "com" folder which is placed on pages (/pages/com/MainHeader)
<template>
<div>
<header-nav :cateList="cateList"/>
</div>
</template>
<script>
import HeaderNav from '~/components/com/nav/HeaderNav.vue';
import CateApi from "~/util/api/category/cate-api";
export default {
components: {HeaderNav},
async asyncData(){
const cateList = await CateApi.getDispCateList();
return{
cateList,
}
},
data() {
return {
cateList: [],
}
},
}
</script>
default
(/layouts/default)
<template>
<div>
<main-header/>
<Nuxt/>
</div>
</template>
<script>
import MainHeader from "~/pages/com/MainHeader.vue"
export default {
components :{
MainHeader,
},
name: "defaultLayout"
}
</script>
You're probably reaching your page directly, something like /com/test-page I guess, there you will get first initial result on the server (you can check, you'll be getting a console.log in there), which is legit because this is how Nuxt works (server-side first then client-side).
Please follow the convention of naming your pages like my-cool-page and not myCoolPage and also keep in mind that asyncData works only inside of pages.
Your project is working perfectly fine, as an example, create the following file /pages/com/main-header.vue
<template>
<div>
<p> main header page</p>
<header-nav :cate-list="cateList" />
</div>
</template>
<script>
import HeaderNav from '~/components/com/nav/HeaderNav.vue';
export default {
components: { HeaderNav },
async asyncData() {
console.log("check your server if accessing this page directly, otherwise you'll see this one in your browser if client-side navigation")
const response = await fetch('https://jsonplaceholder.typicode.com/todos')
const cateList = await response.json()
return { cateList }
},
}
</script>

vue.js post list not updating after form submission

In my vue app I have two components one which is a form that posts the form data to my api. And the other gets and displays these posts in a section on the page. My issue is that when I submit a new post the posts lists aren't updated. The data stays the same unless I refresh the page. How can I get my posts list to update when I submit the form?
My Code:
client/src/App.vue
<template>
<div id="app">
<MainHeader :modalVisability="modal" v-on:showModal="toggleModal" />
<div id="content_wrap">
<Summary />
</div>
<OppForm :modalVisability="modal" />
</div>
</template>
<script>
import MainHeader from './components/MainHeader.vue';
import OppForm from './components/oppForm.vue';
import Summary from './components/Summary.vue';
export default {
name: 'App',
components: {
MainHeader,
Summary,
OppForm
},
data () {
return {
modal: false
}
},
methods: {
toggleModal (modalBool) {
this.modal = modalBool;
}
}
}
</script>
client/src/components/oppForm.vue
<template>
<div id="opp_form_modal" >
<form #submit.prevent="SubmitOpp" v-if="modalVisability">
<input type="text" name="company_name" v-model="company_name">
<button type="submit">Submit</button>
</form>
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'oppForm',
props: {
modalVisability: Boolean,
},
data () {
return {
company_name: ''
}
},
methods: {
SubmitOpp () {
axios.post('http://localhost:5000/', {
company_name: this.company_name,
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
}
}
}
</script>
client/src/components/Summary.vue
<template>
<div id="summary_section">
<h2>Summary</h2>
<div id="summary_board">
<div class="column">
<div class="head">
<h3>Opportunities</h3>
</div>
<div class="body">
<div class="post"
v-for="(post, index) in posts"
:key="index"
>
<p class="company">{{ post.company_name }}</p>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import axios from 'axios';
export default {
data() {
return{
posts: []
};
},
created() {
axios.get('http://localhost:5000/')
.then(res => {
// console.log(res);
const data = res.data;
this.posts = data;
})
.catch(error => console.log(error));
}
}
</script>
The problem is that you're actually fetching your posts only on the app creation (i.e. inside the created() method).
You should wrap your axios call inside a function updatePosts() and then call it whenever you add a new post successfully, or you could create a custom event that is triggered whenever a new post is added.
created() is called only once (see vue lifecycle) so you fetch API before submitting form.
Try to add some console.log to understand what is called when.
You could use an global event bus and send form value as event data to summary. I could imagine also a solution where event is used to "tell" summary that form was submitted (just boolean, not data itself). In summary you then call API each time you receive event.
Or simple add an "update" button to summary to manually call API.
See Communication between sibling components in VueJs 2.0
or global vue instance for events for detailed examples.

VueJS: render new component after click

I'd like to render new component in vue.js as if it's new page.
I'm trying to do it with something called "dynamic component"
parent: Post.vue
child: Detail.vue
so, if one of the posts is clicked, Post is off and Detail is on.
The thing is I have to send clicked post's id to the Detail.
Here's some of my code.
Post.vue
<template>
<div>
<div v-if="loading">
loading...
</div>
<div v-else class="container">
<ul>
<li v-for="(post, index) in filteredPosts" v-bind:key="post.no">
<section class="post__main">
<div #click..?? class="main__title">{{post.title}}</div>
</section>
</li>
</ul>
</div>
</div>
</template>
<script>
created() {
axios.get(this.url, {
params: {
page: this.page,
ord: this.ord,
category: []
}
}).then(res => {
this.posts = res.data.list;
this.loading = false;
this.page++;
});
Detail.vue
<template>
<div>
{{contents}}
</div>
</template>
<script>
import axios from 'axios';
export default {
name: 'Datail',
data() {
return {
req_no: null,
contents: {}
}
},
created() {
axios.get(url, {
params: {
req_no: this.req_no
}
}).then(res => {
this.contents = this.res.data
});
}
}
</script>
I feel like I can do it with props and v-if.
Can someone help me? Thanks!
Once a post is clicked, pass post id to the click handler. In the click handler, route to detail.vue passing post id as route param.
like below:
<li v-for="(post, index) in filteredPosts" v-bind:key="post.no">
<section class="post__main">
<div #click="onPostClick(post.id)" class="main__title">{{post.title}}</div>
</section>
</li>
And in your click handler:
onPostClick(id: number) {
this.$router.push({
name: 'details',
params: {
id
}
});
}
This will work provided you set up vue router correctly in your app and have a valid route for details.
You can access the post id in details component as follows:
created() {
this.postId = this.$route.params.id;
}
I would take a look at the <component> which takes a prop :to and renders a component, this is good for something like tabs where you are rendering different component from a the same general location on the page without reloading the whole page. See here:
https://v2.vuejs.org/v2/guide/components-dynamic-async.html
This seems to be a very good use case for you, just pass into the component the props you need.

vuejs - How to pass variable to main js file and then iterate through?

I'm working in Laravel. What I want to do is to get the posts from the server and show them in view, however before that I wanted to set static array containing sample posts but my problem is that I can't access posts variable in app.js file when passing by data(){...} and receiving by props: ['posts']. When I even console.log(this.posts) in app.js file it returns undefined. I'm confused what the problem is:
My resources/js/app.js file:
window.Vue = require('vue');
window.App = require('./App.vue');
window.addEventListener('load', function ()
{
const app = new Vue({
el: '#app',
render: h => h(App)
});
});
My resources/js/App.vue file:
<template>
<div>
<app-head></app-head>
<app-post v-for="post in posts" :key="post.id"></app-post>
<app-foot></app-foot>
</div>
</template>
<script>
import Head from './components/Head.vue';
import Foot from './components/Foot.vue';
import Post from './components/Post.vue';
export default {
props: ['posts'],
components: {
'app-head': Head,
'app-post': Post,
'app-foot': Foot,
}
}
</script>
My resources/js/components/Post.vue file:
<template>
<div class="post">
// The post content...
</div>
</template>
<script>
export default {
data() {
return {
posts: [
{id: 1, title: 'Hello :)'}
]
}
}
}
</script>
Few alterations are required.
App.vue
<template>
<div>
<app-head></app-head>
<app-post :posts="posts"></app-post>
<app-foot></app-foot>
</div>
</template>
<script>
import Head from './components/Head.vue';
import Foot from './components/Foot.vue';
import Post from './components/Post.vue';
export default {
components: {
'app-head': Head,
'app-post': Post,
'app-foot': Foot,
},
data() {
return {
posts: [
{id: 1, title: 'Hello :)'}
]
}
}
}
</script>
If you need to prop the posts list to the component then it has to come from here. Hence, notice data is modified (or inserted).
Post.vue
<template>
<div class="post" v-for="post in posts" :key="post.id">
// The post content...
</div>
</template>
<script>
export default {
props: ['posts']
}
</script>
Notice that v-for should be in the post component.
Let me know if that works for you.
You should use props in your component(Post) the receive the data from parent component(App).

Categories