How to change vue-cookie-law default Props - javascript

I am trying to change buttonText default value of the component vue-cookie-law with Props.
I can change the default value directly from the node_modules plugin source code, but I would like to change it from a Vue Single File Component.
vue-cookie-law - https://www.npmjs.com/package/vue-cookie-law
prop default type
buttonText: 'Got It!'
Since I haven't used Props before, I've been trying few things, below is my CookieLaw.vue component
<template>
<footer>
<cookie-law theme="base">
<div slot="message">
We use cookies to enhance your experience. By continuing to visit our site you agree to our use of cookies.
<router-link to="terms_and_conditions">View Policy</router-link>
</div>
</cookie-law>
</footer>
</template>
<script>
import CookieLaw from "vue-cookie-law";
export default {
props: {
buttonText: {
default: "Agree"
}
},
components: { CookieLaw }
};
</script>
The props are not changing the default of buttonText.

buttonText is one of the default props for the vue-cookie-law component as you know ... not the parent component (the one that you import it on ) so you have to bind them to the component it self :
<cookie-law theme="base" buttonText="Agree">
...
</cookie-law>
Or bind a dynamic value :
<script>
import CookieLaw from "vue-cookie-law";
export default {
data() {
return {
text: 'Agree'
}
}
components: {
CookieLaw
}
}; <
</script>
<cookie-law theme="base" :buttonText="text">
...
</cookie-law>

Related

Add a class to a nabar modal in a component Vue.js

I wanted to know how can I add a class to a modal in a navbar components? My navbar is in App.vue and I wanted to create a message that would add the class "is-active" to a modal in my navbar when I click on it. But I can't find the way to do that..
Thank you
Usually when you have a parent -> child relationship you can use events. In this case since you have two components that are not linked (directly) then you have two alternatives.
Using store (it is usually used in cases where your application is of a considerate size)
You can use vuex to have a central place where you will have your global state. A simple example would be:
store/main.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
isModalOpen: false
},
getters: {
isModalOpen => (state) => state.isModalOpen,
},
mutations: {
setIsModalOpen (state, isOpen) {
state.isModalOpen = isOpen;
}
}
})
then you can access the store in your component as such:
<template>
<navbar :class="[isNavBarOpen ? "is-active" : ""]" />
</template>
export default {
computed: {
isNavBarOpen () {
this.$store.getters['isModalOpen']
}
}
}
Event bus (it is usually used in cases where you have a small app and do not need a global state manager)
Read more about EventBus here.
You can create a simple EventBus
services/eventBus.js
import Vue from 'vue';
const export EventBus = new Vue();
then on your component when the modal is open you can do:
// # -> is an alias to your root folder. Most projects scafolded by Vue CLI has this by default
import {EventBus} from "#/services/eventBus"
export default {
methods: {
openStore: () => {
// your logic to open modal
EventBus.$emit('modal-open');
}
}
}
then on your App.vue you just listen to this event
App.vue
<template>
<navbar :class="[isModalOpen ? "is-active" : ""]" />
</template>
// # -> is an alias to your root folder. Most projects scafolded by Vue CLI has this by default
import {EventBus} from "#/services/eventBus"
export default {
data() {
return {
isModalOpen: false,
}
},
created() {
EventBus.$on('modal-open', this.onModalOpen);
},
methods: {
onModalOpen() {
this.isModalOpen = true;
}
}
}
The one you will pick depends on our application structure and if you think it is complex enough to use a central state management (vuex).
There might contain some errors in the code but the main idea is there.

component not handling empty prop

I have a component data.table.vue and other components for eg abc.vue and xyz.vue.
inside this data.table.vue, a paragraph to render depending on the prop received by the it.. However, not both my components abc.vue and xyz.vue will send props.. only abc.vue needs to send props.. for eg:
in abc.vue:
<template>
<data-table
:isShown=true
<data-table>
</template>
and in xyz.vue no props
<template>
<data-table
</data-table>
</template>
and in data.table.vue
<p v-if="isShown"> hello world </p>
but I want this paragraph to be always shown for xyz component..
and only for abc.vue, i want this paragraph to render according to the props isShown.. However, even in xyz.vue , its being rendered depending on the props sent in abc.vue..
Please help ..
You can set a default prop like this.
export default {
props: {
isShown: {
type: Object,
default: true
}
}
}
Default will be taken when no props are passed.
For Vue3 with the composite API you can set default props like this:
<script lang="ts">
import { defineComponent } from 'vue'
export default defineComponent({
props: {
isShown: {
type: boolean,
default: true
},
},
setup() {},
})
</script>
And with the script setup
<script setup lang="ts">
const props = withDefaults(
defineProps<{
isShown: boolean
}>(),
{
isShown: true
}
)
</script>

How I can make #Model and #Emit work together in VueJs with Typescript?

