I want to send a signal from a child component to a parent. I don't want to use Vuex as for my level of VueJS knowledge Vuex is too complicated. I am using single file components.
child.vue
<script>
export default {
name: 'ChildComponent',
methods: {
// ajax post here ...
if (response.data.status === 'accepted'){
this.$emit('send-data', 'accepted')
}
}
parent.vue
<script>
import ChildComponent from './ChildComponent.vue'
export default {
name: 'Parent',
data () {
return {
stage: 1
}
},
components: {
ChildComponent
},
// how can I replace 'events' with $on in a single file component and listen for events after all components have been created
events: {
'send-data': function (dataResponse) {
if (dataResponse === 'accepted'){
this.stage = 2
}
}
}
examples in the VueJS docs show something like this for the parent:
var eventHub = new Vue()
created: function () {
eventHub.$on('add-todo', this.addTodo)
eventHub.$on('delete-todo', this.deleteTodo)
},
but I want to listen to events at any time, not just on creation. How can I replace the parents 'events' with a $on function?
If you start listening for event on created that would work for the entire life cycle of the component. Alternatively you could set event to trigger using v-on or # shortcut while using the component.
Example
Vue.component('my-component', {
template: '<div><button v-on:click="sendHello">hello</button></div>',
methods:{
sendHello: function(){
console.log('hello');
this.$emit('hello','hello')
}
}
});
new Vue({
el: '#app',
data: {
message: 'Hello Vue.js!'
},
methods:{
sayHi: function(){
console.log('say hi')
}
}
})
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>VueJs</title>
</head>
<body>
<div id="app">
<p>{{ message }}</p>
<my-component v-on:hello='sayHi'></my-component>
</div>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
</body>
</html>
Related
I'm trying to create custom component using Stencil with input. My intention is to make component with input. After change input value It should emit It to my Vue instance and console log this event (later it will update value in Vue instance). But after change input value in Stencil nothing happen.
Learning how Stencil components works I used:
https://medium.com/#cindyliuyn/create-a-stencil-form-input-component-for-angular-and-vue-js-22cb1c4fdec3
Trying to solve problem I tried also:
https://medium.com/sharenowtech/using-stenciljs-with-vue-a076244790e5
HTML and Vue code:
<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0" />
<title>Stencil Component Starter</title>
<script src="https://unpkg.com/vue"></script>
<script type="module" src="/build/vox-wc-research.esm.js"></script>
<script nomodule src="/build/vox-wc-research.js"></script>
</head>
<body>
<div id="app">
<test-component :placeholder="placeholder" :label="label" :value="value" #valueChange="e => onValueChange"></test-component>
{{value}}
</div>
</body>
<script>
var app = new Vue({
el: '#app',
data: {
label: 'Nazwa Użytkownika',
value: '',
placeholder: 'Wpisz nazwę użytkownika',
},
methods: {
onValueChange(e) {
console.log(e);
},
},
});
</script>
</html>
Stencil Component:
import { h, Component, Element, Event, EventEmitter, Prop /* PropDidChange */ } from '#stencil/core';
#Component({
tag: 'test-component',
styleUrl: 'test-component.css',
//shadow: true,
})
export class FormInputBase {
#Element() el: HTMLElement;
#Prop() type: string = 'text';
#Prop() label: string;
#Prop() placeholder: string;
#Prop({ mutable: true }) value: string;
#Event() valueChange: EventEmitter;
handleChange(event) {
const val = event.target.value;
console.log(val);
this.value = val;
this.valueChange.emit(val);
}
render() {
return (
<div>
<label>
{this.label}
<div>
<input placeholder={this.placeholder} value={this.value} onInput={event => this.handleChange(event)}></input>
{this.value}
</div>
</label>
</div>
);
}
}
Vue doesn't support camel-case event names because all v-on: event listeners are converted to lower-case (see https://v2.vuejs.org/v2/guide/components-custom-events.html#Event-Names).
However when you load your component(s), you can use the options of Stencil's defineCustomElements to "transform" all your event names:
import { applyPolyfills, defineCustomElements } from 'my-component/loader';
applyPolyFills().then(() => {
defineCustomElements({
ce: (eventName, opts) => new CustomEvent(eventName.toLowerCase(), opts)
});
});
For a more full-blown example have a look at Ionic Framework's source:
https://github.com/ionic-team/ionic-framework/blob/b064fdebef14018b77242b791914d5bb10863d39/packages/vue/src/ionic-vue.ts
I have a display component (app-display) with dynamic component inside (by default: app-empty):
app.component('appDisplay', {
template: `<component :is="currentComponent"></component>`,
data() {
return {currentComponent: 'appEmpty'}
}
});
I need to create new instance of app-message, to set property message for this instance and to set the instance as current component for app-display on button click.
This is a browser code for the question:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue component reference</title>
<script src="https://unpkg.com/vue#next"></script>
</head>
<body>
<div id="app"></div>
<script>
// main application with button & display panel
const app = self.Vue.createApp({
template: `
<app-btn></app-btn>
<app-display></app-display>
`
});
// button component to fire action
app.component('appBtn', {
template: `
<button v-on:click="setDisplay">Display message</button>`,
methods: {
setDisplay() {
console.log('I need to set "appMessage" (with some "message" param) as inner component to "app-display" here.');
}
}
});
// component with dynamic component inside
app.component('appDisplay', {
template: `<component :is="currentComponent"></component>`,
data() {
return {currentComponent: 'appEmpty'}
}
});
// default component to display
app.component('appEmpty', {
template: `<div>I'm empty.</div>`
});
// this component with custom message should be displayed on button click
app.component('appMessage', {
template: `
<div>{{ message }}</div>
`,
props: {
message: String
}
});
// mount main app to the page
app.mount('#app');
</script>
</body>
</html>
How can I access app-display from app-btn?
You should emit an event from button component to main component with component name to display and the message and in the main component you should define a message and current component name which will be updated by the handler of the emitted event and passed as props to the component that displays them:
// main application with button & display panel
const app = self.Vue.createApp({
template: `
<app-btn #change-content="changeContent"></app-btn>
<app-display :currentComponent="componentName" :message="msg"></app-display>
`,
data(){
return{
componentName:'appEmpty',
msg:''
}
},
methods:{
changeContent(compName,msg){
console.log(compName,msg)
this.componentName=compName
this.msg=msg
}
}
});
// button component to fire action
app.component('appBtn', {
template: `
<button v-on:click="setDisplay">Display message</button>`,
methods: {
setDisplay() {
this.$emit('change-content','appMessage','Hello message :)')
}
}
});
// component with dynamic component inside
app.component('appDisplay', {
props:{
currentComponent:{
type:String,
default:'appEmpty'
}
},
template: `<component :is="currentComponent"></component>`,
});
// default component to display
app.component('appEmpty', {
template: `<div>I'm empty.</div>`
});
// this component with custom message should be displayed on button click
app.component('appMessage', {
template: `
<div>{{ message }}</div>
`,
props: {
message: String
}
});
// mount main app to the page
app.mount('#app');
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue component reference</title>
<script src="https://unpkg.com/vue#next"></script>
</head>
<body>
<div id="app"></div>
</body>
</html>
I tried to make communicate to components with vuejs and vuex :
First component is a menu
Second component, inside the first, is an hamburgerButton.
In this very current case, when I click the hamburgerButton, this one is animated and the menu too. With this code, after the button click, the menu is animated with vueTransition and the hamburgerButton too. Note that i use vuex to manage a menuIsOpen state.
My problem is when i click an item of the menu, I would like fired the hamburgerButton animation.
hamburgerButtonanimation method, call animButtonHandler(), is encapsulated in a #click event. Actually i understand why it doesn't work right know, but I don't perceive how to handle this method to the click of a Parent element (item of the menu). So my question is, how can I access a method to a child component from a parent component ? Or is there an another workaround methodology to achieve this ?
parent component - menu.vue :
<template>
<div class="menu">
<!-- import hamburgerButton component -->
<hamburger-button></hamburger-button>
<transition v-on:enter="enterMenuHandler" v-on:leave="leaveMenuHandler">
<div class="menu_wrapper" v-if="this.$store.state.menuIsOpen">
<ul class="menu_items">
<li class="menu_item" #click="$store.commit('toggleMenu')">
<router-link class="menu_link" to="/">home</router-link>
<router-link class="menu_link" to="/contact">contact</router-link>
</li>
</ul>
</div>
</transition>
</div>
</template>
<script>
import hamburgerButton from "hamburgerButton.vue";
export default {
components: {
'hamburger-button': hamburgerButton,
},
methods: {
enterMenuHandler(el, done){
TweenLite.fromTo(el, 0.5, {
opacity: '0',
},{
opacity: '1',
onComplete: done
});
},
leaveMenuHandler(el, done){
TweenLite.to(el, 0.5, {
opacity: '0',
onComplete: done
});
},
}
}
</script>
child component : hamburgerButton.vue :
<template>
<div class="hamburgerButton" #click="animButtonHandler()">
<div class="hamburgerButton_inner" ref="hamburgerButtonInner">
<i class="hamburgerButton_icon></i>
</div>
</div>
</template>
<script>
export default {
methods: {
animButtonHandler (){
// toggle the state of menu if button clicked
this.$store.commit('toggleMenu');
const isOpen = this.$store.state.menuIsOpen === true;
// anim the button
TweenLite.to(this.$refs.hamburgerButtonInner, 0.5, {
rotation: isOpen ? "43deg" : '0',
});
},
}
}
</script>
store.js (imported in the main.js) :
let store = new Vuex.Store({
state : {
menuIsOpen : false,
},
mutations: {
toggleMenu(state) {
state.menuIsOpen = !state.menuIsOpen
}
}
});
I have added basic Example of event bus. you can now compare it with and do changes like wise.
if find any difficulties please comment.
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/vue#2.1.10/dist/vue.min.js"></script>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<div id="app">
<h2>event bus</h2>
<button #click="callChildAnimateMethod"> Button On Parent Call Child </button>
<childcmp><childcmp />
</div>
<script>
var EventBus = new Vue();
Vue.component('childcmp', {
template: `<div>child demo - {{ message }}</div>`,
data: function() {
return {
message: 'hello'
}
},
mounted: function() {
// listen for event
EventBus.$on('animate', this.animButtonHandler);
},
destroyed: function(){
// remove listener
EventBus.$off('animate', this.animButtonHandler);
},
methods: {
animButtonHandler: function() {
console.log('this is child method');
this.message = 'I am changed.'
}
}
});
new Vue({
el: '#app',
data: function() {
return {
}
},
methods: {
callChildAnimateMethod: function() {
EventBus.$emit('animate');
}
}
});
</script>
</body>
</html>
Update
you need to define EventBus
eventbus.js
import Vue from 'vue';
const EventBus = new Vue();
export default EventBus;
parent component - menu.vue
import EventBus from './eventbus.js'
... your code
child component : hamburgerButton.vue :
import EventBus from './eventbus.js'
... your code
now EventBus will be available to your code.
Since you wanted to know how to imtegrate event bus with your code, here it is:
Create an event bus which is just an empty vue instance
Add it in you main.js file or outsource it in a spererate file;
main.js
export const EventBus = new Vue();
menu.vue
<template>
<div class="menu">
<!-- import hamburgerButton component -->
<hamburger-button></hamburger-button>
<transition v-on:enter="enterMenuHandler" v-on:leave="leaveMenuHandler">
<div class="menu_wrapper" v-if="this.$store.state.menuIsOpen">
<ul class="menu_items">
<li class="menu_item" #click="toggleMenu">
<router-link class="menu_link" to="/">home</router-link>
<router-link class="menu_link" to="/contact">contact</router-link>
</li>
</ul>w
</div>
</transition>
</div>
</template>
<script>
import hamburgerButton from "hamburgerButton.vue";
import {EventBus} from './path/to/main.js' //or a path to file where you exported your EventBus
export default {
components: {
'hamburger-button': hamburgerButton,
},
methods: {
toggleMenu(){
this.$store.commit('toggleMenu');
EventBus.$emit('animate-hamburger-btn');
},
enterMenuHandler(el, done){
TweenLite.fromTo(el, 0.5, {
opacity: '0',
},{
opacity: '1',
onComplete: done
});
},
leaveMenuHandler(el, done){
TweenLite.to(el, 0.5, {
opacity: '0',
onComplete: done
});
},
}
}
</script>
set up a event listener on the event bus in the created hook and perform the animation on every animate-hamburger-btn event
hamburgerButton.vue
<template>
<div class="hamburgerButton" #click="animButtonHandler()">
<div class="hamburgerButton_inner" ref="hamburgerButtonInner">
<i class="hamburgerButton_icon></i>
</div>
</div>
</template>
<script>
import {EventBus} from './path/to/main.js' //or a path to file where you exported your EventBus
export default {
created(){
EventBus.$on('animate-hamburger-btn', () => {
this.animateBtn();
});
}.
methods: {
animButtonHandler (){
// toggle the state of menu if button clicked
this.$store.commit('toggleMenu');
this.animateBtn();
},
animateBtn(){
const isOpen = this.$store.state.menuIsOpen === true;
// anim the button
TweenLite.to(this.$refs.hamburgerButtonInner, 0.5, {
rotation: isOpen ? "43deg" : '0',
});
}
}
}
</script>
Context
In Vue 2.0 the documentation and others clearly indicate that communication from parent to child happens via props.
Question
How does a parent tell its child an event has happened via props?
Should I just watch a prop called event? That doesn't feel right, nor do alternatives ($emit/$on is for child to parent, and a hub model is for distant elements).
Example
I have a parent container and it needs to tell its child container that it's okay to engage certain actions on an API. I need to be able to trigger functions.
Vue 3 Composition API
Create a ref for the child component, assign it in the template, and use the <ref>.value to call the child component directly.
<script setup>
import {ref} from 'vue';
const childComponentRef = ref(null);
function click() {
// `childComponentRef.value` accesses the component instance
childComponentRef.value.doSomething(2.0);
}
</script>
<template>
<div>
<child-component ref="childComponentRef" />
<button #click="click">Click me</button>
</div>
</template>
Couple things to note-
If your child component is using <script setup>, you'll need to declare public methods (e.g. doSomething above) using defineExpose.
If you're using Typescript, details of how to type annotate this are here.
Vue 3 Options API / Vue 2
Give the child component a ref and use $refs to call a method on the child component directly.
html:
<div id="app">
<child-component ref="childComponent"></child-component>
<button #click="click">Click</button>
</div>
javascript:
var ChildComponent = {
template: '<div>{{value}}</div>',
data: function () {
return {
value: 0
};
},
methods: {
setValue: function(value) {
this.value = value;
}
}
}
new Vue({
el: '#app',
components: {
'child-component': ChildComponent
},
methods: {
click: function() {
this.$refs.childComponent.setValue(2.0);
}
}
})
For more info, see Vue 3 docs on component refs or Vue 2 documentation on refs.
What you are describing is a change of state in the parent. You pass that to the child via a prop. As you suggested, you would watch that prop. When the child takes action, it notifies the parent via an emit, and the parent might then change the state again.
var Child = {
template: '<div>{{counter}}</div>',
props: ['canI'],
data: function () {
return {
counter: 0
};
},
watch: {
canI: function () {
if (this.canI) {
++this.counter;
this.$emit('increment');
}
}
}
}
new Vue({
el: '#app',
components: {
'my-component': Child
},
data: {
childState: false
},
methods: {
permitChild: function () {
this.childState = true;
},
lockChild: function () {
this.childState = false;
}
}
})
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.2.1/vue.js"></script>
<div id="app">
<my-component :can-I="childState" v-on:increment="lockChild"></my-component>
<button #click="permitChild">Go</button>
</div>
If you truly want to pass events to a child, you can do that by creating a bus (which is just a Vue instance) and passing it to the child as a prop.
You can use $emit and $on. Using #RoyJ code:
html:
<div id="app">
<my-component></my-component>
<button #click="click">Click</button>
</div>
javascript:
var Child = {
template: '<div>{{value}}</div>',
data: function () {
return {
value: 0
};
},
methods: {
setValue: function(value) {
this.value = value;
}
},
created: function() {
this.$parent.$on('update', this.setValue);
}
}
new Vue({
el: '#app',
components: {
'my-component': Child
},
methods: {
click: function() {
this.$emit('update', 7);
}
}
})
Running example: https://jsfiddle.net/rjurado/m2spy60r/1/
A simple decoupled way to call methods on child components is by emitting a handler from the child and then invoking it from parent.
var Child = {
template: '<div>{{value}}</div>',
data: function () {
return {
value: 0
};
},
methods: {
setValue(value) {
this.value = value;
}
},
created() {
this.$emit('handler', this.setValue);
}
}
new Vue({
el: '#app',
components: {
'my-component': Child
},
methods: {
setValueHandler(fn) {
this.setter = fn
},
click() {
this.setter(70)
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.17/dist/vue.js"></script>
<div id="app">
<my-component #handler="setValueHandler"></my-component>
<button #click="click">Click</button>
</div>
The parent keeps track of the child handler functions and calls whenever necessary.
Did not like the event-bus approach using $on bindings in the child during create. Why? Subsequent create calls (I'm using vue-router) bind the message handler more than once--leading to multiple responses per message.
The orthodox solution of passing props down from parent to child and putting a property watcher in the child worked a little better. Only problem being that the child can only act on a value transition. Passing the same message multiple times needs some kind of bookkeeping to force a transition so the child can pick up the change.
I've found that if I wrap the message in an array, it will always trigger the child watcher--even if the value remains the same.
Parent:
{
data: function() {
msgChild: null,
},
methods: {
mMessageDoIt: function() {
this.msgChild = ['doIt'];
}
}
...
}
Child:
{
props: ['msgChild'],
watch: {
'msgChild': function(arMsg) {
console.log(arMsg[0]);
}
}
}
HTML:
<parent>
<child v-bind="{ 'msgChild': msgChild }"></child>
</parent>
The below example is self explainatory. where refs and events can be used to call function from and to parent and child.
// PARENT
<template>
<parent>
<child
#onChange="childCallBack"
ref="childRef"
:data="moduleData"
/>
<button #click="callChild">Call Method in child</button>
</parent>
</template>
<script>
export default {
methods: {
callChild() {
this.$refs.childRef.childMethod('Hi from parent');
},
childCallBack(message) {
console.log('message from child', message);
}
}
};
</script>
// CHILD
<template>
<child>
<button #click="callParent">Call Parent</button>
</child>
</template>
<script>
export default {
methods: {
callParent() {
this.$emit('onChange', 'hi from child');
},
childMethod(message) {
console.log('message from parent', message);
}
}
}
</script>
If you have time, use Vuex store for watching variables (aka state) or trigger (aka dispatch) an action directly.
Calling child component in parent
<component :is="my_component" ref="my_comp"></component>
<v-btn #click="$refs.my_comp.alertme"></v-btn>
in Child component
mycomp.vue
methods:{
alertme(){
alert("alert")
}
}
I think we should to have a consideration about the necessity of parent to use the child’s methods.In fact,parents needn’t to concern the method of child,but can treat the child component as a FSA(finite state machine).Parents component to control the state of child component.So the solution to watch the status change or just use the compute function is enough
you can use key to reload child component using key
<component :is="child1" :filter="filter" :key="componentKey"></component>
If you want to reload component with new filter, if button click filter the child component
reloadData() {
this.filter = ['filter1','filter2']
this.componentKey += 1;
},
and use the filter to trigger the function
You can simulate sending event to child by toggling a boolean prop in parent.
Parent code :
...
<child :event="event">
...
export default {
data() {
event: false
},
methods: {
simulateEmitEventToChild() {
this.event = !this.event;
},
handleExample() {
this.simulateEmitEventToChild();
}
}
}
Child code :
export default {
props: {
event: {
type: Boolean
}
},
watch: {
event: function(value) {
console.log("parent event");
}
}
}
I'm trying a demo on vuejs. Now I want the html title to bind a vm field.
The below is what I tried:
index.html
<!DOCTYPE html>
<html id="html">
<head>
<title>{{ hello }}</title>
<script src="lib/requirejs/require.min.js" data-main="app"></script>
</head>
<body>
{{ hello }}
<input v-model="hello" title="hello" />
</body>
</html>
app.js
define([
'jquery', 'vue'
], function ($, Vue) {
var vm = new Vue({
el: 'html',
data: {
hello: 'Hello world'
}
});
});
But the title seemed not bounded, how to make it work?
There are essentially two ways to solve it.
Use an existing Package
For example, vue-meta:
<template>
<div id="app">
<router-view></router-view>
</div>
</template>
<script>
export default {
name: 'App',
metaInfo: {
// if no subcomponents specify a metaInfo.title, this title will be used
title: 'Default Title',
// all titles will be injected into this template
titleTemplate: '%s | My Awesome Webapp'
}
}
</script>
Create your own Component
Create a vue file containing:
<script>
export default {
name: 'vue-title',
props: ['title'],
watch: {
title: {
immediate: true,
handler() {
document.title = this.title;
}
}
},
render () {
},
}
</script>
Register the component using
import titleComponent from './title.component.vue';
Vue.component('vue-title', titleComponent);
Then you can use it in your templates, e.g.
<vue-title title="Static Title"></vue-title>
<vue-title :title="dynamic.something + ' - Static'"></vue-title>
You can do it with 1 line in the App.vue file, like this:
<script>
export default {
name: 'app',
created () {
document.title = "Look Ma!";
}
}
</script>
Or change the <title> tag content in public/index.html
<!DOCTYPE html>
<html>
<head>
<title>Look Ma!</title> <!- ------ Here ->
</head>
...
This answer is for vue 1.x
using requirejs.
define([
'https://cdn.jsdelivr.net/vue/latest/vue.js'
], function(Vue) {
var vm = new Vue({
el: 'html',
data: {
hello: 'Hello world'
}
});
});
<!DOCTYPE html>
<html id="html">
<head>
<title>{{ hello }}</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.2.0/require.js" data-main="app"></script>
</head>
<body>
{{ hello }}
<input v-model="hello" title="hello" />
</body>
</html>
you can do it like this using the ready function to set the initial value and watch to update when the data changes.
<html>
<head>
<title>Replace Me</title>
</head>
<body>
<script src="https://cdn.jsdelivr.net/vue/latest/vue.js"></script>
<div id="app">
<input v-model="title">
</div>
<script>
new Vue({
el: '#app',
ready: function () {
document.title = this.title
},
data: {
title: 'My Title'
},
watch: {
title: function (val, old) {
document.title = val
}
}
})
</script>
</body>
</html>
also i tried this based on your original code and it works
<html>
<head>
<title>{{ title }}</title>
</head>
<body>
<script src="https://cdn.jsdelivr.net/vue/latest/vue.js"></script>
<div id="app">
<input v-model="title">
</div>
<script>
new Vue({
el: 'html',
data: {
title: 'My Title'
}
})
</script>
</body>
</html>
Just to chime in here. I have read that VueJS wants nothing to do with the meta stuff so I would do such things outside of the "VueJS" realm.
Basically make a plain vanilla js service like below. Here you could add all the functions to handle the meta data stuff such as the Open Graph data.
meta.js
export setTitle(title) {
document.title = title
}
Now we can import the service in main and then provide it to any component in the app who wants it. I could even use my meta service in other projects too which use different frameworks like React or Angular. Portability is super cool!
main.js
import meta from './meta'
new Vue({
router,
render: h => h(App),
provide: {
meta: meta
}
}).$mount('#app')
Here the component injects the meta service it wants to use.
someView.vue
export default {
name: 'someView',
inject: ['meta'],
data: function() {
returns {
title: 'Cool title'
}
},
created: function() {
this.meta.setTitle(this.title);
}
}
This way the meta service is decoupled from the app because different parent components can provide different versions of the meta service. Now you can implement various strategies to see which one is right for you or even different strategies per component.
Basically the inject walks up the component hierarchy and takes the meta service from the first parent who provides it. As long as the meta service follows a proper interface, you're golden.
Decoupling with DI is super cool 😃
Title and meta tags can be edited and updated asynchronously.
You can use state management, create a store for SEO using vuex and update each part accordingly.
Or you can update the element by yourself easily
created: function() {
ajax().then(function(data){
document.title = data.title
document.head.querySelector('meta[name=description]').content = data.description
})
}
If you are using Vuex and want <title> to be part of your application state, then:
create a pageTitle state variable in Vuex
map the state to the template using mapState()
watch it in template, probably add immediate: true to trigger the watcher right away
in watcher, document.title = pageTitle
This will allow you to manage title with Vuex and keep them in sync. I found it useful for SPAs.
By doing this you don't have to mess with your original HTML template, as most of the time Vue root template resides inside <body>.
This is for Vue 2.x.
router.beforeEach((to, from, next) => {
let mohican = to.path; if (mohican == '/') mohican = 'Home'
document.title = mohican.replace('/','');
next();
return;
});
I have an application toolbar component which is common for all pages of my SPA website and is nested in App.vue. In every page I update my common toolbar title in the created hook of the page using Vuex store:
//in every page.vue
created() {
this.$store.commit('toolBar', { pageTitle: this.pageTitle, ... })
},
To automatically update the website title (along with the toolbar title) I use this mutation in the store:
//store.js
toolBar(state,val){
document.title = val.pageTitle
state.toolBar = val
},
Similarly, I use the same mechanism to update e.g. SEO metadata
just pass
:title="data.name"