Nuxt.js title and meta content 'undefined' when viewing page source - seo

I am currently setting up titles and meta descriptions in a Nuxt.js application, but have encountered an issue. When I click 'View Page Source' the dynamic titles and meta content I set in the head() shows as 'undefined':
However, when I inspect the page this dynamic content shows as expected:
On each page I've set the title and meta as follows:
head() {
return {
title: this.title + ' | CompanyName',
meta: [
{
hid: 'description',
name: 'description',
content: this.description,
},
],
}
},
Any suggestions as to why this is happening, and how to fix?

I seem to have found a solution. In the above example the this.description and this.title were being populated from an API call in the mounted() function. If this is switched to an API call through asyncData() then when you view the page source, it is no longer undefined.
async asyncData(context) {
return context.$axios.get(`/endpoint`).then(result => {
return {
description: result.data,
}
}).catch(e => {
context.error({ statusCode: 404, message: 'Error' })
})
}

Related

Adding dynamic meta tags fetched from API to nuxtjs static site

I have a static site with Nuxt and content coming in from Strapi. I want to dynamically set the meta tags that are fetched asynchronously.
My site has an index page which passes the fetched data to index-web or index-mobile through props.
let pageMeta: any
const apiBase: string = 'https://strapi.xyz.com'
export default Vue.extend({
components: { Greeting, Showcase, Features, Footer },
props: {
data: Map,
pageMeta,
},
data() {
return {
loading: true,
}
},
metaInfo(): any {
return {
meta: [
{
hid: 'description',
name: 'description',
content: pageMeta.description,
},
{
hid: 'author',
name: 'author',
content: pageMeta.author,
},
],
}
},
})
The prop being passed in a JSON parsed object.
Having done this, the generated site does not have the meta tags added in.
As mentioned, you need to access the property with .this.

vue-meta doesnt display title, content, or schema on page refresh or external link click for dynamic components

