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
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.
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
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'm trying to synchronize a prop between a parent and child in vue.js.
I have a parent component that includes a child component with a prop. When the prop changes in the parent, the prop is updated in the child and when the prop changes in the child, the parent is updated.
I started with this example:
The html where my component is mounted
<div>
<div id="app">
</div>
</div>
And the parent + child:
Vue.component('myinput', {
template: '<div><input v-model="mytext" /></div>',
props: ['text'],
data() {
return {
mytext: this.text
}
}
})
const vm = new Vue({
el: '#app',
template: '<div> <myinput :text="mytext"></myinput> <p> {{mytext}} </p></div>',
data() {
return {
mytext: "Hello World!"
}
}
});
Since I cannot mutate the props, I need a local copy [Example Codepen].
If I want my parent component to be updated when the child change, I need to $emit the value.
For that, I created a wrapper around mytext in the child component:
computed: {
wrapperMyText: {
get() {
return this.mytext;
},
set(v) {
this.mytext = v;
this.$emit('update:text', v);
}
}
I use wrapperMyText in the model. [Example Codepen]
Now, if I want the binding the other way (parent to child) I need to add a watcher in the child.
watch: {
text(v) {
this.mytext = v;
}
},
[Example Codepen].
In the example, there is only one prop, I need to create a watcher and a computed property. If I have 5 properties, I will need 5 watchers and 5 computed properties. That means the component code is polluted (too much code) with all this stuff. So my questions are:
Is this the standard pattern to sync a prop?
Is it better in this case to use a Store (like vuex)?
Why is so much code necessary to do a simple case?
After reflection, it seems I'm trying to re code v-model with properties.
A single computed property will accomplish everything you are attempting.
Vue.component('myinput', {
template: '<div><input v-model="wrapperMyText"></div>',
props: ['text'],
computed: {
wrapperMyText: {
get() { return this.text;},
set(v) { this.$emit('update:text', v);}
}
}
})
Here is your updated pen.
So to answer your questions:
This is a standard pattern.
If you put the state in Vuex, your input component will no longer be re-usable.
You don't need all that code.
I would like to separate the business logic and the template of a component.
In React I would use the Container/Presentation pattern.
const Container = (props) => <Presentational ...props/>
But what is the equivalent with vue.js?
Say I have this all in one component (did not test this one, it just for example) :
<template>
<div id="app">
<div v-for="user in users">
{{user.name}}
</div>
</div>
</template>
<script>
Vue.component({
el: '#app',
props: {
filter: "foo"
},
data: {
users: [],
},
ready: function () {
this.$http.get('/path/to/end-point?filter='+this.props.filter)
.then(function (response) {
this.users = response.data
})
}
})
</script>
How I could extract a container with just the fetch logic?
I really don't see a need for a container component. Abstract your fetch code out into a vuex action and bind your store state within the component using computed properties.
You can extend any Vue component, which will allow you to override any methods from the parent. So, you can create a base component and then extend that:
const Base = Vue.component('base-comp', {
template: "#base",
data() {
return {
name: 'foo'
}
}
});
const Child = Base.extend({
data() {
return {
name: 'bar'
}
}
});
Vue.component('child-comp', Child)
You can check out the JSFiddle here: https://jsfiddle.net/tdgxdhz9/
If you are using single file components, then it's simply a case of importing the base component and extending it, which keeps the original template in tact:
import Base from './BaseComponent.vue'
Base.extend({
// Javascript code here
})
You can use vuex-connect to create container components like in React. Here an example of project : https://github.com/pocka/vue-container-component-example
What you are looking for is Vue Mixins.
You can write a mixins file which contains your business logic and import it into your Vue components.
Link:- https://v2.vuejs.org/v2/guide/mixins.html