Vue.js/Laravel: How to pass data between multiple components - javascript

require('./bootstrap');
window.Vue = require('vue');
Vue.component('exampleComponent1', require('./components/exampleComponent1.vue'));
Vue.component('exampleComponent2', require('./components/exampleComponent2.vue'));
const app = new Vue({
el: '#app'
});
from the above code, I want to pass data from exampleComponent1 to exampleComponent2 when some event has occurred in exampleComponent1.
What is the optimal solution for this ??

The key here is to set their parent component as the one receiving from the first (using emit) and sending to the second (using props):
const component1 = Vue.component('component1', {
template: '#component1',
data() { return { name: '' } },
methods: {
updateName() { this.$emit("namechanged", this.name); }
}
});
const component2 = Vue.component('component2', {
template: '#component2',
props: ['name'],
});
new Vue({
el: "#app",
components: { component1, component2 },
data() { return { name: '' } },
methods: {
updateName(newName) { this.name = newName; }
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div><component1 #namechanged="updateName"/></div>
<div><component2 :name="name"/></div>
</div>
<template id="component1"><input v-model="name" #input="updateName"/></template>
<template id="component2"><p>Input From component 1: {{name}}</p></template>

You can use a Event Bus for this.
// in some global file
const EventBus = new Vue();
// creating or emitting event
EventBus.$emit('someEvent', 'some-data')
// listen to the event
EventBus.$on('someEvent', function(data) {
console.log(data) // 'some-data
})

Related

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>`
});
}
}

Call parent's method for Local-Registration component VueJs

I have Vue object:
var app = new Vue({
el: '#my-id',
data() {
return {
example: 1
}
},
methods: {
exampleMethos(data) {
console.log('data', data);
}
},
components: {
'my-component': {
methods: {
callMethod() {
console.log('I want call exampleMethos here');
}
},
template: `
<div>
<input type="checkbox" :change="callMethod()">
</div>`
}
}
});
<div id="my-id">
<my-component></my-component>
<my-component></my-component>
<my-component></my-component>
<my-component></my-component>
<my-component></my-component>
</div>
<script src="https://vuejs.org/js/vue.js "></script>
I know that i can use custom event in my-component:
with send event :
this.$emit('call', 'data');
but i just want call only exampleMethos and my-component is Local-Registration inside 'app'.
Can I call exampleMethos without use #call in html ?
Thank you so much.
here you are,But it's not the best practice, which can result in child components and parent component coupling.
var app = new Vue({
el: '#app',
components: {
child: {
template: `<div><button #click="btnClick">call parent method</button></div>`,
methods: {
btnClick () {
this.$parent.fn()
}
}
}
},
data () {
return {
}
},
methods: {
fn(){
alert('parent method called')
}
}
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<child></child>
</div>

change parent props from child component in vuejs2

I want to change the parent's prop's value from a child component. This works great in vuejs 1 but not in vue 2 (I want to use it in vue.js 2).
Here is a small example :
HTML
<div id="app">
<parent :content="{value:'hello parent'}"><</parent>
</div>
JavaScript
var parent = {
template: '<child :content="content"></child>',
props: ['content'],
};
var child = {
template: '<div>{{ content.value }}<button #click="change">change me</button></div>',
props: ['content'],
methods: {
change() {
this.content.value = "Value changed !";
}
}
};
Vue.component('child', child);
Vue.component('parent', parent);
new Vue({
el: '#app',
});
https://jsfiddle.net/f5gt94f2/
tl;dr: in vue2, you need to use the .sync modifier.
Create a local copy of the content prop in the parent's data (see reason here).
var parent = {
...
data() {
return {
localContent: this.content // creating a local copy, so we can mutate and react to it
}
}
};
Now, pass that localContent to the child, not content. And pass it using .sync so it can be updated:
var parent = {
template: '<div><child :content.sync="localContent"></child></div>',
... // ^^^^^^^^^^^^^^^^^^^^-- changed here
Now, in the child, don't assign to this.content.value, emit an update event instead:
var child = {
...
change() {
this.$emit('update:content', {value: "Value changed !"})
}
}
};
This event, with the new value, will be picked up by the parent and will update its localContent which also will, in consequence, update the child's content prop.
Final running code below.
var parent = {
template: '<div><child :content.sync="localContent"></child><br>At parent: {{ localContent }}</div>',
props: ['content'],
data() {
return {
localContent: this.content
}
}
};
var child = {
template: '<div>At child: {{ content.value }}<button #click="change">change me</button></div>',
props: ['content'],
methods: {
change() {
this.$emit('update:content', {value: "Value changed !"})
}
}
};
Vue.component('child', child);
Vue.component('parent', parent);
new Vue({
el: '#app'
});
<script src="https://unpkg.com/vue#2.5.13/dist/vue.js"></script>
<div id="app">
<parent :content="{value:'hello parent'}"></parent>
</div>
You will have to use emit events for this
Parent:
<child :content="content" #updateParent="updateValue"></child>
methods: {
updateValue (value) {
// Your code here
}
}
Child:
props: ['content'],
methods: {
change () {
this.$emit('updateParent', value)
}
}
https://v2.vuejs.org/v2/guide/components.html#Custom-Events

How to broadcast to all components from root Vue.js

I want to $emit some data to all child components in vue.js 2 and this is my code :
Root Component
const app = new Vue({
created: function(){
// here is some promise and after it done the below line runs
this.$emit('foo', 'bar');
}
});
Child Component
<template></template>
<script>
export default {
created: function() {
this.$on('foo', function(data) {
console.log(data);
});
}
}
<script>
It's not working.
Is there any way to broadcast some data to all child components from root?
Vuejs is bottom up approach, means child component is compiled first and ask for this.$on so broadcasting will not work here with $emit-$on
Use props in child component to watch root data changes, giving this example where child1 and child2 having same root component data named name
var child1 = Vue.extend({
template: "<div>Child Component1 : Niklesh : {{name}} <div v-if='loading'>Loading...</div></div>",
props: ['name','loading']
});
var child2 = Vue.extend({
template: "<div>Child Component1 : Rishi : {{name}} <div v-if='loading'>Loading...</div></div>",
props: ['name','loading']
});
var app = new Vue({
el: "#vue-instance",
data: {
name: "...",
loading: true
},
mounted() {
var vm = this;
setTimeout(function() {
vm.name = "Raut";
vm.loading = false;
}, 1000);
},
components: {
child1,
child2
},
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.1/vue.js"></script>
<div id="vue-instance">
<child1 :name="name" :loading="loading"></child1>
<child2 :name="name" :loading="loading"></child2>
</div>
Use another Vue instance as an Event bus
Code Pen Sample
<div id="app">
<child></child>
</div>
var bus = new Vue()
Vue.component('child', {
data() {
return {
message: 'waiting for event...'
}
},
template: '<div>{{ message }}</div>',
created: function() {
bus.$on('foo', data => {
this.message = data;
})
}
})
var app = new Vue({
el: '#app',
created: function() {
setTimeout(()=> {
bus.$emit('foo', 'bar')
}, 1000)
}
})

Vue.js - One-way data binding updating parent from child

Given that a colon indicates one-way-data-binding in VueJS2, I would like to understand why in this example, the child is able to update the array that was declared in the parent and passed to the child via prop (one-way).
https://jsfiddle.net/ecgxykrt/
<script src="https://unpkg.com/vue"></script>
<div id="app">
<span>Parent value: {{ dataTest }}</span>
<test :datatest="dataTest" />
</div>
var test = {
props: ['datatest'],
mounted: function() {
this.datatest.push(10)
},
render: function() {}
}
new Vue({
el: '#app',
components: {
'test': test
},
data: function() {
return {
dataTest: []
}
}
})
Thanks in advance!
Vue prevents you from assigning to a prop. It does not prevent you from calling a prop's methods or modifying its elements or members, any of which can change the contents of the object. None of these things changes the value of the prop itself, which is a reference to an underlying structure.
A related issue is the fact that Vue cannot detect changes to Array elements or additions/deletions of Object members.
More here.
If you wanted to, you could avoid this by creating a shallow copy and assigning it to a new data item in the child.
https://jsfiddle.net/6xxba1fz/
var test = {
props: ['test'],
data: function() {
return {
myTest: this.test.slice()
}
},
mounted: function() {
this.myTest.push(10)
},
render: function() {}
}
new Vue({
el: '#app',
components: {
'test': test
},
data: function() {
return {
dataTest: []
}
}
})
Please avoid to using the some name for key and value
:datatest="dataTest" Wrong Way
:data-test="dataTest" Better Way (use Kabab case)
HTML
<div id="app">
<span>Parent value: {{ dataTest }}</span>
<test :data-test="dataTest" />
</div>
JS
var test = {
props: {
dataTest:{
type:Number
}
},
mounted: function() {
this.datatest.push(10)
},
render: function() {}
}
new Vue({
el: '#app',
components: {
'test': test
},
data: function() {
return {
dataTest: []
}
}
})
Result:
Parent value: []

Categories