Trying to set Vue Meta page title using string + variable - javascript

I'm using Vue Meta as part of a blog application within a project using Nuxt JS 2.4.5
I'm having some trouble trying to set the title + a variable from data () and I'm not sure what I'm missing
I've tried multiple attempts at getting it to work, moving code around, using this setting it manually, nothing seems to work...
<script>
import BlogsFromJson from '~/static/articles/blogs.json';
export default {
head: {
title: 'My Website: Blog: ' + this.myBlogTitle, // or something else
meta: [
{ hid: 'description', name: 'description', content: 'Read the latest news and articles from Flex Repay UK.' }
]
},
data () {
return {
title: this.$route.params.title,
blog: BlogsFromJson,
myBlogTitle: 'some title'
}
}
}
</script>
I've tried setting a variable within data () and using it statically.
Doing this should give me My Website: Blog: some title
What could I be missing here?

Try to use function instead of object for head.
Change
head: {
...
},
to
head () {
return {
...
}
}

Instead of defining metaInfo as an object, define it as a function and access this as usual:
Post.vue:
<template>
<div>
<h1>{{{ title }}}</h1>
</div>
</template>
your script
<script>
export default {
name: 'post',
props: ['title'],
data () {
return {
description: 'A blog post about some stuff'
}
},
metaInfo () {
return {
title: this.title,
meta: [
{ vmid: 'description', name: 'description', content: this.description }
]
}
}
}
</script>
PostContainer.vue:
<template>
<div>
<post :title="title"></post>
</div>
</template>
<script>
import Post from './Post.vue'
export default {
name: 'post-container',
components: { Post },
data () {
return {
title: 'Example blog post'
}
}
}
</script>

metaInfo() {
return {
title: this.pageTitle,
}
}

Related

Multiple NuxtJS og:image Meta Tags

In the specification of Open Graph protocol - Arrays it is documented that multiple versions of the type og:image may be arranged.
We use on components basis this structure in NuxtJS ssr:
export default {
data() {
return {
title: 'Home page'
}
},
head() {
return {
title: this.title,
meta: [
{
hid: 'description',
name: 'description',
content: 'Home page description'
}
// And others...
]
}
}
}
The goal is to have multiple nodes of the tag <meta property="og:image" content="https://example.com/rock.jpg" /> generated by NuxtJS Meta. The thinking is, since these are dynamic pages, to build an array and merge them into the meta array.
export default {
data() {
return {
title: 'Home page'
}
},
head() {
return {
title: this.title,
meta: [
{
hid: 'description',
name: 'description',
content: 'Home page description'
}
// And others...
...this.getMetaOgImages
]
}
},
computed: {
getMetaOgImages() {
const tempArr = []
// this.images comes from map state vuex-store
this.images.forEach(image => {
tempArr.push(
{
hid: 'og:image',
property: 'og:image',
content: image.url
},
{
hid: 'og:image:secure_url',
property: 'og:image:secure_url',
content: image.url
}
)
})
return tempArr
}
}
}
The computed property contains all urls, but in the browser (in the generated <meta> tags) only the first element is present.
It seems to me that vue-meta filters out the other elements because they are considered duplicates.
Can anyone confirm this or does anyone have experience with outputting multiple elements?
According to the specification this is valid and should not be a problem.

Binding a typescript variable to translate service

