gregives/nuxt-markdown

Accessing markdown frontmatter attributes inside the head

Opened this issue · 6 comments

Can you advise on how you would be able to get the frontmatter data inside the head element?

export default {
  asyncData({ app }) {
    return {
      aboutPage: app.$markdown.about
    };
  },
  data() {
    return {
      aboutPageContent: null
    };
  },
  created() {
    this.aboutPageContent = () => this.$markdown.loadContent();
  },
  head() {
    return {
      title: <access data here>
    };
  }
};

So this might link into why I asked about single pages.

Doing the above I can't access all properties.

So with the above script section this works <h2>{{ aboutPage.title }}</h2> but this doesn't <cover :thumbnail="aboutPage.hero" />

If I look in the generate nuxt payload I can see that hero is in there but I can't seem to access it using aboutPage.hero

{aboutPage:{title:"About",hero:"about\u002Fcarl-newton-1JcEl81di6Y-unsplash.jpg",file:a,path:b,collection:a}}

However...

If I do this (using the same structure for the blog _slug and not the blog index)

export default {
  asyncData({ app }) {
    return {
      aboutPage: app.$markdown.loadData()
    };
  },
  data() {
    return {
      aboutPageContent: null
    };
  },
  created() {
    this.aboutPageContent = () => this.$markdown.loadContent();
  },
  head() {
    return {
      title: this.aboutPage.title
    };
  }
};

I can set the title inside the head and my hero now becomes available via blogPage.hero

So maybe this is the reason to add in a single page type collection? So that we can use aboutPage: app.$markdown.page instead of aboutPage: app.$markdown.loadData() for single pages?

So this works

  asyncData({ app, $dayjs }) {
    const blogPost = app.$markdown.loadData();

    blogPost.date = $dayjs(blogPost.date).format("YYYY");

    return {
      blogPost
    };
  },

but this doesn't

  asyncData({ app, $dayjs }) {
    const blogPosts = app.$markdown.blog

    blogPosts.date = $dayjs(blogPosts.date).format("YYYY");

    return {
      blogPosts
    };
  }

So there's something about the named collection that doesn't seem to allow access to the data. So in the first example that just uses loadData() - I can do this blogPost.date = 'test' and now all my dates say test as expected, but in that second example that uses the named collection I can't transform any of the data meaning blogPost.date = 'test' doesn't work.

You can access the front matter for the current page using loadData() in asyncData and accessing the data you want in head.

<script>
export default {
  asyncData ({ app }) {
    return {
      blogPost: app.$markdown.loadData()
    }
  },
  head () {
    return {
      title: this.blogPost.title
    }
  }
}
</script>

Is there a reason you don't want to use loadData?

Regarding your example which didn't work, this is because blogPosts is an array. Therefore, you are attempting to set the date property of an array.

  asyncData({ app, $dayjs }) {
    const blogPosts = app.$markdown.blog

    blogPosts.date = $dayjs(blogPosts.date).format("YYYY");

    return {
      blogPosts
    };
  }

You may want to do something like this instead:

asyncData({ app, $dayjs }) {
  const blogPosts = app.$markdown.blog

  blogPosts.forEach((blogPost) => {
    blogPost.date = $dayjs(blogPost.date).format("YYYY")
  })

  return {
    blogPosts
  }
}

Ok, cool. The date thing was obviously me being stupid I should have clocked the array output.

So with regards to app.$markdown.loadData()

I have this folder structure

content
- blog
- - posts
- - - post1.md
- - - etc...
- - index.md
pages
- blog
- - posts
- - - _slug.vue
- - tags
- - - _slug.vue
- - index.vue

I have this script in blog/index.vue

  asyncData({ app, $dayjs }) {
    const blogContent = app.$markdown.loadData()
    const blogPosts = app.$markdown.blogposts;

    blogPosts.forEach(blogPost => {
      blogPost.date = $dayjs(blogPost.date).format("YYYY");
    });

    return {
      blogContent,
      blogPosts
    };
  },

And this settings in nuxt

{
                name: 'blogposts',
                directory: 'content/blog/posts',
                includeSubdirectories: false,
                routePrefix: '/blog/post/',
                serverTransform(collection) {
                    collection.forEach(({ content, data }) => {
                        data.mins = Math.round(content.split(' ').length / 250) || 1

                        data.tags = data.tags.map((tag) => {
                            const slug = tag.toLowerCase();
                            return {
                                name: slug,
                                path: `/blog/tag/${slug}/`
                            }
                        })
                    })

                    return collection.sort((a, b) => b.data.date - a.data.date)
                },
                clientTransform() {
                    return function (collection) {
                        collection.forEach((data) => {
                            data.date = new Date(data.date)
                        })

                        return collection
                    }
                }
            }

So I'm pretty much following your example.

When I generate I get this error Error generating route "/blog/": This page could not be found. This is coming from the fact I have an index.md inside the blog folder because if I remove it I don't get the error but I know have no control over the content for the blog list page, only the posts output. If I add the route manually into router I still get the error, I can also see in one of your examples you provide a mock promise, and I don't want to do anything like that, it feels like a hack (also I still get the same error).

So, if I add this to nuxt config

{
                name: 'blogcontent',
                directory: 'content/blog',
                includeSubdirectories: false,
                routePrefix: '/blog/'
            }

I know get no errors, but as you've pointed out already the output of the new blogcontent is now an array - so doing const blogContent = app.$markdown.blogcontent and {{ blogContent.title }} doesn't work because it's an array output.

If i were to leave in the new blogcontent definition in your plugin config and change to const blogContent = app.$markdown.loadData() then it works as expected but I've had to define the route in your plugin config with a named collection that I'm not even using.

Does that all make sense? I have no problem using loadData as it works for the about page great, but because of the routing error with blog, and the way I have had to get around it, maybe something could be done to make it more intuitive.

Okay I see what you mean: the problem is that you have to specify a collection for your blogContent, even though it's just a single page and you won't use the named collection anywhere.

Currently, you have to specify all the directories where your Markdown files are, otherwise, Nuxt Markdown doesn't know about them; however, maybe it's worth Nuxt Markdown automatically finding all of the Markdown files in the content directory and only use collections where you need e.g. for a collection of blog posts or projects. I'll have a think about it, as that would be a pretty major change to how Nuxt Markdown works.

Yeah maybe this just comes back to single page handling /shrug haha