can anyone help me with decorators #Model and #Emit?
I'm trying to change order on click in my component and used documentation from here: https://github.com/kaorun343/vue-property-decorator.
Here is my code:
<template>
<button #click="onSortClick">Sort</button>
</template>
<script lang="ts">
import Vue from "vue";
import { Emit, Componet, Model } from "vue-property-decorator";
export default class MyButton extends Vue {
#Model("sort", { type: String, default: "none" }) readonly order!: string;
#Emit("sort")
onSortClick() {
const nextSortOrder = {
ascending: "descending",
descending: "none",
none: "ascending"
};
return nextSortOrder[this.order];
}
}
</script>
But when I click the button, the value of variable "order" is not changing. Am I doing something wrong?
Yes, you are. There are a few things wrong here.
You need to import vue like this import { Vue, Component, Model, Emit } from 'vue-property-decorator;
The class needs to have an #Component decorator like this
#Component({/* Additional data can go in here */})
export default class MyButton extends Vue {}
This isn't how vue intended emitting of events to work. You cannot alter the value of order because it is a readonly property within the same file. If you place your button in another component like this
// ParentFile.vue
<template>
<my-button #sort="order = $event"></my-button>
</template>
<script lang="ts">
import { Component, Vue, Watch } from 'vue-property-decorator';
import MyButton from '#/components/MyButton.vue';
#Component({
components: {
MyButton
}
})
export default class Home extends Vue {
order = 'Wow';
#Watch('order')
orderChanged(newVal: string) {
// eslint-disable-next-line no-console
console.log(newVal); // order will change here
}
}
</script>
and listen to the emitted event as above then the order variable in the parent component will change but not the child.

Change a property's value in one component from within another component

