Nuxt: Dynamic og:image when generating static files - vue.js

I am working on my first Nuxt project. I am trying to dynamically set the og:image URL by taking images of a post. In principle I am able to achieve this with the following code in the script
export default {
head() {
return {
title: this.post.title,
meta: [
{
hid: "description",
name: "description",
content: this.post.description,
},
{ hid: "og:title", property: "og:title", content: this.post.title },
{
hid: "og:description",
property: "og:description",
content: this.deck.description,
},
{ hid: "og:type", property: "og:type", content: "article" },
{
hid: "og:image",
property: "og:image",
content:
"https:[website]" +
require(`#/content${this.slug.dir}/images/preview.png`),
},
{
hid: "og:url",
property: "og:url",
content:
"https:[website]" + this.$route.fullPath,
},
],
};
},
async asyncData({ $content, params }) {
...
},
};
</script>
I notice the following:
On the browser page, when I open view source, these tags are not shown. However, the inspector shows the meta tags in the head .
This is as expected because, the view source is the base HTML head that is served from the backend, while the meta tags, the Nuxt JS must be dynamically adding on load.
I think we type the URL in a social media post like Facebook, WhatsApp, Twitter, LinkedIn, the manner it works is that only the head portion of the page is retrieved and the og parameters are retrieved to display the image. But the JS script is not executed.
And that is why I get a missing image error or a blank thumbnail.
The nuxt.config.js is as below
// nuxt.config.js
export default {
...
target: 'static',
...
ssr: false, // removing this makes no difference
...
}
Question is: how can I get Nuxt to add the meta tags in the generated HTML? Thanks

Related

How to modify alternate links during SSR using nuxt and Vue i18n

I want to modify the href attribute(to remove query string) for specific pages
like screenshot below:
or maybe entirely remove the link element for specific pages
How can I archive that?
With nuxt, you just have to specify a head function that defines these links:
export default {
head() {
return {
link: [
{ rel: 'alternate', hreflang: 'zh', href: 'http://my-main-alternate-link' },
{ rel: 'alternate', hreflang: 'en', href: 'http://my-english-alternate-link' }
]
}
}
}
You'll have to manually specify all the links for all languages I'm afraid.

How to integrate vue.config.js

