I'm using vue3, and I would like to change the metadata on the subpages when they are linked in facebook or other social media sites.
I've gotten vue3-meta to "work" but it does change the title of the page from the returned api call, in the browser. But the links still pull the meta from the index.html I believe vue-meta is only changing the meta tags in the browser memory rather the root file?
It has the right name on the browser tab, but facebook debug page, it forever says the default site name, found in index.html
Package.json
"vue-meta": "^3.0.0-alpha.10",
Main.js
import { createMetaManager, plugin as vueMetaPlugin } from 'vue-meta';
let app = createApp(App)
.use(router)
.use(createMetaManager())
.use(vueMetaPlugin, {
keyName: 'metaInfo',
attribute: 'data-vue-meta',
ssrAttribute: 'data-vue-meta-server-rendered',
tagIDKeyName: 'vmid',
refreshOnceOnNavigation: true
})
Home.js
import {useMeta} from "vue-meta";
...
setup() {
useMeta({
title: 'MyAwesomeSite',
htmlAttrs: {lang: 'en', amp: true}
})
},
Product Page, This is really the one I want to change
metaInfo() {
return {
title: `${this.ProductName}`,
description: `${this.product.SlugLine}`,
twitter: {
title: `${this.productName}`,
description: `${this.product.SlugLine}`,
image: `${this.product.productImg}`,
},
og: {
title : `${this.productName}`,
description : `${this.product.SlugLine}`,
url : `${this.$route}`,
image : `${this.product.productImg}`,
site_name : `My Awesome Site`,
}
}
},
Related
I am trying to push to home page after login. When used:
this.$router.push()
url changes from
localhost:8100/auth/login
to
localhost:8100/home
but page remains same i.e. Login Page.
My Routes index file is as:
const routes:Array<RouteRecordRaw> = [
{
path:'/',
redirect:'/home
},{
path:'/home',
component: ()=>import('#/views/HomePage.vue')
},{
path:'/auth/login',
component: ()=>import('#/views/auth/LoginPage.vue')
}
]
const router = createRouter({
history:createWebHistory(process.env.BASE_URL),
routes
})
what can be the solution for this?
Found solution...
I had not use ion-page tag on template of DashboardPage.vue. After adding ion-page tag, problem was resolved
I'm trying to build a website builder within the drag-and-drop abilities via using Vue3. So, the user will be playing with the canvas and generate a config structure that going to post the backend. Furthermore, the server-side will generate static HTML according to this config.
Eventually, the config will be like the below and it works perfectly. The config only can have HTML tags and attributes currently. Backend uses h() function to generate dom tree.
My question is: can I use .vue component that will generate on the server side as well? For example, the client-side has a Container.vue file that includes some interactions, styles, etc. How can the backend recognize/resolve this vue file?
UPDATE:
Basically, I want to use the Vue component that exists on the Client side on the backend side to generate HTML strings same as exactly client side. (including styles, interactions etc).
Currently, I'm able to generate HTML string via the below config but want to extend/support Vue component itself.
Note: client and server are completely different projects. Currently, server takes config and runs createSSRApp, renderToString methods.
Here is the gist of how server would handle the API:
https://gist.github.com/yulafezmesi/162eafcf7f0dcb3cb83fb822568a6126
{
id: "1",
tagName: "main",
root: true,
type: "container",
properties: {
class: "h-full",
style: {
width: "800px",
transform: "translateZ(0)",
},
},
children: [
{
id: "9",
type: "image",
tagName: "figure",
interactive: true,
properties: {
class: "absolute w-28",
style: {
translate: "63px 132px",
},
},
},
],
}
This might get you started: https://vuejs.org/guide/scaling-up/ssr.html#rendering-an-app
From the docs:
// this runs in Node.js on the server.
import { createSSRApp } from 'vue'
// Vue's server-rendering API is exposed under `vue/server-renderer`.
import { renderToString } from 'vue/server-renderer'
const app = createSSRApp({
data: () => ({ count: 1 }),
template: `<button #click="count++">{{ count }}</button>`
})
renderToString(app).then((html) => {
console.log(html)
})
I guess extract the template from request or by reading the submitted Vue file and use that as the template parameter value
I use this package : https://www.npmjs.com/package/vue-recaptcha-v3
I add on my main.js :
import { VueReCaptcha } from 'vue-recaptcha-v3'
Vue.use(VueReCaptcha, { siteKey: 'xxxxxxx' })
I add this code :
await this.$recaptcha('login').then((token) => {
recaptcha = token
})
to my component to get token from google recapchta
My problem is the captcha icon in the lower right corner appears on all pages
I want it to only appear in certain components
Maybe I must to change this : Vue.use(VueReCaptcha, { siteKey: 'xxxxxxxxxxxxxxxxx' }). Seems it still mounting to Vue.use. I want to mount to a certain component instead of vue root instance
How can I solve this problem?
Update
I try like this :
Vue.use(VueReCaptcha, {
siteKey: 'xxxxxxx',
loaderOptions: {
useRecaptchaNet: true,
autoHideBadge: true
}
})
It hides the badge. I want the badge to still appear. But only on 1 page, the registration page. How can I do it?
I've had the same issue while using the npm package, it's pretty annoying.
At the end of the day, I've decided not to use the package & follow Google's documentation.
This line here :
grecaptcha.execute('_reCAPTCHA_site_key_', {action: 'login'}).then(function(token) {
recaptcha = token
})
Is equivalent to this line here from the npm package :
this.$recaptcha('login').then((token) => {
recaptcha = token
})
You just need to add this line into your < head > for recaptcha to work :
<script src="https://www.google.com/recaptcha/api.js?render=_reCAPTCHA_site_key"></script>
But as soon the script tag is in your < head >, you will be facing the same issue of it showing on every page.
The hack is that you only insert it into the < head > on components that you need.
There are ways to do this but I ended up referencing this.
You can put it in the methods of your component & call the method when the component is loaded.
That way it will only show up on the pages that you need it to.
in main.js set autoHideBadge true:
import { VueReCaptcha } from 'vue-recaptcha-v3'
Vue.use(VueReCaptcha, { siteKey: 'your site key',
loaderOptions:{autoHideBadge: true }})
in every page you want to show the badge you can show the badge in mounted,
for some reasons until a few seconds after mounted event this.$recaptchaInstance is null and you cant use it, so I use a timeout to showing the badge 5 second after page load in mounted.
mounted(){
setTimeout(()=>{
const recaptcha = this.$recaptchaInstance
recaptcha.showBadge()
},5000)
},
when you show it you have to hide it again in the same page.
beforeDestroy() {
const recaptcha = this.$recaptchaInstance
recaptcha.hideBadge()
},
If you are using composition API setup this is what you need:
const reCaptchaIn = useReCaptcha().instance
onMounted(() => {
setTimeout(() => {
reCaptchaIn.value.showBadge()
}, 3000)
})
Just use this code:
const recaptcha = this.$recaptchaInstance
// Hide reCAPTCHA badge:
recaptcha.value.hideBadge()
// Show reCAPTCHA badge:
recaptcha.value.showBadge()
vue-recaptcha-v3 npm
I stumbled upon this incredibly simple answer. It is excellent especially if you wish to hide the badge from all your pages. You can perhaps use scoped css to hide on some pages as well.
.grecaptcha-badge { visibility: hidden; }
You can read the post here
I wanted to confirm whether I got my analytics tracking setup correctly in my single page application within the VueJS framework.
I am using the Vue plugin for Matomo which is found here:
https://github.com/AmazingDreams/vue-matomo
I imported the VueMatomo plugin in my main.js entry file like so:
import VueMatomo from 'vue-matomo';
Then, I assign the VueMatomo as a global method in my main.js file like so:
Vue.use(VueMatomo, {
// Configure your matomo server and site
host: 'https://matomo.example.com', <-- i configured this to match my real site
siteId: 5, <--- i configured this to match my real site
// Enables automatically registering pageviews on the router
router: router,
// Enables link tracking on regular links. Note that this won't
// work for routing links (ie. internal Vue router links)
// Default: true
enableLinkTracking: true,
// Require consent before sending tracking information to matomo
// Default: false
requireConsent: false,
// Whether to track the initial page view
// Default: true
trackInitialView: true,
// Changes the default .js and .php endpoint's filename
// Default: 'piwik'
trackerFileName: 'piwik',
// Whether or not to log debug information
// Default: false
debug: false
});
That gives me access to the Matomo API (_paq) in my components. However, this is where I am confused.
For example, I have a view called overview.vue which is the main page of the site. In this vue template, I have the following code in my created() hook. Since I am using a SPA, I need to somehow get the name of the page that the user is on and push it to the Matomo Reporting Tool. This is what I did:
<template>...snip...</template>
<script>
export default {
name: 'OverView',
created: function() {
window._paq.push(['setCustomUrl', '/' + window.location.hash.substr(1)]);
window._paq.push(['setDocumentTitle', 'Overview Page']);
window._paq.push(['trackPageView']);
}
};
</script>
Is the above adequate or is there a better lifecyle hook (mounted?) for the tracking code? Perhaps navigation guards are more appropriate?
Thank you
I got matomo working on my vue.js app (v 2.6.10).
I'm using a trial account from https://matomo.org/
In my main.js file:
// Analytics
import VueMatomo from "vue-matomo";
Vue.use(VueMatomo, {
host: "https://example.matomo.cloud", // switch this to your account
siteId: 1, // switch this as well you can find the site id after adding the website to the dashboard.
router: router,
enableLinkTracking: true,
requireConsent: false,
trackInitialView: true,
trackerFileName: "piwik",
debug: true
});
I can confirm that all of my nested routes are tracked. I can see what pages i viewed on my matomo dashboard.
To get custom events working just add the following:
this.$matomo.trackEvent("Event Category", "Event Name", "event action");
To give this some context, for my app i'm using it in a computed property:
computed: {
selectedMapDataType: {
get() {
return this.$store.state.mapDataType;
},
set(selected) {
this.$matomo.trackEvent("Dashboard Update", "Dashboard Data", selected);
this.$store.dispatch("updateMapDataType", selected);
}
},
...}
Search Results in google are displayed via TitleTag and the <meta name="description"..."/> Tag.
The <title>-Tag is editiable via Angular2 how to change page title in angular2 router
What's left is the description.
Is it possibile to write a directive in angular2, that manipulates the meta-tags in the <head> part of my page.
So depending on the selected route, the meta description changes like:
<meta name="description" content="**my description for this route**"/>
Since Angular4, you can use Angular Meta service.
import { Meta } from '#angular/platform-browser';
// [...]
constructor(private meta: Meta) {}
// [...]
this.meta.addTag({ name: 'robots', content: 'noindex' });
It is possible. I implemented it in my app and below I provide the description how it is made.
The basic idea is to use Meta from #angular/platform-browser
To dynamically change particular meta tag you have to:
Remove the old one using removeTag(attrSelector: string) : void
method.
Add the new one using addTag(tag: MetaDefinition, forceCreation?:
boolean) : HTMLMetaElement method.
And you have to do it when the router fires route change event.
Notice: In fact it is also necessary to have default <title>...</title> and <meta name="description"..." content="..."/> in head of index.html so before it is set dynamically there is already some static content.
My app-routing.module.ts content:
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/mergeMap';
import { NgModule } from '#angular/core';
import { RouterModule, Routes, Router, NavigationEnd, ActivatedRoute } from '#angular/router';
import { StringComparisonComponent } from '../module-string-comparison/string-comparison.component';
import { ClockCalculatorComponent } from '../module-clock-calculator/clock-calculator.component';
import { Title, Meta } from '#angular/platform-browser';
const routes: Routes = [
{
path: '', redirectTo: '/string-comparison', pathMatch: 'full',
data: { title: 'String comparison title', metaDescription: 'String comparison meta description content' }
},
{
path: 'string-comparison', component: StringComparisonComponent,
data: { title: 'String comparison title', metaDescription: 'String comparison meta description content' }
},
{
path: 'clock-time-calculator', component: ClockCalculatorComponent,
data: { title: 'Clock time calculator title', metaDescription: 'Clock time calculator meta description content' }
}
];
#NgModule({
imports: [ RouterModule.forRoot(routes) ],
exports: [ RouterModule ]
})
export class AppRoutingModule {
constructor(
private router: Router,
private activatedRoute: ActivatedRoute,
private titleService: Title,
private metaService: Meta
){
//Boilerplate code to filter out only important router events and to pull out data object field from each route
this.router.events
.filter(event => event instanceof NavigationEnd)
.map(() => this.activatedRoute)
.map(route => {
while (route.firstChild) route = route.firstChild;
return route;
})
.filter(route => route.outlet === 'primary')
//Data fields are merged so we can use them directly to take title and metaDescription for each route from them
.mergeMap(route => route.data)
//Real action starts there
.subscribe((event) => {
//Changing title
this.titleService.setTitle(event['title']);
//Changing meta with name="description"
var tag = { name: 'description', content: event['metaDescription'] };
let attributeSelector : string = 'name="description"';
this.metaService.removeTag(attributeSelector);
this.metaService.addTag(tag, false);
});
}
}
As it can be seen there is an additional data object field for
each route. It contains title and metaDescription strings
which will be used as title and meta tag content.
In constructor we filter out router events and we subscribe to filtered
router event. Rxjs is used there, but in fact it is not necessary to use it. Regular if statements and loops could be used insead of stream, filter and map.
We also merge our data object field with our event so we can easily
use info like title and metaDescription strings.
We dynamically change <title>...</title> and <meta name="description"..." content="..."/> tags.
Effects:
First component
Second component
In fact I currently use a little bit more sophisticated version of this solution which uses also ngx-translate to show different title and meta description for different languages.
Full code is available in angular2-bootstrap-translate-website-starter project.
The app-routing.module.ts file with ngx-translate solution is exactly there: app-routing.module.ts.
There is also the production app which uses the same solution: http://www.online-utils.com.
For sure it is not the only way and there might be better ways to do it. But I tested this solution and it works.
In fact the solution is very similar to this from corresponding post about changing title: How to change page title in angular2 router.
Angular Meta docs: https://angular.io/docs/ts/latest/api/platform-browser/index/Meta-class.html. In fact they aren't very informative and I had to experiment and look into real .js code to make this dynamic meta change working.
I have developed and just released #ngx-meta/core plugin, which manipulates the meta tags at the route level, and allows setting the meta tags programmatically within the component constructor.
You can find detailed instructions at #ngx-meta/core github repository. Also, source files might be helpful to introduce a custom logic.
There is currently no out-of-the-box solution only an open issue to implement it https://github.com/angular/angular/issues/7438.
You can of course implement something like the title service yourself, just use the TitleService as template
A Meta service similar to Title service is in the works (currently only a pull request).