When I navigate to my dynamic components trough navigation bar, vue-meta title, content, and schema are displayed correctly, but when I refresh the page or click on the external link I get a value of undefined.
i have stored title content and schema in the json file.
metaInfo() {
return {
title: `${this.seoTitle}`,
meta: [
{name: "robots", content: "index,follow"},
{
name: 'description',
content: `${this.seoContent}`
}
],
link: [
{rel: 'favicon', href: 'logo.ico'}
],
script: [{
type: 'application/ld+json',
json: this.markups
}]
}
},
data() {
return {
seoTitle: this.$route.params.title,
seoContent: this.$route.params.content,
markups:this.$route.params.markup,
}
}
<div class="landing-group-tours box" v-for="tour in boatTours" :key="tour.id">
<router-link
:to="{name: 'details', params:{id: tour.id, title: tour.seoTitle, content: tour.seoContent, markup:tour.markup}}">
</div>
<script>
import tours from '#/data/privateToursData.json'
export default{
data(){
return{
boatTours: tours
{
}
}
</script>
You should store the router parameters in the router itself or in the URL, not the link. What you do is passing objects internally when you click the link, but as you noticed, when you click the browser refresh button these params are gone.
What happens is that Vue will load the app and router, identify what components are responsible for rendering the route and pass the detected parameters from your router to the components. Hence losing any additional data you had in your link before.
Try to keep only the dynamic params in your router and load the rest in the component, based on app logic. I.e. Assuming your route looks like /details/:id, you should initialize the SEO params in your details component.
Typically these come from the backend and for faster access I would transform the array to literal object and access the record by key. I.e. transform the array from:
[
{ "id": 1008; "title": "title1", "content": "....", ... },
{ "id": 1009, "title": "..."... }
]
to
{
"1008": { title: "title1", content: "....", ... },
"1009": { .... }
}
and then store it in VueX (https://vuex.vuejs.org/guide/getters.html)
getters: {
// ...
getBoatTour: (state) => (id) => {
return state.boatTours[id] || { title: 'Not found', content: '......' }
}
}
data() {
const SEOdata = store.getters.getBoatTour(this.$route.params.id);
return {
seoTitle: SEOdata.title,
seoContent: SEOdata.content,
markups: SEOdata.markup,
}
}

Alternate page with proper canonical tag and duplicate URL part errors in Google search console report

I have a static blog site created with Nuxt and hosted on Netlify. For content, I am using markdown files and the new Content module in Nuxt. I used Nuxt and static content, along with vue-meta for SEO purposes, but after a couple months, my coverage report shows multiple errors, and I can't get any pages returned in Google, even when searching on the site name or a specific string of text used on the site.
Here's my setup:
My markdown content is stored in /content/posts
In my blog/index.vue file, this is what I have for my meta tags and data fetching:
<script>
export default {
name: 'Posts',
head() {
return {
title: 'Blog Posts',
meta: [
{
hid: 'description',
name: 'description',
content: 'Example Site blog index'
},
{
hid: 'link',
name: 'link',
content: 'https://example.com/blog'
}
]
}
},
async asyncData({ $content, params }) {
const posts = await $content('posts', params.slug)
.only(['title', 'description', 'publishDate', 'slug'])
.sortBy('publishDate', 'desc')
.fetch()
return {
posts
}
},
};
</script>
In my /blog/_slug/index.vue page, I'm doing almost the same thing.
export default {
name: 'Post',
computed: {
pageConfig() {
return {
identifier: this.post.slug
}
}
},
head() {
return {
title: this.post.title,
meta: [
{
hid: 'description',
name: 'description',
content: this.post.description
},
{
property: "og:site_name",
hid: "og:site_name",
vmid: "og:site_name",
content: "My Site Name"
},
{
property: "og:title",
hid: "og-title",
vmid: "og-title",
content: this.post.title
},
{
property: "og:description",
hid: "og-description",
vmid: "og-description",
content: this.post.description
},
{
property: "og:type",
hid: "og-type",
vmid: "og-type",
content: "article"
},
{
property: "og:url",
hid: "og-url",
vmid: "og-url",
content: 'https://example.com/blog/' + this.post.slug
},
{
hid: "link",
name: "link",
content: 'https://example.com/blog/' + this.post.slug
},
],
link: [
{
rel: 'canonical',
href: 'https://example.com/blog/' + this.post.slug
}
]
}
},
async asyncData({ $content, params }) {
const post = await $content('posts', params.slug).fetch()
return {
post
}
},
}
I'm using this to generate my sitemap in nuxt.config.js
sitemap: {
path: '/sitemap.xml',
hostname: process.env.BASE_URL,
gzip: true,
routes: async () => {
let routes = []
const { $content } = require('#nuxt/content')
let posts = await $content('posts').fetch()
for (const post of posts) {
routes.push(`blog/${post.slug}`)
}
return routes
},
}
and this generates a sitemap at /sitemap.xml with the following structure
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:mobile="http://www.google.com/schemas/sitemap-mobile/1.0" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1">
<url>
<loc>https://example.com/blog/blog-post-1</loc>
</url>
<url>
<loc>https://example.com/blog/blog-post-2</loc>
</url>
<url>
<loc>https://example.com/blog/blog-post-3</loc>
</url>
</urlset>
I submitted my sitemap to Google.
When I look at my Coverage report in Google Search Console, I have two main problems:
A large number of posts listed under Not Found (404) with a URL of https://example.com/blog/blog/my-blog-post (two /blogs in the URL). I've noticed this happens sometimes when I click on a post right after deploying to Netlify. I've looked around the code, and I can't find where I might be adding an extra /blog in the URL.
An equally large number under Alternate page with proper canonical tag. Before I put the canonical tag in my /_slug/index/vue header, I got errors about missing a canonical tag, and now that I put that in, I get this error instead.
What do I need to do to fix these errors and get my content correctly indexed?

Vuetify Data Table Error that resolves if I force rerender

I am having a weird issue with Vuetify data table. I have two files that are relevant:
The parent:
<!-- DataTable component with articles and headers passed as props -->
<ArticleTable :propData="articles" :headers="headers"></ArticleTable>
<script>
//Add data
data() {
return {
//For the article list from the server
articles: null,
//Headers for Datatable
headers: [
{ text: "Article #", value: "articleID" },
{ text: "Title", value: "articleTitle" },
{ text: "Content", value: "articleContent" },
{ text: "Click to Like", value: "liked", sortable: false },
{ text: "Likes", value: "articleLikes" }
]
};
},
//When the component is first created, call method
created() {
this.getArticles();
},
//Methods
methods: {
getArticles() {
this.error = null;
//Sets parameters from external file
let url = serverDetails.url;
let params = { ...serverDetails.params };
//GET request for all articles
axios
.get(`${url}articles`, {
params
})
//On success save response in articles variable
.then(response => {
this.articles = response.data;
console.log("Articles found: ", response.data);
})
//Catch and display any errors
.catch(error => {
this.error = error.toString();
console.log("Error on retriving articles: " + error);
});
}
}
};
Which makes a request to my server and pulls back a list of articles. This is then passed to the child component:
<template>
<div>
<!-- Vuetify data table -->
<v-data-table
:items="propData"
:headers="headers"
:search="search"
:custom-filter="filter"
:key="renderKey"
>
</v-data-table>
<script>
"use strict";
export default {
name: "ArticleTable",
//Add props
props: {
propData: {
type: Array
},
headers: {
//Expects compulsory Array
type: Array,
required: true
}
},
//Add data
data() {
return {
//Render key
renderKey: 0,
};
},
</script>
(I have cut out as much irrelevant material as possible). However I am getting a weird problem with the data table, the console if throwing several errors, all roughly saying some version of:
[Vue warn]: Error in callback for immediate watcher "computedOptions": "TypeError: Cannot read property 'slice' of null"
and
TypeError: Cannot read property 'slice' of null
or
[Vue warn]: Error in getter for watcher "computedItems": "TypeError: Cannot read property 'slice' of null"
Before the table would not render at all but if I add
beforeUpdate() {
this.renderKey += 1;
},
to the child, forcing it to re render the table appears fine.
Is there any way to resolve those errors?
Thanks a million
Vuetify throw this error when the items array passed to datatable is undefined or null. A quick sollution is to set in your data the initial value of articles to [] instead of null.
data() {
return {
//For the article list from the server
articles: [], // this should always be an array, even if empty
//Headers for Datatable
headers: [
{ text: "Article #", value: "articleID" },
{ text: "Title", value: "articleTitle" },
{ text: "Content", value: "articleContent" },
{ text: "Click to Like", value: "liked", sortable: false },
{ text: "Likes", value: "articleLikes" }
]
};
},

How to generate dynamic data table through api call in vuetify in vuejs

I am trying to generate dynamic data table through Vuetify ,in vuejs but dont see any example in vuetify official documentation,does anyone mind sharing an example
By "dynamic" I'm guessing you mean data loads asynchronously from an API. The Vuetify docs have numerous examples of data tables including one that simulates requesting data from an API. A very basic implementation looks something like (note: this example uses the random user generator API:
<template>
<v-data-table
:headers="headers"
:items="people"
item-key="login.uuid"
:loading="loading"
:options.sync="options"
:server-items-length="totalPeople"
#pagination="updatePage"
#update:options="customSort"
></v-data-table>
</template>
<script>
import axios from 'axios'
export default {
data: () => ({
apiURL: 'https://randomuser.me/api/',
headers: [
{ text: 'Name', value: 'name', align: 'start' },
{ text: 'Country', value: 'country' },
{ text: 'DOB (Age)', value: 'dob' },
{ text: 'Contacts', value: 'contacts' },
],
loading: false,
options: {
page: 1,
itemsPerPage: 10,
sortBy: ['name'],
sortDesc: [true],
},
people: [],
}),
mounted () {
this.getPeople()
},
methods: {
async getPeople (page = 1, results = 10, seed = 'example') {
this.loading = true
const params = { params: { page, results, seed } }
try {
this.people = (await axios.get(this.apiURL, params)).data.results
this.loading = false
} catch (error) {
console.log(error)
this.loading = false
}
},
updatePage (pagination) {
const { itemsPerPage: results, page } = pagination
this.pagination = pagination
this.getPeople({ page, results })
},
customSort (options) {
// The Random User API does NOT support sorting, but if it did, you
// would need to make an API call that returned a sorted
// list of results based on the sort parameter(s)
console.log(options)
},
},
}
</script>
For a more complex example that uses more of Vuetify's features, check out this codepen.