I created a Vue project using Vue CLI 3 using all the default preset options. When I run the app in Chrome using vue serve, I noticed that the elements inside the head element don't match the head elements inside /public/index.html at all. I read that I could add the meta elements I want inside vue.config.js, but when I go to run the app it doesn't seem to use that file. My vue.config.js looks like this:
module.exports = {
chainWebpack: config => {
config
.plugin('html')
.tap(args => {
args[0].title = 'MyApp title';
args[0].meta = {viewport: 'width=device-width,initial-scale=1,user-scalable=no'};
return args;
})
}
}
And the HTMLWebpackPlugin portion of vue inspect looks like this:
/* config.plugin('html') */
new HtmlWebpackPlugin(
{
templateParameters: function () { /* omitted long function */ },
template: '/*path-to-project-root*/node_modules/#vue/cli-service/lib/config/index-default.html'
}
)
...
...which looks like it isn't even pointing to /public/index.html.
I don't really know where to look next, any help would be appreciated.
You can set meta data from within the page components
<script>
head () {
return {
title: 'my title,
meta: [
{ hid: 'description', name: 'description', content: 'my description' }
]
}
</script>

Run nuxt on https locally – problem with nuxt.config.js

I am trying to run nuxt locally with https to test some geolocation stuff.
(https://nuxtjs.org/, https://nuxtjs.org/api/nuxt)
I was following this tutorial:
https://www.mattshull.com/blog/https-on-localhost
And then I found this:
https://github.com/nuxt/nuxt.js/issues/146
Both links seem to describe pretty nicely how to run nuxt with server.js programmatically.
The thing is that in my nuxt.config.js I seem to have some problems.
I get the following error when runnung yarn dev:
/Users/USER/Documents/github/mynuxtrepo/nuxt.config.js:2
import { module } from 'npmmodule'
> SyntaxError: Unexpected token {
In my nuxt config I import a custom helper to generate localized routes. Not really important what it does but obviously it can't handle the import syntax.
I assume that the node version does not understand.
So how can I get it to run? Do I have to require everything instead of importing?
Or is my assumption wrong and the cause lies somewhere totally different?
Thank you for your help
Cheers.
------
Edit 1:
My nuxt config looks like this:
// eslint-disable-next-line prettier/prettier
import { generateLocalizedRoutes, generateRoutesFromData } from 'vuecid-helpers'
import config from './config'
// TODO: Add your post types
const postTypes = [{ type: 'pages' }, { type: 'posts', paginated: true }]
// TODO: Add your site title
const siteTitle = 'Title'
const shortTitle = 'short title'
const siteDescription = 'Page demonstrated with a wonderful example'
const themeColor = '#ffffff'
// TODO: Replace favicon source file in /static/icon.png (512px x 512px)
// eslint-disable-next-line prettier/prettier
const iconSizes = [32, 57, 60, 72, 76, 144, 120, 144, 152, 167, 180, 192, 512]
module.exports = {
mode: 'universal',
/*
** Headers of the page
*/
head: {
title: 'Loading…',
htmlAttrs: {
lang: config.env.DEFAULTLANG,
dir: 'ltr' // define directionality of text globally
},
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
// TODO: Check this info
{ name: 'author', content: 'Author' },
{ name: 'theme-color', content: themeColor },
// TODO: Add or remove google-site-verification
{
name: 'google-site-verification',
content: '...W1GdU'
}
],
link: []
},
/*
** env: lets you create environment variables that will be shared for the client and server-side.
*/
env: config.env,
/*
** Customize the progress-bar color
** TODO: Set your desired loading bar color
*/
loading: { color: '#0000ff' },
/*
** CSS
*/
css: ['#/assets/css/main.scss'],
/*
** Plugins
*/
plugins: [
{ src: '~/plugins/global.js' },
{ src: '~/plugins/throwNuxtError.js' },
{ src: '~/plugins/axios' },
{ src: '~/plugins/whatinput.js', ssr: false },
{ src: '~/plugins/i18n.js', injectAs: 'i18n' },
{ src: '~/plugins/vuex-router-sync' },
{ src: '~/plugins/vue-bows' },
{ src: '~/plugins/vue-breakpoint-component', ssr: false }
],
/*
** Modules
*/
modules: [
'#nuxtjs/axios',
'#nuxtjs/sitemap',
[
'#nuxtjs/pwa',
{
icon: {
sizes: iconSizes
},
// Override certain meta tags
meta: {
viewport: 'width=device-width, initial-scale=1',
favicon: true // Generates only apple-touch-icon
},
manifest: {
name: siteTitle,
lang: config.env.DEFAULTLANG,
dir: 'ltr',
short_name: shortTitle,
theme_color: themeColor,
start_url: '/',
display: 'standalone',
background_color: '#fff',
description: siteDescription
}
}
]
],
/*
** Workbox config
*/
workbox: {
config: {
debug: false,
cacheId: siteTitle
}
},
/*
** Axios config
*/
axios: {
baseURL: '/'
},
/*
** Generate
*/
generate: {
subFolders: true,
routes: [
...generateRoutesFromData({
langs: config.env.LANGS,
postTypes: postTypes,
dataPath: '../../../../../static/data',
bundle: 'basic',
homeSlug: config.env.HOMESLUG,
errorPrefix: config.env.ERROR_PREFIX
})
]
},
/*
** Build configuration
*/
build: {
extend(config, { isDev, isClient }) {
/*
** Run ESLINT on save
*/
if (isDev && isClient) {
config.module.rules.push({
enforce: 'pre',
test: /\.(js|vue)$/,
loader: 'eslint-loader',
exclude: /(node_modules)/
})
}
}
},
/*
** Router
*/
router: {
linkActiveClass: 'is-active',
linkExactActiveClass: 'is-active-exact',
middleware: ['i18n'],
extendRoutes(routes) {
// extends basic routes (based on your files/folders in pages directory) with i18n locales (from our config.js)
const newRoutes = generateLocalizedRoutes({
baseRoutes: routes,
defaultLang: config.env.DEFAULTLANG,
langs: config.env.LANGS,
routesAliases: config.routesAliases
})
// Clear array
routes.splice(0, routes.length)
// Push newly created routes
routes.push(...newRoutes)
}
},
/*
** Sitemap Configuration
*/
sitemap: {
path: '/sitemap.xml',
hostname: config.env.FRONTENDURLPRODUCTION,
cacheTime: 1000 * 60 * 15,
generate: true,
routes: [
...generateRoutesFromData({
langs: config.env.LANGS,
postTypes: postTypes,
dataPath: '../../../../../static/data',
bundle: 'basic',
homeSlug: config.env.HOMESLUG,
errorPrefix: config.env.ERROR_PREFIX
})
]
}
}
You can see that the generateLocalizedRoutes and the generateRoutesFromData methods are used to generate localized routes and is also taking post json files to generate routes from data (:slug).
--------- Edit 2:
I got it to run eventually.
I had to require all parts within the nuxt.config.js instead of importing them. I also resolved issues with the certificates. So I thought it was all cool 🚀.
BUT!!! 🚧:
Then I found out that I had my config file used within my post template.
So I thought I would also require the file within my template:
Like const config = require('~/config').
But then I would get this error:
[nuxt] Error while initializing app TypeError: ""exports" is read-only"
After some research, I found that is probably a thing when using common.js require and module.exports together with ES6 import/export within my project. (Probably linked to: https://github.com/FranckFreiburger/vue-pdf/issues/1).
So how could I still use my config.js when running nuxt programmatically (with require) and then also within my app?
I am glad to hear any ideas on this...
Cheers
Well, just to close this:
My problem resulted from running nuxt as a node app, which does not understand ES6 import statements, which appeared in my nuxt config.
So I had to rewrite things to work with commons.js (require).
This works for now.
(I also tried to run babel-node when starting the server.js, but had no success. Does not mean this did not work, I just wasn't keen on trying harder).
Thanks for the comments.
cheers

vue-meta => How to change the header information

I have a nuxtjs project with a page opening on a url like server\posts\id. On this page I have added head information to influence the meta tags. However, some tags are post specific and need to be filled dynamically. This only seems possible after you have data loaded in mounted. How can I add the meta maniplulation to mounted?
It seems you need an extra 'data' property. If you use this in the header, and update it later it will change the meta information.
Right way to get meta from api is: using fetch method
async fetch({ store, params }) {
await store.dispatch('modules/item/get_item', params.article)
},
Use Computed :
computed: {
...mapState('modules/item', {
Item: (state) => state.data
})
},
and use nuxt head (vue-meta)
head() {
return {
title:
this.$store.state.modules.general.info.name + ' / ' + this.Item.title,
meta: [
{
hid: 'description',
name: 'description',
content:
this.$store.state.modules.general.info.name +
' / ' +
this.Item.seo_description
},
}

When using the Custom HTML app, is it possible to display custom portfolio fields in a rally grid?

My goal is to display some custom porfolio fields (the field is called executive champion) in a custon HTML app using a rally grid on the dashboard. I have reviewed the docs and some examples and it does not look like this is possible. Has anyone accomplished this and how? My code is as follows. I used the following to get started https://github.com/davidpthomas/BasicRallyGrid.
<!DOCTYPE html>
<html>
<head>
<title>BasicRallyGrid</title>
<script type="text/javascript" src="/apps/2.0rc2/sdk.js"></script>
<script type="text/javascript">
Rally.onReady(function () {
// Custom Rally App that displays Stories in a grid.
//
// Note: various console debugging messages intentionally kept in the code for learning purposes
Ext.define('CustomApp', {
extend: 'Rally.app.App', // The parent class manages the app 'lifecycle' and calls launch() when ready
componentCls: 'app', // CSS styles found in app.css
// Entry Point to App
launch: function() {
console.log('our first app'); // see console api: https://developers.google.com/chrome-developer-tools/docs/console-api
this._loadData(); // we need to prefix with 'this.' so we call a method found at the app level.
},
// Get data from Rally
_loadData: function() {
var myStore = Ext.create('Rally.data.wsapi.Store', {
model: 'PortfolioItem',
autoLoad: true, // <----- Don't forget to set this to true! heh
listeners: {
load: function(myStore, myData, success) {
console.log('got data!', myStore, myData);
this._loadGrid(myStore); // if we did NOT pass scope:this below, this line would be incorrectly trying to call _createGrid() on the store which does not exist.
},
scope: this // This tells the wsapi data store to forward pass along the app-level context into ALL listener functions
},
fetch: ['Executive Champion', 'Name', 'ScheduleState'] // Look in the WSAPI docs online to see all fields available!
});
},
// Create and Show a Grid of given stories
_loadGrid: function(myStoryStore) {
var myGrid = Ext.create('Rally.ui.grid.Grid', {
store: myStoryStore,
columnCfgs: [
'Executive Champion', 'Name', 'ScheduleState'
]
});
this.add(myGrid);
console.log('what is this?', this);
}
});
Rally.launchApp('CustomApp', {
name:"BasicRallyGrid",
parentRepos:""
});
});
</script>
<style type="text/css">
.app {
/* Add app styles here */
}
</style>
</head>
<body></body>
</html>
It is certainly possible to display PI custom field values in a grid.
Even if your custom field has a space in the DisplayName, remove the space from 'Executive Champion' in your code.
Here is an example. I have a custom field on portfolio item object that is shown in the Setup>Workspaces & Projects as follows:
Name: PiCustom
DisplayName: PiCustom
Workspace admin rights are required to see that page.
WS API references this field as c_PiCustom (with c_ prepended automatically to indicate a custom field)
In my code either one works fine:
PiCustom, c_PiCustom
If a workspace admin changes this custom field's DisplayName to include a space as follows:
Name: PiCustom
DisplayName: Pi Custom
It will still be displayed as c_PiCustom in WS API
and either one of those two ways to reference this field in my code will still work:
PiCustom,c_PiCustom
but Pi Custom will not work.
Remove the space in your code. Or check exact spelling of that field in the WS API and follow that convention.
Here is a full example using 2.0rc3:
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
launch: function() {
this.add({
xtype: 'rallygrid',
model: 'PortfolioItem/Feature',
enableRanking: true,
storeConfig: {
context: {
context: this.getContext().getDataContext()
}
},
{
dataIndex: 'FormattedID'
},{
dataIndex: 'Name',
},
{
dataIndex: 'PiCustom'
}
]
});
}
});