I'm trying to wrap my head around hoe Vue.js works, reading lots of documents and tutorials and taking some pluralsight classes. I have a very basic website UI up and running. Here's the App.vue (which I'm using kinda as a master page).
(To make reading this easier and faster, look for this comment: This is the part you should pay attention to)...
<template>
<div id="app">
<div>
<div>
<CommandBar />
</div>
<div>
<Navigation />
</div>
</div>
<div id="lowerContent">
<!-- This is the part you should pay attention to -->
<template v-if="showLeftContent">
<div id="leftPane">
<div id="leftContent">
<router-view name="LeftSideBar"></router-view>
</div>
</div>
</template>
<!-- // This is the part you should pay attention to -->
<div id="mainPane">
<div id="mainContent">
<router-view name="MainContent"></router-view>
</div>
</div>
</div>
</div>
</template>
And then in the same App.vue file, here's the script portion
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import CommandBar from './components/CommandBar.vue';
import Navigation from './components/Navigation.vue';
#Component({
components: {
CommandBar,
Navigation,
}
})
export default class App extends Vue {
data() {
return {
showLeftContent: true // <--- This is the part you should pay attention to
}
}
}
</script>
Ok, so the idea is, one some pages I want to show a left sidebar, but on other pages I don't. That's why that div is wrapped in <template v-if="showLeftContent">.
Then with the named <router-view>'s I can control which components get loaded into them in the `router\index.ts\ file. The routes look like this:
{
path: '/home',
name: 'Home',
components: {
default: Home,
MainContent: Home, // load the Home compliment the main content
LeftSideBar: UserSearch // load the UserSearch component in the left side bar area
}
},
So far so good! But here's the kicker. Some pages won't have a left side bar, and on those pages, I want to change showLeftContent from true to false. That's the part I can't figure out.
Let's say we have a "Notes" component that looks like this.
<template>
<div class="notes">
Notes
</div>
</template>
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
#Component
export default class Notes extends Vue {
data() {
return {
showLeftContent: false // DOES NOT WORK
}
}
}
</script>
Obviously, I'm not handling showLeftContent properly here. It would seem as if the properties in data are scoped only to that component, which I understand. I'm just not finding anything on how I can set a data property in the App component and then change it in a child component when that child is loaded through a router-view.
Thanks!
EDIT:
I changed the script section of the Notes component from:
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
#Component
export default class Notes extends Vue {
data() {
return {
showLeftContent: false // DOES NOT WORK
}
}
}
</script>
to:
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
#Component
export default class Notes extends Vue {
mounted() {
this.$root.$data.showLeftContent = false;
}
}
</script>
And while that didn't cause any compile or runtime errors, it also didn't have the desired effect. On Notes, the left side bar still shows.
EDIT 2:
If I put an alert in the script section of the Notes component:
export default class Notes extends Vue {
mounted() {
alert(this.$root.$data.showLeftContent);
//this.$root.$data.showLeftContent = false;
}
}
The alert does not pop until I click on "Notes" in the navigation. But, the value is "undefined".
EDIT 3:
Struggling with the syntax here (keep in mind this is TypeScript, which I don't know very well!!)
Edit 4:
Inching along!
export default class App extends Vue {
data() {
return {
showLeftContent: true
}
}
leftContent(value: boolean) {
alert('clicked');
this.$root.$emit('left-content', value);
}
}
This does not result in any errors, but it also doesn't work. The event never gets fired. I'm going to try putting it in the Navigation component and see if that works.
As it says on #lukebearden answer you can use the emit event to pass true/false to the main App component on router-link click.
Assuming your Navigation component looks like below, you can do something like that:
#Navigation.vue
<template>
<div>
<router-link to="/home" #click.native="leftContent(true)">Home</router-link> -
<router-link to="/notes" #click.native="leftContent(false)">Notes</router-link>
</div>
</template>
<script>
export default {
methods: {
leftContent(value) {
this.$emit('left-content', value)
}
}
}
</script>
And in your main App you listen the emit on Navigation:
<template>
<div id="app">
<div>
<Navigation #left-content="leftContent" />
</div>
<div id="lowerContent">
<template v-if="showLeftContent">
//...
</template>
<div id="mainPane">
//...
</div>
</div>
</div>
</template>
<script>
//...
data() {
return {
showLeftContent: true
}
},
methods: {
leftContent(value) {
this.showLeftContent = value
}
}
};
</script>
A basic approach in a parent-child component relationship is to emit events from the child and then listen and handle that event in the parent component.
However, I'm not sure that approach works when working with the router-view. This person solved it by watching the $route attribute for changes. https://forum.vuejs.org/t/emitting-events-from-vue-router/10136/6
You might also want to look into creating a simple event bus using a vue instance, or using vuex.
If you'd like to access the data property (or props, options etc) of the root instance, you can use this.$root.$data. (Check Vue Guide: Handling Edge)
For your codes, you can change this.$root.$data.showLeftContent to true/false in the hook=mounted of other Components, then when Vue creates instances for those components, it will show/hide the left side panel relevantly.
Below is one demo:
Vue.config.productionTip = false
Vue.component('child', {
template: `<div :style="{'background-color':color}" style="padding: 10px">
Reach to root: <button #click="changeRootData()">Click me!</button>
<hr>
<slot></slot>
</div>`,
props: ['color'],
methods: {
changeRootData() {
this.$root.$data.testValue += ' :) '
}
}
})
new Vue({
el: '#app',
data() {
return {
testValue: 'Puss In Boots'
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<h2>{{testValue}}</h2>
<child color="red"><child color="gray"><child color="green"></child></child></child>
</div>

Vue control and update state from another component with vuex

For example, I have a file named App.vue and there I have navigation drawer component. And I have a file named Home.vue with the toolbar component. The thing is that I need to toggle navigation drawer state(true or false) from the Home.vue's toolbar component(also, the navigation drawer component is rendered in Home.vue). The code below doesn't return any error and doesn't change the navigation drawer visibility. Also, if set state manually in store.js, navigation drawer follows it. Can anyone please help?
store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
drawer: false
},
mutations: {
toggleDrawer: function(state) {
return state.drawer = !state.drawer;
}
},
actions: {
toggleDrawer({ commit }) {
commit('toggleDrawer');
}
},
getters: {
active: (state) => {
return state.drawer;
}
}
})
App.vue
<v-navigation-drawer v-model="drawer"> ... </v-navigation-drawer>
<script>
import store from './store'
export default {
data: function() {
return {
drawer: this.$store.state.drawer
}
}
}
</script>
Home.vue
<v-toolbar-side-icon #click="$store.commit('toggleDrawer')"> ... </v-toolbar-side-icon>
<script>
import store from '../store'
export default {
data: function() {
return {
drawer: this.$store.state.drawer // Seems like I don't need it here
}
}
}
</script>
This is an older post, but in case anyone come looking for the answer, this seems to work.
from the Vuex guide, Form Handling section, Two-way Computed Property
I modified the codesandbox provided by Sovalina (thanks!) link
The other way is to use v-model
<v-navigation-drawer v-model="drawer" app>
with the two way computed property, instead of mapGetters
<script>
export default {
computed: {
drawer: {
get () {
return this.$store.state.drawer
},
set (value) {
this.$store.commit('toggleDrawer', value)
}
}
}
}
</script>
You can use the navigation drawer's property permanent instead of v-model (to avoid mutate your store outside vuex) and use the getter active you defined.
App.vue:
<template>
<v-app >
<v-navigation-drawer :permanent="active">
...
</v-navigation-drawer>
</v-app>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
computed: {
...mapGetters([
'active'
])
},
}
</script>
Home.vue:
<template>
<v-toolbar-side-icon #click="toggle"> ... </v-toolbar-side-icon>
</template>
<script>
export default {
methods: {
toggle() {
this.$store.dispatch('toggleDrawer')
}
}
}
</script>
Note: as you defined an action toggleDrawer in your store you can use dispatch instead of commit.
Live example here

Categories

Resources