VueJS conditional class binding on route name? - javascript

I have some text that i am trying to change the styling on based on route name.I have set up 2 route names, ads and webs and have set up a watcher to watch the routes. If the route name is either ads or webs i want the text to have color:white;background-color:orange. But i am not sure how to do it.
new Vue({
el: '#app',
data() {
return {
styleClass: null
}
},
watch: {
'$route' (to, from) {
if (to.name == 'ads' || to.name == 'webs') {
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/babel-polyfill/dist/polyfill.min.js"></script>
<div id="app">
<span><h1>Hello!!!</h1></span>
</div>

I'd create a computed property to represent if the current route matches your criteria.
computed: {
highlight () {
return this.$route.matched.some(({ name }) => /^(ad|web)s$/.test(name))
}
}
Then you can use that in a class binding expression.
<span :class="{ highlight }">Text goes here</span>
and in your CSS
.highlight {
color: white;
background-color: orange;
}
See https://router.vuejs.org/api/#route-object-properties for info on the matched property.

I guess you are missing the route vuejs plugin and name your application into the route name too. Here the full example. Hope you get what you want
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }
const routes = [
{ path: '/foo', component: Foo, name: 'ads' },
{ path: '/bar', component: Bar, name: 'nowebs' }
]
const router = new VueRouter({
routes
})
const app = new Vue({
router,
data() {
return {
styleClass: null
}
},
watch: {
'$route' (to, from) {
if (to.name == 'ads' || to.name == 'webs') {
this.styleClass = "color:white;background-color:orange"
} else {
this.styleClass = ""
}
}
}
}).$mount('#app')
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<script src="https://cdn.jsdelivr.net/npm/babel-polyfill/dist/polyfill.min.js"></script>
<div id="app">
<span><h1 :style="styleClass">Hello!!!</h1></span>
<p>
<router-link to="/foo">Go to Foo</router-link>
<router-link to="/bar">Go to Bar</router-link>
</p>
<router-view></router-view>
</div>

Related

Cannot pass props in component from Vue router

Hi I'm trying to pass props from my vue router but it's not printing anything and when logged in mounted it's returning undefined, but its giving value when I'm trying console.log(this.$route.params.id); when I try for this.id returns undefined or rather in my User template its not outputting anything, same code is working in the online tutorial that I'm watching, please help me, is there any modification happened in a recent release
let User = {
props: ['id'],
template: `
<div>Hello # {{id}}</div>
`,
mounted() {
console.log(this.$route.params); // this is returning the value
console.log(this.id); // this is giving undefined
}
}
let App = {
template: `
<div class="wrapper">
<router-view></router-view>
</div>
`
}
let router = new VueRouter({
routes: [{
path: '/user/:id',
component: User,
props: true
}, ],
})
let app = new Vue({
el: '#app',
router: router,
components: {
'app': App
}
})
router.push('/user/1')
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.14/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router#2.0.0/dist/vue-router.js"></script>
<div id="app">
<app></app>
</div>
You are using very old version of Vue Router. Just switch to current version - 3.5.2 - and your code will work as expected....
let User = {
props: ['id'],
template: `
<div>Hello # {{id}}</div>
`,
mounted() {
console.log(this.$route.params); // this is returning the value
console.log(this.id); // this is giving undefined
}
}
let App = {
template: `
<div class="wrapper">
<router-view></router-view>
</div>
`
}
let router = new VueRouter({
routes: [{
path: '/user/:id',
component: User,
props: true
}, ],
})
let app = new Vue({
el: '#app',
router: router,
components: {
'app': App
}
})
router.push('/user/1')
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.14/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router#3.5.2/dist/vue-router.js"></script>
<div id="app">
<app></app>
</div>

Vuejs how to use dynamical template in component?

const CustomComponent = {
props: ['index'],
template: `<span>I am a custom component: {{ index }}</span>`
};
const UserInputResult = {
components: {
CustomComponent
},
props: ['templateString'],
template: `<section v-html="templateString"></section>`
}
const app = new Vue({
el: '#app',
data(){
return {
userInput: 'user input example [:component-1]'
}
},
components: {
UserInputResult
},
methods: {
generateTemplate(){
let raw = this.userInput;
if (!!raw && raw.match(/\[\:component\-\d+\]/g)) {
let components = [...raw.match(/\[\:component\-\d+\]/g)];
components.forEach(component => {
raw = raw.replace(component, `<custom-component :index="${component.match(/\d+/)[0]}"></custom-component>`);
});
}
return raw;
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<textarea v-model="userInput"></textarea>
<user-input-result :template-string="generateTemplate()">
</div>
I want to render a custom component which has a dynamical template base on user input.
when user input a specific string ([:component-1]), it will be render as a component (CustomComponent)
how to achieve this?
Thanks a lot for anyone help!
You should look into v-slot
https://v2.vuejs.org/v2/guide/components-slots.html
Example:
Parent:
<child-component v-html="myTemplate">
<span>From parent</span>
</child-component>
Child:
<div>
<v-slot></v-slot> //Will output "<span>From parent</span>"
</div>
**Added more explaination
You can then condition check and update myTemplate to your desired template. "<span>From parent</span>" is just there for explanation on how slot works.
updated by the questioner
const CustomComponent = {
props: ['index'],
template: `<span>I am a custom component: {{ index }}</span>`
};
const UserInputResult = {
template: `<section><slot></slot></section>`
}
const app = new Vue({
el: '#app',
data(){
return {
userInput: 'user input example [:component-1]'
}
},
components: {
UserInputResult,
CustomComponent
},
methods: {
generateTemplate(){
let raw = this.userInput;
if (!!raw && raw.match(/\[\:component\-\d+\]/g)) {
let components = [...raw.match(/\[\:component\-\d+\]/g)];
components.forEach(component => {
raw = raw.replace(component, `<custom-component :index="${component.match(/\d+/)[0]}"></custom-component>`);
});
}
return raw;
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<textarea v-model="userInput"></textarea>
<user-input-result>
{{ generateTemplate() }}
</user-input-result>
</div>
I figured it out by using Vue.complie
according to dynamically-fetch-and-compile-a-template-with-nuxt
const UserInputResult = {
props: ['templateString'],
render(h){
return h({
components: {
CustomComponent
},
template: `<section>${this.templateString}</section>`
});
}
}

Props not updated when set from router

When I set a component props in the route definition the watcher doesn't work and the view is never updated.
In this example the update works correctly :
const Home = { template: '<div>Home</div>' }
const Test = {
template: '<div>Test : {{ nb }}<br><button #click="addOne" type="button">+1</button></div>',
props: {nb: Number},
methods: {
addOne() {
this.nb++
}
}
}
const router = new VueRouter({
routes: [
{ path: '/', component: Home },
{ name: 'test', path: '/test/:nb', component: Test, props: true }
]
})
new Vue({
router,
el: '#app'
})
<script src="https://npmcdn.com/vue/dist/vue.js"></script>
<script src="https://npmcdn.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<router-link to="/">/home</router-link>
<router-link :to="{ name: 'test', params: { nb: 42 }}">/test</router-link>
<router-view></router-view>
</div>
Note : This example show a "mutating a prop directly" warning, but it's just for the simplicity it's not related to the issue since I don't do that in my code.
In this example the update doesn't work :
const Home = { template: '<div>Home</div>' }
const Test = {
template: '<div>Test : {{ counter.value }}<br><button #click="addOne" type="button">+1</button></div>',
props: {counter: Object},
methods: {
addOne() {
this.counter.value++
console.log('new value : ' + this.counter.value)
}
}
}
class Counter {
constructor(value) {
this.value = value
}
}
const router = new VueRouter({
routes: [
{ path: '/', component: Home },
{
name: 'test',
path: '/test/:nb',
props: (route) => ({counter: new Counter(route.params.nb)}),
component: Test
}
]
})
new Vue({
router,
el: '#app'
})
<script src="https://npmcdn.com/vue/dist/vue.js"></script>
<script src="https://npmcdn.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<router-link to="/">/home</router-link>
<router-link :to="{ name: 'test', params: { nb: 42 }}">/test</router-link>
<router-view></router-view>
</div>
The counter object won't be reactive, so property changes won't be detected. Passing an object as a prop does not add reactivity.
I've just changed one line in your example:
props: (route) => ({counter: Vue.observable(new Counter(route.params.nb))}),
Passing the object to Vue.observable will apply the reactivity.
Note that 'applying reactivity' means rewriting all of the properties to use getters and setters. In the simple example presented here that just means the value property, which should be fine. Vue can only rewrite properties it can see, so any data hidden within closures would not be accessible.
const Home = { template: '<div>Home</div>' }
const Test = {
template: '<div>Test : {{ counter.value }}<br><button #click="addOne" type="button">+1</button></div>',
props: {counter: Object},
methods: {
addOne() {
this.counter.value++
console.log('new value : ' + this.counter.value)
}
}
}
class Counter {
constructor(value) {
this.value = value
}
}
const router = new VueRouter({
routes: [
{ path: '/', component: Home },
{
name: 'test',
path: '/test/:nb',
props: (route) => ({counter: Vue.observable(new Counter(route.params.nb))}),
component: Test
}
]
})
new Vue({
router,
el: '#app'
})
<script src="https://npmcdn.com/vue/dist/vue.js"></script>
<script src="https://npmcdn.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<router-link to="/">/home</router-link>
<router-link :to="{ name: 'test', params: { nb: 42 }}">/test</router-link>
<router-view></router-view>
</div>
Part of the reason why Vue does not automatically apply reactivity to objects passed as props is that the receiving component shouldn't be modifying the object anyway. It's a violation of one-way data flow. In this example you should probably be updating the route directly rather than modifying the counter object.

Set page title with Vue Router

I have created a vue-router with a page title (h1).
Every vue-router link has a meta-title. How can I dynamically set my h1 page title?
I have tried $emit, but this is not working in my router. How can I change my page title?
const dashboard = {
template: `<div>DASHBOARD</div>`
}
const about = {
template: `<div>ABOUT</div>`
}
const routes = [
{path: '/dashboard', component: dashboard, name:'dashboard', meta: { title: 'Dashboard' }},
{path: '/about', component: about, name:'about', meta: { title: 'About' }}
]
const router = new VueRouter({
routes // short for routes: routes
})
router.beforeEach((to, from, next) => {
this.$emit('pagetitle', to.meta.title); // DOESN'T WORK!!
next();
});
new Vue({
el: '#app',
router,
data() {
return {
pagetitle: 'not set'
}
},
watch: {
'$route': function(value) {
// I can use things like value.name === 'about', but "watch" doesn't set the page title at the first page load.
}
}
})
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<router-link to="/dashboard">Dashboard</router-link>
<router-link to="/about">About</router-link>
<router-view></router-view>
<h1 v-text="pagetitle"></h1>
How can I change the page title???
</div>
In your <h1></h1> tag, just use the following
{{ $route.meta.title }}
If you would like to have correct browser history use this one:
router.afterEach((to, from) => {
Vue.nextTick( () => {
document.title = to.meta.title ? to.meta.title : 'default title';
});
})
Also if you need to change the document's title-tag, in your router.beforeEach replace this line :
this.$emit('pagetitle', to.meta.title);
with
document.title = to.meta.title;
It will work.

vue-router access component data

I am playing with an example given from vue-router documentation
HTML:
<div id="app">
<h3>{{title}}</h3>
<router-link to="/">/home</router-link>
<router-link to="/foo">/foo</router-link>
<router-view></router-view>
</div>
JavaScript:
const Foo = { template: '<div>foo</div>', data: {title: "Foo" }
const Bar = { template: '<div>bar</div>', data: {title: "Bar" }
Is it possible to access selected component data outside anchor and update {{title}} header?
Full example is here
I've two possibilities. First one is to edit your parent data directly with this.$parent.title.
And the second way is to trigger an event this.$parent.$emit('changeTitle', 'foo');.
I think the last one is better, because the control of your state is always in your parent component.
const Home = {
template: '<div>Home</div>',
data: {
title: "Home"
},
mounted() {
this.$parent.title = 'home'; // directly
}
}
const Foo = {
template: '<div>Foo</div>',
data: {
title: "Foo"
},
mounted() {
this.$parent.$emit('changeTitle', 'foo'); // emit an event
}
}
const router = new VueRouter({
mode: 'history',
routes: [
{
path: '/',
component: Home
},
{
path: '/foo',
component: Foo
}
]
})
new Vue({
router,
el: '#app',
data: {
title: 'Initial'
},
destroy() {
this.$off('changeTitle');
},
created() {
const _self = this;
this.$on('changeTitle', function(newTitle) {
_self.title = newTitle;
});
}
})
<script src="https://npmcdn.com/vue/dist/vue.js"></script>
<script src="https://npmcdn.com/vue-router/dist/vue-router.js"></script>
<div id="app">
<h3>{{title}}</h3>
<router-link to="/">/home</router-link>
<router-link to="/foo">/foo</router-link>
<router-view></router-view>
</div>

Categories