I'm trying to have a TypeScript variable bind to the translate service just like binding in the HTML markup, which works fine.
Here's what I've tried so far
ngOnInit() {
this.customTranslateService.get("mainLayout.userProfileDropdown.changeLocale").subscribe((result) => {
this.changeLocaleText = result;
})
this.customTranslateService.translateService.onLangChange.subscribe((event: LangChangeEvent) => {
this.changeLocaleText = this.customTranslateService.instant("mainLayout.userProfileDropdown.changeLocale");
});
this.userProfileMenuOptions = [
{
text: this.changeLocaleText, itemId: "LocaleSelect"
},
{
text: "Report a bug", itemId: "BugReport"
},
{
text: "Request a feature", itemId: "FeatureRequest"
},
{
text: "Log Out", itemId: "LogOut"
}
];
}
customTranslateService is just a service that wraps TranslateService.
The first subscription works ok, but when I switch languages, the onLangChange does trigger, changing the variable content correctly, but userProfileMenuOptions's reference to changeLocaleText is not binded therefore not updated.
Using a BehaviorSubject can't really be done here as it is typescript code, not html markup that can use the async pipe.
Maybe recreating the userProfileMenuOptions array everytime the language change subscription is called could be an option, although I'm not sure the component that uses the array will like it.
PS: instant will work here because I have an application loader that loads all available languages before the application is available to the user.
Any ideas ?
ngOnInit() {
this.customTranslateService.get("mainLayout.userProfileDropdown.changeLocale").subscribe((result) => {
this.changeLocaleText = result;
})
const getUserPorfileMenuOptions = (changeLocaleText: string) => {
return [
{
text: this.changeLocaleText, itemId: "LocaleSelect"
},
{
text: "Report a bug", itemId: "BugReport"
},
{
text: "Request a feature", itemId: "FeatureRequest"
},
{
text: "Log Out", itemId: "LogOut"
}
];
}
this.customTranslateService.translateService.onLangChange.subscribe((event: LangChangeEvent) => {
this.changeLocaleText = this.customTranslateService.instant("mainLayout.userProfileDropdown.changeLocale");
this.userProfileMenuOptions = getUserPorfileMenuOptions(this.changeLocaleText);
});
this.userProfileMenuOptions = getUserPorfileMenuOptions(this.changeLocaleText);
}

Dynamic import of an instance in vue?

How can you create a dynamic import of an instance in vue using a parameter?
I would like to dynamically import the language into flatpickr-vue.
import { de } from 'flatpickr/dist/l10n/de.js';
how do I bring the "locale" parameter into the import path dynamically?
<akaunting-date
...
:config="{
...
locale: '{{ language()->getShortCode() }}',
}"
...
></akaunting-date>
Link to original code
<template>
<base-input :label="title"
:name="name"
:class="[
{'readonly': readonly},
{'disabled': disabled},
formClasses
]"
:footer-error="formError"
:prependIcon="icon"
:readonly="readonly"
:disabled="disabled"
>
<flat-picker slot-scope="{focus, blur}"
#on-open="focus"
#on-close="blur"
:config="config"
class="form-control datepicker"
v-model="real_model"
#input="change"
:readonly="readonly"
:disabled="disabled">
</flat-picker>
</base-input>
</template>
<script>
import flatPicker from "vue-flatpickr-component";
import "flatpickr/dist/flatpickr.css";
import { de } from 'flatpickr/dist/l10n/de.js';
export default {
name: 'akaunting-date',
components: {
flatPicker
},
props: {
title: {
type: String,
default: '',
description: "Modal header title"
},
placeholder: {
type: String,
default: '',
description: "Modal header title"
},
readonly: {
type: Boolean,
default: false,
description: "Input readonly status"
},
disabled: {
type: Boolean,
default: false,
description: "Input disabled status"
},
formClasses: null,
formError: null,
name: null,
value: {
default: null,
description: "Input value defalut"
},
model: {
default: null,
description: "Input model defalut"
},
config: null,
icon: {
type: String,
description: "Prepend icon (left)"
}
},
data() {
return {
real_model: this.model
}
},
mounted() {
this.real_model = this.value;
if (this.model) {
this.real_model = this.model;
}
this.$emit('interface', this.real_model);
},
methods: {
change() {
this.$emit('interface', this.real_model);
this.$emit('change', this.real_model);
}
}
}
</script>
Link to original code
i think i'm on the right track ...
computed: {
config() {
return {
locale: require('flatpickr/dist/l10n/' + this.locale + '.js').default.en,
}
}
},
now I would have to change the ".en" in .default dynamically. is that possible?
is not yet completely dynamic and there is still the following error message, which I do not understand
[Vue warn]: The computed property "config" is already defined as a prop.
Because the module you want is not known at runtime, you will have to import it asynchronously, else your script will have to wait for the file to be fetched and parsed..
If you don't want to use the promise.then() route, then you can opt for this:
// do something with the 'locale module' once it is available
function sayHelloTo(locale, name) {
console.log(locale.helloText + ' ' + name)
}
// get the module asyncly
async function loadLocale(countryCode, thenDoThis, ...optionalArgs) => {
const locale = await import(`flatpickr/dist/l10n/${countryCode}.js`)
thenDoThis(locale, ...optionalArgs)
})
loadLocale('DE', sayHelloTo, 'John') // runs asyncly

