How to use Vue global variables in html head section? - vue.js

I am using Vue version 2.6.11 in my website. I set some global variables according to this question in my src/main.js file as follow:
/* global variables */
Vue.prototype.$frontUrl = "http://localhost:8080/";
Vue.prototype.$backUrl = "http://localhost:3500/";
That works fine if I use them in my Vue views or components like this.$backUrl in this form tag:
<form novalidate class="row pl-md-5" :action="this.$backUrl + 'sql'" method="post">
<!-- some input tags ... -->
</form>
But I want to use them in my public/index.html file as the code below:
<!DOCTYPE html>
<html>
<head>
<!-- og meta tags -->
<meta name="type" property="og:type" content="website">
<meta name="image" property="og:image" :content='this.$backUrl + "img/images/portfolio-preview.png"'>
<meta name="url" property="og:url" content="">
</head>
</html>
It does not work. It does not give me any error but don't use Vue global variable value. If I see my page source in browsers, It shows me <meta name="image" property="og:image" :content='this.$backUrl + "img/images/portfolio-preview.png"'>. Could anyone please help me how to use those variables in my index.html head section?

This is a tricky question and I solved this way. All credit to this site who points the answer: link
create a definitions file definitions.js
export default {
urlTest: "localhost:8080"
}
To keep code logic you need to change the global variables definitions for something like this:
import def from '../definitions.js'
Vue.prototype.$frontUrl = def.urlTest
//Only if you really need to store as a global
then on the vue-router where you define the path:
import HomeView from '../views/HomeView.vue'
import definitions from '../config/definitions.js'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [{
path: '/',
meta: {
title: "Home",
metaTags: [{
//<meta name="image" property="og:image" :content='this.$backUrl + "img/images/portfolio-preview.png"'>
name: 'image',
property: 'og:image',
content: definitions.urlTest + "/img/images/portfolio-preview.png"
}]
},
name: 'home',
component: HomeView
}]
})
// This callback runs before every route change, including on page load.
router.beforeEach((to, from, next) => {
// This goes through the matched routes from last to first, finding the closest route with a title.
// e.g., if we have `/some/deep/nested/route` and `/some`, `/deep`, and `/nested` have titles,
// `/nested`'s will be chosen.
const nearestWithTitle = to.matched.slice().reverse().find(r => r.meta && r.meta.title);
// Find the nearest route element with meta tags.
const nearestWithMeta = to.matched.slice().reverse().find(r => r.meta && r.meta.metaTags);
const previousNearestWithMeta = from.matched.slice().reverse().find(r => r.meta && r.meta.metaTags);
// If a route with a title was found, set the document (page) title to that value.
if (nearestWithTitle) {
document.title = nearestWithTitle.meta.title;
} else if (previousNearestWithMeta) {
document.title = previousNearestWithMeta.meta.title;
}
// Remove any stale meta tags from the document using the key attribute we set below.
Array.from(document.querySelectorAll('[data-vue-router-controlled]')).map(el => el.parentNode.removeChild(el));
// Skip rendering meta tags if there are none.
if (!nearestWithMeta) return next();
// Turn the meta tag definitions into actual elements in the head.
nearestWithMeta.meta.metaTags.map(tagDef => {
const tag = document.createElement('meta');
Object.keys(tagDef).forEach(key => {
tag.setAttribute(key, tagDef[key]);
});
// We use this to track which meta tags we create so we don't interfere with other ones.
tag.setAttribute('data-vue-router-controlled', '');
return tag;
})
// Add the meta tags to the document head.
.forEach(tag => document.head.appendChild(tag));
next();
});
This is with Vue3 but with the method and router path definition has the same format.
For more detailed explanation check the link on top.

I finally found a solution with the help of Vue CLI Environment Variables. I saved my global variables values in a .env file in the root directory of project like the code below:
.env file:
VUE_APP_BACK_URL=http://localhost:3500/
Then I changed my src/main.js file that contains global variables like the code below:
main.js:
Vue.prototype.$backUrl = process.env.VUE_APP_BACK_URL;
So that I don't need to change other components and views that used this global variable. Finally for html head tag I used variables defined in .env file directly as could be seen in the code below:
<!DOCTYPE html>
<html>
<head>
<!-- og meta tags -->
<meta name="type" property="og:type" content="website">
<meta name="image" property="og:image" content='<%= process.env.VUE_APP_BACK_URL + "img/images/portfolio-preview.png"%>'>
<meta name="url" property="og:url" content="<%= process.env.VUE_APP_BACK_URL %>">
</head>
</html>

