Vue instance inside Vue instance - javascript

Sup people!
I got this HTML code here:
// index.html
<div data-init="component-one">
<...>
<div data-init="component-two">
<button #click="doSomething($event)">
</div>
</div>
This basically references a Vue instance inside another Vue instance if I understood everything correctly. The respective JS code is split up in two files and looks like this:
// componentOne.js
new Vue(
el: '[data-init="component-one"]',
data: {...},
methods: {...}
);
// componentTwo.js
new Vue(
el: '[data-init="component-two"]'
data: {...}
methods: {
doSomething: function(event) {...}
}
);
Now, the problem with this is, that doSomething from componentTwo never gets called.
But when I do some inline stuff, like {{ 3 + 3 }}, it gets computed like it should. So Vue knows there is something. And it also removes the #click element on page load.
I tried fiddling around with inline-template as well, but it doesn't really work as I'd expect it to in this situation. And I figured it isn't meant for this case anyway, so I dropped it again.
What would the correct approach be here? And how can I make this work the easiest way possible with how it's set up right now?
The Vue version we use is 2.1.8.
Cheers!

The problem is that you have two vue instances nested to each other.
If the elements are nested, then you should use the same instance or try components
https://jsfiddle.net/p16y2g16/1/
// componentTwo.js
var item = Vue.component('item',({
name:'item',
template:'<button #click="doSomething($event)">{{ message2 }</button>',
data: function(){
return{
message2: 'ddddddddddd!'
}},
methods: {
doSomething: function(event) {alert('s')}
}
}));
var app = new Vue({
el: '[data-init="component-one"]',
data: {
message: 'Hello Vue!'
}
});
<div data-init="component-one">
<button >{{ message }}</button>
<item></item>
</div>
Separate instances work if they are independant of each other.
as follows:
https://jsfiddle.net/p16y2g16/
var app = new Vue({
el: '[data-init="component-one"]',
data: {
message: 'Hello Vue!'
}
});
// componentTwo.js
var ddd = new Vue({
el: '[data-init="component-two"]',
data: {
message: 'ddddddddddd!'
},
methods: {
doSomething: function(event) {alert('s')}
}
});

But when I do some inline stuff, like {{ 3 + 3 }}, it gets computed like it should. So Vue knows there is something.
Because you have parent instance 'componentOne'. It activated Vue for this template. If you need to set another instance inside, you have to separate part of template. Example (it can lag in snippet!) .
Alternative
https://jsfiddle.net/qh8a8ebg/2/
// componentOne.js
new Vue({
el: '[data-init="component-one"]',
data: {
text: 'first'
},
methods: {}
});
// componentTwo.js
new Vue({
el: '[data-init="component-two"]',
data: {
text: 'second'
},
template: `<button #click="doSomething($event)">{{text}}</button>`,
methods: {
doSomething: function(event) {
console.log(event);
}
}
});
<script src="https://vuejs.org/js/vue.min.js"></script>
<div data-init="component-one">
{{text}}
</div>
<div data-init="component-two">
</div>

The button element inside component-two is referenced as a slot in Vue.
The evaluation of the #click directive value happens in the parent component (component-one, which host component-two). Therefor, you need to declare the click handler over there (over component-one).
If you want the handler to be handled inside component-two, you should declare a click directive for the slot element in it's (component-two) template, and pass the handler function, for instance, as a pop.
good luck.

You're doing everything right except you've nested the 2nd Vue instance inside the 1st. Just put it to the side and it will work as expected.
Vue ignores binding more than once to the same element to avoid infinite loops, which is the only reason it doesn't work nested.

Use vue-cli to create a webpack starter app. vue init app --webpack
Then, try to structure your components this way. Read more: https://v2.vuejs.org/v2/guide/components.html#What-are-Components
This is main.js
import Vue from 'vue'
import ComponentOne from './ComponentOne.vue'
import ComponentTwo from './ComponentTwo.vue'
new Vue({
el: '#app',
template: '<App/>',
components: {
ComponentOne,
ComponentTwo
}
})
This is ComponentOne.vue
<template>
<div class="user">
<div v-for="user in users">
<p>Username: {{ user.username }}</p>
</div>
</div>
</template>
<script>
export default {
data () {
return {
users: [
{username: 'Bryan'},
{username: 'Gwen'},
{username: 'Gabriel'}
]
}
}
}
</script>
This is ComponentTwo.vue
<template>
<div class="two">
Hello World
</div>
</template>
<script>
export default {
}
</script>