Fetching data from an API works locally but not live

First time building a site with Nuxt.js and I'm requesting data from an API that works great locally but when I access the site through Netlify, none of the code seems to run. I'm assuming this has something to do with my misunderstanding of how static sites work.
What I'm trying to do is check if there is a browser cookie, if not create one and fetch the API to get the user's general location and check their city against an array of cities in NY. If there is a match then close the v-banner. If there is already a browser cookie then close the v-banner.
Abridged index.vue:
<template>
<v-app v-bind:class="{ alertOpen: alertOpen }">
<v-main>
<v-banner
class="state-alert"
transition="slide-y-transition"
v-bind:class="{ alertOpen: alertOpen }"
>
By our calculations, it looks like you might be visiting our website from outside of New York. Unfortunately at this time, we can't sell our Granola outside of New York. If you are buying a gift for someone with a New York address then please proceed.
<template>
<v-btn
icon
color="alert"
v-on:click="alertOpen = false"
>
<v-icon>mdi-close</v-icon>
</v-btn>
</template>
</v-banner>
</v-main>
</v-app>
</template>
<script>
let nyList = ['Albany', 'Amsterdam', 'Auburn', 'Batavia', 'Beacon', 'Binghamton', 'Buffalo', 'Canandaigua', 'Cohoes', 'Corning', 'Cortland', 'Dunkirk', 'Elmira', 'Fulton', 'Geneva', 'Glen Cove', 'Glens Falls', 'Gloversville', 'Hornell', 'Hudson', 'Ithaca', 'Jamestown', 'Johnstown', 'Kingston', 'Lackawanna', 'Little Falls', 'Lockport', 'Long Beach', 'Mechanicville', 'Middletown', 'Mount Vernon', 'New Rochelle', 'New York', 'Newburgh', 'Niagara Falls', 'North Tonawanda', 'Norwich', 'Ogdensburg', 'Olean', 'Oneida', 'Oneonta', 'Oswego', 'Peekskill', 'Plattsburgh', 'Port Jervis', 'Poughkeepsie', 'Rensselaer', 'Rochester', 'Rome', 'Rye', 'Salamanca', 'Saratoga Springs', 'Schenectady', 'Sherrill', 'Syracuse', 'Tonawanda', 'Troy', 'Utica', 'Watertown', 'Watervliet', 'White Plains', 'Yonkers'];
export default ({
head() {
return {
script: [{
body: true
}]
}
},
data() {
return {
geoData: [],
alertOpen: true
}
},
async fetch() {
const locationCookie = this.$cookies.get('location-cookie');
if(!locationCookie) {
console.log('no cookie');
this.$cookies.set('location-cookie', 'true', {
path: '/',
maxAge: 31556952
});
this.geoData = await fetch(
'https://ipgeolocation.abstractapi.com/v1/?api_key=1eef312cdda9428cac26815c9d3bdd26'
).then(res => res.json());
var vm = this;
compareCity(this.geoData.city);
function compareCity(city) {
var i;
for(i = 0; i < nyList.length; i++) {
if(nyList[i].toLowerCase().replace(/\s/g, '') == city.toLowerCase().replace(/\s/g, '')) {
console.log(city);
vm.alertOpen = false;
}
}
}
} else {
console.log('yes cookie');
this.alertOpen = false;
}
}
});
</script>
Not sure if ya'll also need to see the nuxt.config.js:
export default {
mode: "universal",
// Global page headers (https://go.nuxtjs.dev/config-head)
head: {
title: 'homemade-crunch',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: '' }
],
link: [
{ rel: "preconnect", href: "https://app.snipcart.com" },
{ rel: "preconnect", href: "https://cdn.snipcart.com" },
{ rel: 'stylesheet', href: 'https://cdn.snipcart.com/themes/v3.0.23/default/snipcart.css', defer: true },
{ rel: 'stylesheet', href: 'https://fonts.googleapis.com/css2?family=Anton&family=Open+Sans&display=swap' }
],
script: [
{ src: "https://cdn.snipcart.com/themes/v3.0/default/snipcart.js", defer: true }
]
},
// Global CSS (https://go.nuxtjs.dev/config-css)
css: [
],
// Plugins to run before rendering page (https://go.nuxtjs.dev/config-plugins)
plugins: [
'#plugins/vuetify'
],
// Auto import components (https://go.nuxtjs.dev/config-components)
components: true,
// Modules for dev and build (recommended) (https://go.nuxtjs.dev/config-modules)
buildModules: [],
// Modules (https://go.nuxtjs.dev/config-modules)
modules: [
'vue-scrollto/nuxt',
'cookie-universal-nuxt'
],
// Build Configuration (https://go.nuxtjs.dev/config-build)
build: {
},
server: {
port: 3333,
host: '0.0.0.0'
}
}
The git repo: https://github.com/mat148/Homemade-crunch
and live site: https://www.homemadecrunch.com/
Let me know if I've forgotten something or done something silly.
Any help would be greatly appreciated.
Thank you!
I decided to move my logic from the index.vue to a javascript file

