I have started using Vuetify, but I am looking for a way to modify the default props on some components.
Is there a way to do this?
i.e. Instead of constantly having to write:
<v-layout wrap></v-layout>
Can I make layouts default prop for wrap be true?
something along these lines, but beware if you are new to vue.js you'll have to do some reading:
relevant doc: vue mixin, vue extends
js
// some already existing component, you need to get it somehow
// most likely via `import <something-to-import>`
let theExternalComponent = {
props: { wrap: { default: false, type: Boolean } },
template: "<li>wrap:{{wrap}}</li>"
};
// this simulates the global registration
Vue.component("v-some-external-component", theExternalComponent);
// -- lets start --
// lets extend that component - and overwrite the default prop for wrap
let extendedExternalwithOtherDefaults = {
extends: theExternalComponent,
mixins: [{ props: { wrap: { default: true } } }],
};
var app = new Vue({
el: "#app",
components: { "v-my-customized-component": extendedExternalwithOtherDefaults }
});
html (pug actually but that does not matter here)
div(id="app")
ul
v-some-external-component
v-some-external-component(wrap)
v-my-customized-component
// now defaults to wrap:true
v-my-customized-component(:wrap="false")
// you can still set the wrap to false if required
output
wrap:false
wrap:true
wrap:true
wrap:false
codepen: https://codepen.io/anon/pen/MLxbEW
Related
import Selector from '#components/contactSelector'
let vueInstance = new Vue({
components: { Selector },
data () {
return {
url: url,
isFilter: false,
type: 'external',
selectedList: []
}
},
render (h) {
return h(Selector, { props: { url, selectedList: this.selectedList, maxHeight: '300px' } })
}
})
I have an above Vue instance created, can we call to render it on multiple divs. I have tried but this seems not working is there any way to mount on multiple divs or it is meant to be like this only single div it will be mounted. If anyone knows please help here. Thanks.
vueInstance.$mount('#contactSelectorId')
vueInstance.$mount('#contactSelectorId2')
I believe that what you are trying to do can be achieved by having a root component which can include your component as a child component.
If you need that one component will appear several times in the same page, this is the direction:
Base component:
import ChildComponent from './ChildComponent';
export default {
components: {
ChildComponent
},
:
:
Then you can simply include it (<child-component/>) in any place you need.
Notice, that in this case the new parent component is the one you need to assign to your vueInstance in order to become the root.
there is User.js class and user object(user = new User();).
The user object is being used in all nested components. in User class there are so many important methods.
How can I simply use/access this.user or this.$user and its methods in any component?
1-solution (temporary working solution): Setting user in vuex's store and define in all components' data:
data(){
return {
user:this.$store.state.user
}
}
Cons: in every component, this should be added. Note: there are so many components.
2-solution: adding user to Vue's prototype like plugin:
Vue.prototype.$user = user
Cons: when user's data changes, it doesn't effect in DOM element (UI).
3-solution: putting to components's props.
Cons: in every component, this should be added. Note: Again there are so many components.
All of the solutions I found have issues, especially as the project gets larger and larger.
Any suggestion and solution will be appreciated!
Note: Applies for Vue 2x
Proposal 1: Using getters from vuex
You could use getters along with mapGetters from Vuex to include users within computed properties for each component.
Vuex
getters: {
// ...
getUser: (state, getters) => {
return getters.user
}
}
component
import { mapGetters } from 'vuex'
computed: {
...mapGetters([getUser])
}
Proposal 2: add a watcher via plugin
Vue
// When using CommonJS via Browserify or Webpack
const Vue = require('vue')
const UserPlug = require('./user-watcher-plugin')
// Don't forget to call this
Vue.use(UserPlug)
user-watcher-plugin.js
const UserPlug = {
install(Vue, options) {
// We call Vue.mixin() here to inject functionality into all components.
Vue.watch: 'user'
}
};
export default UserPlug;
Proposal 3: add a computed property user as plugin via mixin
Vue
// When using CommonJS via Browserify or Webpack
const Vue = require('vue')
const UserPlug = require('./user-watcher-plugin')
// Don't forget to call this
Vue.use(UserPlug)
user-watcher-plugin.js
const UserPlug = {
install(Vue, options) {
// We call Vue.mixin() here to inject functionality into all components.
Vue.mixin({
computed: {
user: function() {
return this.$store.state.user
}
}
})
}
};
export default UserPlug;
Based on #Denis answer, specifically Proposal 3, Here is the UserPlugin.js:
import store from '#/store/store';
import User from './User';
const UserPlugin = {
install(Vue) {
const $user = new User();
window.$user = $user;
store.commit('setUser', $user);
Vue.mixin({
computed: {
$user() {
return store.state.user;
}
}
});
}
};
export default UserPlugin;
and main.js:
import UserPlugin from './common/UserPlugin';
Vue.use(UserPlugin);
new Vue({
render: h => h(App)
}).$mount('#app');
For further usage, I published small library for solving these kinda issues:
https://www.npmjs.com/package/vue-global-var
Assuming you don't actually use all methods/attributes of user in every component, but a subset of them everytime, I don't see any reason why solution 1 & 2 do not work for you, since passing the whole user object to every component is not necessary.
Let's say your object User have some attributes (a1, a2, a3, etc.) and methods (m1, m2, m3...). If a component only needs some of them (e.g. a1, a2, m1, m2, m3) then with Vuex, you can use mapping functions (mapState, mapGetters, mapMutations and mapActions) to get the exact info from user
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex'
export default {
computed: {
...mapState('user', [ 'a1' ]),
...mapGetters('user', [ 'a2' ])
},
methods: {
...mapMutations('user', [ 'm1' ]),
...mapActions('user', [ 'm2', 'm3' ])
}
}
For solution 2 (using prototype), to make component update when user data changes, you can map the necessary data to component via methods.
export default {
methods: {
userA1() {
return this.$user.attributes.a1;
},
userM1() {
this.$user.methods.m1();
}
// and so on
}
}
Even better, you can create mixins to explicitly map data from user, and reuse your mixins to avoid duplicated code in components. It can be applied for both Vuex solution and prototype solution.
// mixin1:
const mixin1 = {
computed: {
...mapState('user', [ 'a1' ]),
},
methods: {
...mapMutations('user', [ 'm1' ])
}
}
// mixin2:
const mixin2 = {
computed: {
...mapGetters('user', [ 'a2' ]),
},
methods: {
...mapActions('user', [ 'm2', 'm3' ])
}
}
// component1
export default {
mixins: [ mixin1 ]
}
// component 2
export default {
mixins: [ mixin1, mixin2 ]
}
But if you really need to pass the whole object user to every component, then nothing could do. Rather, you should review your implementation and see if there is any better way to break the object into smaller meaningful ones.
You can use mixins to add User.js to your root component like
import userLib from './User';
//User.js path should correct
Then
var app = new Vue({
router,
mixins: [
userLib
],
//.....
});
After that you can use any of these User method in your any component like
this.$parent.userClassMehtod();
or if any data access
this.$parent.userClassData;
Finally dont forget to add export default{//..} in User.js
Note: This is only work if you export all method of User.js into export default
I just created the minimal codesandbox to clear the idea of how dependency Injection works in vue.
You can have a second Vue instance and declare a reactive property.
See: Reactivity in depth
I have a classic Vue Component like this
Vue.component('bar', {
template: `<div class="bar"></div>`,
data () {
return {
blocks: [
]
}
}
});
And also i have Vue Instance like this.
new Vue ({
el: '#app',
data: {
value: 1
},
methods: {
add: function() {
// here to do
}
}
});
When add function work, i have to add to data component blocks value. I can't use Vuex and what is this other solutions?
Since this is a parent -> child communication, you can very simply use props:
Store blocks on the parent component.
Pass blocks as a prop to bar component.
Do any additional processing using computed properties.
Display data using child's template.
Here's some guiding:
Define your component with props:
Vue.component('bar', {
template: `<div class="bar">{{blocks.length}}</div>`,
props: ['blocks']
});
Define blocks on the parent (in your example, the main vue component):
new Vue ({
el: '#app',
data: {
blocks: []
},
methods: {
add: function() {
// do something with blocks
}
}
});
Pass data down to the component:
<bar :blocks="blocks"></bar>
You can use the way which called “Event bus”
https://alligator.io/vuejs/global-event-bus/
Or you can create your own state management
https://v2.vuejs.org/v2/guide/state-management.html#Simple-State-Management-from-Scratch
But i highly recommend you too use vuex, since it will become hard to manage events and state when the project grows
I have a component with one method, which I'm firing on creation. It's using vue-select but purpose of this component shouldn't be relevant to my issue.
<template>
<v-select :on-change="onchangecallback"></v-select>
</template>
<script>
import Vue from 'vue'
import vSelect from 'vue-select'
Vue.component('v-select', vSelect);
export default {
methods: {
onchangecallback: () => {alert('default')}
},
created: function() {
this.onchangecallback();
}
}
</script>
In other file I'm importing this component and creating a new instance of it with Vue constructor and passing new onchangecallback method, which, by my understanding, should overwrite the default onchangecallback method:
import VSelect from './components/ui/VSelect.vue';
new Vue({
VSelect,
el: '#app',
components: {VSelect},
template: `<v-select />`,
methods: {
onchangecallback: () => {alert('custom')} // doesn't work
}
});
But when I start the app, instead of alert('custom') I still get alert('default').
I don't know what you are trying to achieve.
But here is my solution https://codesandbox.io/s/84qw9z13v9
You need to define a prop to pass your new callback function to that component (through the prop)
props: {
onchangecallback: {
type: Function,
default() {
return function() {
alert('default');
};
},
},
},
created: function() {
this.onchangecallback();
}
And the use it instead the default one.
Check all the code in that snippet.
For communication between components you're supposed to use events. Emit an event in a child component and make the parent component listen to it:
In the child component:
created() {
this.$emit('change')
}
In the parent component:
Template:
<child-component v-on:change="doSomething" />
Methods:
methods: {
doSomething() {
// ...
}
}
Explanation
In the child component you emit an event, in this case "change", whenever something changed. In your case this was upon creation of the component.
In the parent component you tell Vue that, whenever the child component emits a "change" event, it should run the method "doSomething" in your parent component.
This is used in many places, i.e. inputs, where the input emits an event "input" whenever its content changes and any parent can listen for that by using v-on:input="methodHere".
I am trying to pass a variable (here, externalVar) to a component, directly when initializing. But I can't find how to do it (probably not understanding documentation well :/ ). What is the correct way to do it?
The initialization :
var externalVar = "hello world"
const leftmenu = new Vue({
el: "#left-menu",
template: "<CLM/>",
components: {CLM},
variableToPass: externalVar
});
The component I am initializing here is defined like this (getting back variableToPass in data):
<template src="./template-l-m.html"></template>
<script>
import draggable from 'vuedraggable';
export default {
name: 'leftmenu',
components: {
draggable
},
data () {
return {
jsonObject: this.variableToPass,
}
},
[ ... ]
</script>
But then , when I am trying to use this.jsonObject, it says that it's undefined. What am I doing wrong ?
If i understand you correctly you want to use props to pass data to child components
Dynamically bind a prop attribute on child component element using :variable="variableToPass"
var externalVar = "hello world"
const leftmenu = new Vue({
el: "#left-menu",
template: "<CLM :variable='variableToPass'/>",
components: {CLM},
data: {
variableToPass: externalVar
}
});
Define a props option in the child component
<template src="./template-l-m.html"></template>
<script>
import draggable from 'vuedraggable';
export default {
name: 'leftmenu',
components: {
draggable
},
props: ['variable'],
data () {
return {
jsonObject: this.variable,
}
},
[ ... ]
</script>
Use data.
var externalVar = "hello world"
const leftmenu = new Vue({
el: "#left-menu",
template: "<CLM/>",
components: {CLM},
data: {
variableToPass: externalVar
}
});
That way you can access your variable like this this.$data.variableToPass
Use $options
in child component
mounted() {
console.log(this.$parent.$options.variableToPass) // hello world
this.jsonObject = this.$parent.$options.variableToPass
}