<div th:if="${msg.replyFloor}">
<div class="msg-lists-item-left">
<span class="msg-left-edit"
th:classappend=" ${msg.unreadCount == 0} ? 'msg-all-read' ">您在</span>
<span th:text="${msg.topic.title}"
class="msg-left-edit-res"
th:classappend=" ${msg.unreadCount == 0} ? 'msg-all-read' ">问题回答</span>
<span th:text="${msg.type.name}"
class="msg-left-edit "
th:classappend=" ${msg.unreadCount == 0} ? 'msg-all-read' ">帖子相关</span>
<span class="msg-left-edit-number" >
产生了<span th:text="${msg.unreadCount} ? : ${msg.unreadCount} + '条新' : ${msg.unreadCount} + '条' "
th:class="${msg.unreadCount} ? : 'number-inner':''">2132条</span>回复
</span>
</div>
<div class="msg-lists-item-right">
<span th:text="${msg.lastShowTime}">2017-8-10</span>
</div>
</div>

Related

passing value from parent to child not working

Using Vuu.js, I'm trying to pass a value from parent to child component. I have this working fine with a provided example. But when I change the name, it stops working. I can't figure out what i'm doing wrong. My understanding on props is limited, i'm still trying to get me head around it.
Working Example:
https://codepen.io/sdras/pen/788a6a21e95589098af070c321214b78
HTML
<div id="app">
<child :text="message"></child>
</div>
JS
Vue.component('child', {
props: ['text'],
template: `<div>{{ text }}</div>`
});
new Vue({
el: "#app",
data() {
return {
message: 'hello mr. magoo'
}
}
});
Non Working Example:
HTML
<div id="app">
<child :myVarName="message"></child>
</div>
JS
Vue.component('child', {
props: ['myVarName'],
template: `<div>{{ myVarName }}</div>`
});
new Vue({
el: "#app",
data() {
return {
message: 'hello mr. magoo'
}
}
});
In your parent template
<div id="app">
<child :myVarName="message"></child>
</div>
replace
<child :myVarName="message"></child>
with
<child :my-var-name="message"></child>
Additionally you can refer this to get insights of casing.
Leave everything as is in your updated example EXCEPT in the HTML change "myVarName" to "my-var-name" - this is done by default by Vue and within the js you can use the camelCased version myVarName still.

VueJS 2 show some HTML if a Function prop was passed

Question
In VueJS 2 how do you show some HTML if a Function prop was passed to the component.
Example
<template>
<div v-if="backBtn" #click="$emit('backBtn')">Back Button</div>
</template>
<script>
export default {
props: {
backBtn: Function
}
}
</script>
I can do this by passing a separate prop to key the v-if off of but I'm trying to do this will the one prop.
I created a Fiddle for this issue here
that should work,
you can add more definition with !== undefined
<template>
<div v-if="backBtn !== undefined" #click="$emit('backBtn')">Back Button</div>
</template>
<script>
export default {
props: {
backBtn: {
type: Function,
},
}
}
</script>
but as mentioned, that should work already, so you error may be somewhere else.
after seeing your code, I see what the issue is. it's a case issue
use :back-btn instead of :backBtn
this happens only if you're using vue runtime only (without the compilation)
read more here:
https://v2.vuejs.org/v2/guide/installation.html#Runtime-Compiler-vs-Runtime-only
you can solve it also by passing the function only
https://jsfiddle.net/rz6hyd7b/7/
Vue.component('my-btn', {
props: {
backbtn: {
type: Function
}
},
template: `
<div>
<div v-if="backbtn" #click="backbtn">Back Button</div>
</div>
`
})
var vm = new Vue({
el: '#app',
components: 'my-btn',
methods: {
btnClicked: function(){
console.log('adsf')
}
},
template: `
<div>
Show Btn => <my-btn :backbtn="btnClicked"></my-btn>
</br>
Hidden Btn => <my-btn></my-btn>
</div>
`
});

Vue.js 2 component not displaying data

I'm trying to make a simple vue component work:
<div id="app">
<my-component></my-component>
</div>
<script>
Vue.component('my-component', {
template: '<div>A custom component! {{ xstr }} aa</div>',
data: function() {
return {
xstr: 'i really want this to be visible',
};
}
});
window.app = new Vue({
el: '#app',
});
</script>
But apparently xstr is not displayed. What am I missing?
I identified my issue, but it's not related to vue. I used jinja2 for generating the html, and since jinja uses the {{ }} syntax for templating as well, the two systems interfered.