Related

Vue: How to change tab title in Chrome

How do I change the default "Webpack App" tab title in Vue.js?
In Your public/index.html or other HTML files used as templates by html-webpack-plugin. find title tag and change it
<title>Page Title</title>
Or you can set the page title dynamically by getting the title from document document.title and change it inside your component like the code below :
<template>
<div id="app"></div>
</template>
<script>
export default {
name: "App",
mounted() {
document.title = "page title";
},};
</script>
you have at least two options:
document.title = 'Your new name'
or in the index.html-file:
<head>
<title>Your new name</title>
</head>
You will find <title>Webpack App</title> in index.html in most probably public folder. You can change it
We needed to fix this in the webpack config which generated the index.html every time we deployed our Storybook app.
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = async ({ config }) => {
config.plugins.push(
new HtmlWebpackPlugin({ title: 'Storybook — Company Name' }),
)
return config
}

How to change initially generated title after file build in index.html

I have downloaded a vue.js template from the web. Whenever I build files via npm the title on the index.html keeps being swapped to the name of the template. Is there a way to change the default title?
As I understand your question - you need to configure your vue.config.js file something like this (pay attention on Webpack part) - these files are from working project, so you have maximum understanding on how it could look at the end:
module.exports = {
baseUrl: '/',
outputDir: (process.env.NODE_ENV === 'production' ? '../web/' : '../web/js/'),
indexPath: '../app/Resources/views/index.html.twig',
// Setting this to false can speed up production builds if you don't need source maps for production.
productionSourceMap: false,
// By default, generated static assets contains hashes in their filenames for better caching control.
// However, this requires the index HTML to be auto-generated by Vue CLI. If you cannot make use of the index HTML
// generated by Vue CLI, you can disable filename hashing by setting this option to false,
filenameHashing: false,
lintOnSave: false,
// https://cli.vuejs.org/ru/config/#devserver-proxy
devServer: {},
// https://cli.vuejs.org/ru/config/#chainwebpack
chainWebpack: config => {
config
.plugin('html')
.tap(args => {
args[0].title = 'Ojok Deep Sales Platform';
args[0].template = './index.html.template';
return args;
})
}
};
And after you have updated your vue.config.js file, change your index.html template file to be like this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title><%= htmlWebpackPlugin.options.title %></title>
<link href='https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900' rel="stylesheet">
<link href="https://fonts.googleapis.com/css?family=Material+Icons" rel="stylesheet">
<script type="text/javascript" src="../js/go.js"></script>
<script type="text/javascript" src="../js/momentjs.js"></script>
<script type="text/javascript" src="../js/webphone/flashphoner.js"></script>
<script type="text/javascript" src="../js/webphone/SoundControl.js"></script>
</head>
<body>
<div id="app"></div>
</body>
</html>
Pay attention on what is being included in <title>-tag:
<title><%= htmlWebpackPlugin.options.title %></title>
After generating new index.html file your title should be set to whatever you have written into args[0].title option.
Hope this helps.
I'm still new at VueJS, but here were my findings. I'd love any suggested options or improvements. I went with option #2.
Option 1: set for Multiple Pages mode
vue.config.js
module.exports = {
pages: {
index: {
entry: 'src/main.js',
// template title tag needs to be <title><%= htmlWebpackPlugin.options.title %></title>
title: 'My Website Title',
},
}
}
Option 2: titleMixin (referenced from https://medium.com/#Taha_Shashtari/the-easy-way-to-change-page-title-in-vue-6caf05006863)
titleMixin.js added to mixins folder
function getTitle (vm) {
const { title } = vm.$options
if (title) {
return typeof title === 'function'
? title.call(vm)
: title
}
}
export default {
created () {
const title = getTitle(this)
if (title) {
document.title = title
}
}
}
added to main.js
import titleMixin from './mixins/titleMixin'
Vue.mixin(titleMixin)
Use in Component Pages
<script>
export default {
title: 'Foo Page'
}
</script>
Use in Vue instance with a function
<script>
export default {
title () {
return `Foo Page — ${this.someValue}`
},
data () {
return {
someValue: 'bar'
}
}
}
</script>
Option 1:
Edit your /public/index.html and replace this:
<title><%= htmlWebpackPlugin.options.title %></title>
with this:
<title>Your Title Here</title>
Option 2:
vue.config.js
module.exports = {
chainWebpack: config => {
config.plugin('html').tap(args => {
args[0].title = 'Your Title Here';
return args;
});
}
}
/public/index.html
<title><%= htmlWebpackPlugin.options.title %></title>

Laravel Mix Vue, Lazy loading component returns error as unknown custom element when using Vue Router

I have got a fresh install of Laravel Mix and I am trying to setup lazy loading components in the project. I have got the correct setup with the babel plugin 'syntax-dynamic-import' so the import statement in app.js works as expected. The issue occurs when I attempt to use the lazy loaded component with vue-router.
My app.js file looks like this:
require('./bootstrap');
import Vue from 'vue';
import VueRouter from 'vue-router';
Vue.use(VueRouter);
const EC = () => import(/* webpackChunkName: "example-component" */ './components/ExampleComponent.vue');
const router = new VueRouter({
mode: 'history',
routes: [
{ path: '/', component: EC }
]
});
const app = new Vue({
router,
el: '#app'
});
and my welcome.blade.php file looks like this:
<!doctype html>
<html lang="{{ app()->getLocale() }}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>Laravel</title>
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
<base href="/" />
</head>
<body>
<div id="app">
<h1>Base title</h1>
<example-component></example-component>
</div>
<script src="{{ asset('js/app.js') }}"></script>
</body>
</html>
So I just trying to land on the root route and display the Example Component. The example component is included in the welcome.blade.php file.
I am receiving this error in the console:
[Vue warn]: Unknown custom element: <example-component> - did you register the component correctly? For recursive components, make sure to provide the "name" option.
(found in <Root>)
I think I am missing something simple, any advice is appreciated.
First, i think you are mixing routes concepts with core components vue concepts...
Try loading the component directly in your vue app...
const app = new Vue({
router,
el: '#app',
components: {
'example-component': () => import('./components/ExampleComponent.vue')
}
});
Component loading is done with <component>
<component v-bind:is="currentTabComponent"></component>
Check the docs, for more info on dynamic components: https://v2.vuejs.org/v2/guide/components-dynamic-async.html
#Erubiel answer did work but it still quite wasn't the setup I wanted. As I am trying to use vue-router I needed to update the view by removing the explicit call to the component and adding the tag in the welcome.blade.php file. This now means my routes are injected into that space. The updated area is:
...
<body>
<div id="app">
<router-view></router-view>
</div>
<script src="{{ asset('js/app.js') }}"></script>
</body>
...
The problem is in the scss splitting in Vue and using mix.scss() both. Laravel mix when having both creates a css file with manifest js file content in it. which is definitely a bug. which the community mentions a bug from Webpack and will be resolved in webpack 5. But If you use only code splitting in Vue and have the default app.scss file imported to main Vue component like this(not in scope), so each other component will get the styling properly
// resources/js/components/app.vue
<template>
<!-- Main Vue Component -->
</template>
<script>
// Main Script
</script>
<style lang="scss">
#import '~#/sass/app.scss';
</style>
and the webpack.mix.js file will have no mix.scss function to run only a single app.js file. here is my file.
// webpack.mix.js
const mix = require('laravel-mix')
mix.babelConfig({
plugins: ['#babel/plugin-syntax-dynamic-import'] // important to install -D
})
mix.config.webpackConfig.output = {
chunkFilename: 'js/[name].bundle.js',
publicPath: '/'
}
mix
.js('resources/js/app.js', 'public/js')
.extract(['vue'])
.webpackConfig({
resolve: {
alias: {
'#': path.resolve('resources/') // just to use relative path properly
}
}
})
Hope this solves everyone's question

"React is undefined" error in CycleJs app

I am experimenting with cycle.js and webpack. I have got the following index.js file which almost a copy of what I found on cycle.js documentation.
import Cycle from '#cycle/core';
import {makeDOMDriver, hJSX} from '#cycle/dom';
function main(drivers) {
return {
DOM: drivers.DOM.select('input').events('click')
.map(ev => ev.target.checked)
.startWith(false)
.map(toggled =>
<div>
<input type="checkbox" /> Toggle me
<p>{toggled ? 'ON' : 'off'}</p>
</div>
)
};
}
const drivers = {
DOM: makeDOMDriver('#app')
};
Cycle.run(main, drivers);
And here is my index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Cycle.js checkbox</title>
</head>
<body>
<div id="app"></div> <!-- Our container -->
<script src="./dist/bundle.js"></script>
</body>
</html>
I am using webpack to generate bundle.js inside dist folder. When I run the app by opening index.html in chrome, I get following error in chrome console
cycle.js:51ReferenceError: React is not defined
at index.js:10
at tryCatcher (rx.all.js:63)
at InnerObserver.next (rx.all.js:5598)
at InnerObserver.Rx.internals.AbstractObserver.AbstractObserver.onNext (rx.all.js:1762)
at InnerObserver.tryCatcher (rx.all.js:63)
at AutoDetachObserverPrototype.next (rx.all.js:11810)
at AutoDetachObserver.Rx.internals.AbstractObserver.AbstractObserver.onNext (rx.all.js:1762)
at ConcatObserver.next (rx.all.js:3466)
at ConcatObserver.Rx.internals.AbstractObserver.AbstractObserver.onNext (rx.all.js:1762)
at ConcatObserver.tryCatcher (rx.all.js:63)
Not sure what I am doing wrong in this seemingly simple first step in a cycle.js app.
You need to set the correct pragma for JSX, or the JSX will be transformed incorrectly.
You can add the correct pragma to the top of your .js file:
/** #jsx hJSX */
Or put this in your babel configuration:
[ "transform-react-jsx", { "pragma": "hJSX" } ]
Relevant GitHub thread.

react-router Link doesn't render href attribute with server side rendering?

I have a expressjs app with the following code, what i am trying to do is to render a react component named Hello with the request to "/". Inside Hello component i am using two react-router Links, but these two Links doesn't render the href attributes.
I am using here react-router 2
Server.js file
var express = require("express");
var app = express();
var React = require("react");
var ReactDOM = require("react-dom/server");
var Hello = React.createFactory(require("./js/components/Hello"));
// The above is a reference to pre-compiled Hello.jsx to Hello.js using babel
app.set("view engine", "ejs");
app.set("views", "./views");
app.use(express.static("./public"));
app.get("/", function (req, res) {
res.render("Home",{data:ReactDOM.renderToString(Hello())});
});
app.listen(app.get("port"), function () {
console.log("server started on port " + app.get("port"));
});
Hello.jsx File
var React = require("react");
var Link = require("react-router/lib/Link");
var Hello = React.createClass({
callMe:function(){
alert("Hurray");
},
componentWillMount:function(){
console.log("componentWillMount fired");
},
render:function(){
// console.log(this);
return(
<div>
<h2 onClick={this.callMe}>Hello Boom Boom {this.props.name}</h2>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
// above two Links rendering anchor tags without href attribute
</div>
);
}
});
module.exports = Hello;
Home.ejs File
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
</head>
<body>
<h2>Welcome to home page</h2>
<div id="container">
<%- data %>
</div>
</body>
</html>
Please consult the React Router server rendering guide: https://github.com/reactjs/react-router/blob/master/docs/guides/ServerRendering.md
You need to render your components in an appropriate routing context for <Link> to generate URLs.
I just had the same problem, and the thing I was missing was the surrounding <RouterContext>. <Link> components seem to have an empty href attribute when not inside of a <Router> or appropriate react-router environment. I improved the routing in the SSR code (I was doing a weird partial solution) and the links started appearing.