vue.js Uncaught SyntaxError: Identifier has already been declared

I have two demos for same library under repo, with demo.
The main difference is that, one is for browser use and the other is for node use.
However browser one will have error
index.js:1 Uncaught SyntaxError: Identifier 'HeaderComp' has already been declared
What is the main cause?
Update:
Please keep in mind I do not declare a variable twice! I also tried to add console log at the top to ensure the script is executed once!
var HeaderComp = {
name: 'HeaderComp',
template: `
Back
{{ r.title }}
`,
mixins: [VueTopDown.VueTopDownItem],
computed: {
routes () {
return [
{ href: '/page', title: 'Page' },
{ href: '/hello-vue', title: 'HelloVue' }
]
}
}
}
var FooterComp = {
name: 'FooterComp',
template: `{{ vueFooter }}`,
mixins: [VueTopDown.VueTopDownItem],
data () {
return {
vueFooter: 'This is Vue Footer'
}
}
}
var ContentComp = {
name: 'ContentComp',
template: ``,
mixins: [VueTopDown.VueTopDownItem],
computed: {
innerHTML () {
var root = document.createElement('div')
root.innerHTML = this[VTDConstants.OUTER_HTML]
return root.querySelector('*').innerHTML
}
}
}
var HelloVue = {
template: `Hello Vue`,
props: ['clazz'],
inheritAttrs: false
}
var Page = {
template: ``,
props: ['clazz', 'innerHTML'],
inheritAttrs: false
}
var router = new VueRouter([
{ path: '/hello-vue', component: HelloVue },
{ path: '/page', component: Page },
{ path: '*', redirect: '/page' }
])
var inst = new Vue({
router,
mixins: [VueTopDown.VueTopDown],
components: {
HeaderComp: HeaderComp,
FooterComp,
ContentComp
},
data () {
return {
[VueTopDown.VTDConstants]: {
'header': HeaderComp,
'footer': FooterComp,
'.content': ContentComp
}
}
}
})
inst.$mount('#app')
Also keep in mind that similar code works fine in node environment but fails in browser!
Doesn't occur if commenting out inst.$mount('#app')
Expect
The expected behavior of browser should be same as that of node.

Categories

Resources