Why all values are not Dynamically Passed in Html using Vue JS?

I use vue js with laravel.
I pass multiple variables threw vuejs component(template), i can pass few variables successfully.
But few of them not passed, shows Empty("").
HTML (Template):
<template v-if="showTemplate" id="segment_body">
<div class="col-md-2" align="center">
<b>Grid</b><br>
<i class=""></i>Grid |
<i class=""></i>List
</div>
<b>ID : #{{ t_id }}</b><br>
<b>LIST : #{{ t_showList }}</b>
</template>
HTML (Data Source):
<script> var a = { list: false} </script>
<div>
<campaign_segment :t_id=1 :t_showList="a.list"></campaign_segment>
</div>
VueJS :
Vue.component('campaign_segment', {
template: '#segment_body',
props: ['t_showList','t_id']
});
OUPUT :
ID : 1
LIST :
If i click Option "Grid",
OUTPUT :
ID: 1
LIST : False
Why I'm not get the value of list?
Why I only get the value of ID ?
Any other solutions ?
You're running into this issue because you're you're trying to pass in the javascript object directly to the component instead of passing it through the Vue instance.
Check out this bin: http://jsbin.com/fosifo/edit?html,js,output
The only real difference in the bin is that rather than trying to pass a to the component directly here (which won't work b/c the vue instance isn't aware of the data):
<script> var a = { list: false} </script>
<div>
<campaign_segment :t_id=1 :t_showList="a.list"></campaign_segment>
</div>
We pass it in through the vue instance:
In your html:
<script>
var a = {list: false}
</script>
In your javascript:
new Vue({
el: '#app',
data:{
a: a // the `a` value here is referencing that same `var a = {list: false}` in your markup.
}
})
By doing it this way, as the vue instance is being created it's able to bind it's a data property to the globally defined variable a from your markup.
It's essentially the same as doing this:
new Vue({
el: '#app',
data:{
a: {list: false}
}
})

Call a Vue.js component method from outside the component

Let's say I have a main Vue instance that has child components. Is there a way of calling a method belonging to one of these components from outside the Vue instance entirely?
Here is an example:
var vm = new Vue({
el: '#app',
components: {
'my-component': {
template: '#my-template',
data: function() {
return {
count: 1,
};
},
methods: {
increaseCount: function() {
this.count++;
}
}
},
}
});
$('#external-button').click(function()
{
vm['my-component'].increaseCount(); // This doesn't work
});
<script src="http://vuejs.org/js/vue.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="app">
<my-component></my-component>
<br>
<button id="external-button">External Button</button>
</div>
<template id="my-template">
<div style="border: 1px solid; padding: 5px;">
<p>A counter: {{ count }}</p>
<button #click="increaseCount">Internal Button</button>
</div>
</template>
So when I click the internal button, the increaseCount() method is bound to its click event so it gets called. There is no way to bind the event to the external button, whose click event I am listening for with jQuery, so I'll need some other way to call increaseCount.
EDIT
It seems this works:
vm.$children[0].increaseCount();
However, this is not a good solution because I am referencing the component by its index in the children array, and with many components this is unlikely to stay constant and the code is less readable.
In the end I opted for using Vue's ref directive. This allows a component to be referenced from the parent for direct access.
E.g.
Have a component registered on my parent instance:
var vm = new Vue({
el: '#app',
components: { 'my-component': myComponent }
});
Render the component in template/html with a reference:
<my-component ref="foo"></my-component>
Now, elsewhere I can access the component externally
<script>
vm.$refs.foo.doSomething(); //assuming my component has a doSomething() method
</script>
See this fiddle for an example: https://jsfiddle.net/0zefx8o6/
(old example using Vue 1: https://jsfiddle.net/6v7y6msr/)
Edit for Vue3 - Composition API
The child-component has to return the function in setup you want to use in the parent-component otherwise the function is not available to the parent.
Note: <sript setup> doc is not affacted, because it provides all the functions and variables to the template by default.
You can set ref for child components then in parent can call via $refs:
Add ref to child component:
<my-component ref="childref"></my-component>
Add click event to parent:
<button id="external-button" #click="$refs.childref.increaseCount()">External Button</button>
var vm = new Vue({
el: '#app',
components: {
'my-component': {
template: '#my-template',
data: function() {
return {
count: 1,
};
},
methods: {
increaseCount: function() {
this.count++;
}
}
},
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<my-component ref="childref"></my-component>
<button id="external-button" #click="$refs.childref.increaseCount()">External Button</button>
</div>
<template id="my-template">
<div style="border: 1px solid; padding: 2px;" ref="childref">
<p>A counter: {{ count }}</p>
<button #click="increaseCount">Internal Button</button>
</div>
</template>
For Vue2 this applies:
var bus = new Vue()
// in component A's method
bus.$emit('id-selected', 1)
// in component B's created hook
bus.$on('id-selected', function (id) {
// ...
})
See here for the Vue docs.
And here is more detail on how to set up this event bus exactly.
If you'd like more info on when to use properties, events and/ or centralized state management see this article.
See below comment of Thomas regarding Vue 3.
You can use Vue event system
vm.$broadcast('event-name', args)
and
vm.$on('event-name', function())
Here is the fiddle:
http://jsfiddle.net/hfalucas/wc1gg5v4/59/
A slightly different (simpler) version of the accepted answer:
Have a component registered on the parent instance:
export default {
components: { 'my-component': myComponent }
}
Render the component in template/html with a reference:
<my-component ref="foo"></my-component>
Access the component method:
<script>
this.$refs.foo.doSomething();
</script>
Say you have a child_method() in the child component:
export default {
methods: {
child_method () {
console.log('I got clicked')
}
}
}
Now you want to execute the child_method from parent component:
<template>
<div>
<button #click="exec">Execute child component</button>
<child-cmp ref="child"></child_cmp> <!-- note the ref="child" here -->
</div>
</template>
export default {
methods: {
exec () { //accessing the child component instance through $refs
this.$refs.child.child_method() //execute the method belongs to the child component
}
}
}
If you want to execute a parent component method from child component:
this.$parent.name_of_method()
NOTE: It is not recommended to access the child and parent component like this.
Instead as best practice use Props & Events for parent-child communication.
If you want communication between components surely use vuex or event bus
Please read this very helpful article
This is a simple way to access a component's methods from other component
// This is external shared (reusable) component, so you can call its methods from other components
export default {
name: 'SharedBase',
methods: {
fetchLocalData: function(module, page){
// .....fetches some data
return { jsonData }
}
}
}
// This is your component where you can call SharedBased component's method(s)
import SharedBase from '[your path to component]';
var sections = [];
export default {
name: 'History',
created: function(){
this.sections = SharedBase.methods['fetchLocalData']('intro', 'history');
}
}
Using Vue 3:
const app = createApp({})
// register an options object
app.component('my-component', {
/* ... */
})
....
// retrieve a registered component
const MyComponent = app.component('my-component')
MyComponent.methods.greet();
https://v3.vuejs.org/api/application-api.html#component
Here is a simple one
this.$children[indexOfComponent].childsMethodName();
I am not sure is it the right way but this one works for me.
First import the component which contains the method you want to call in your component
import myComponent from './MyComponent'
and then call any method of MyCompenent
myComponent.methods.doSomething()
Declare your function in a component like this:
export default {
mounted () {
this.$root.$on('component1', () => {
// do your logic here :D
});
}
};
and call it from any page like this:
this.$root.$emit("component1");
If you're using Vue 3 with <script setup> sugar, note that internal bindings of a component are closed (not visible from outside the component) and you must use defineExpose(see docs) to make them visible from outside. Something like this:
<script setup lang="ts">
const method1 = () => { ... };
const method2 = () => { ... };
defineExpose({
method1,
method2,
});
</script>
Since
Components using are closed by default
Sometimes you want to keep these things contained within your component. Depending on DOM state (the elements you're listening on must exist in DOM when your Vue component is instantiated), you can listen to events on elements outside of your component from within your Vue component. Let's say there is an element outside of your component, and when the user clicks it, you want your component to respond.
In html you have:
Launch the component
...
<my-component></my-component>
In your Vue component:
methods() {
doSomething() {
// do something
}
},
created() {
document.getElementById('outsideLink').addEventListener('click', evt =>
{
this.doSomething();
});
}
I have used a very simple solution. I have included a HTML element, that calls the method, in my Vue Component that I select, using Vanilla JS, and I trigger click!
In the Vue Component, I have included something like the following:
<span data-id="btnReload" #click="fetchTaskList()"><i class="fa fa-refresh"></i></span>
That I use using Vanilla JS:
const btnReload = document.querySelector('[data-id="btnReload"]');
btnReload.click();

Categories