Call component methods outside component - vue.js

I am fairly new to Vue.js and I might be thinking about this the wrong way, in which case I would really appreciate the help.
I have a component defined as below:
Vue.component('btn', {
data() {
return {
active: false
};
},
props: ['type', 'msg'],
template: "<button class='btn__{{ type }}'><div class='la-ball-scale-multiple' v-show='active'><div></div><div></div><div></div></div>{{ msg }}</button>",
methods: {
start: function() {
this.active = true;
},
stop: function() {
this.active = false;
}
}
});
and I can add the btn component multiple times on any document, like this:
<btn type="primary" msg="Submit" #click="test"></btn>
When I click on it, it should do whatever is defined in test, which is in the root vue instance as a method.
However, I want to call the component's method, start in the test method. I also want to be able to call the component's stop method when I am done with the code logic inside test.
I don't think exposing the methods as events within the component and then calling this.$broadcast is a solution because, if I have two components, both of them seem to set active to true with this solution.
Any help please?

You could pass in your test function as a prop (called onclick for example) and then pass the context of your btn component as an argument to the test function. That way, you could access start and stop.
test:
test: function(e, btn) {
btn.start();
// ...
btn.stop();
}
btn:
Vue.component('btn', {
data() {
return {
active: false
};
},
props: ['type', 'msg', 'onclick'],
template: "<button class='btn__{{ type }}' #click="btnClick($event)"><div class='la-ball-scale-multiple' v-show='active'><div></div><div></div><div></div></div>{{ msg }}</button>",
methods: {
start: function() {
this.active = true;
},
stop: function() {
this.active = false;
},
btnClick: function(e) {
this.onclick(e, this); // call your onclick handler in the context of this component
},
}
});
custom element:
<btn type="primary" msg="Submit" :onclick="test"></btn>
Have a look at the JSFiddle:
https://jsfiddle.net/2a5os24x/6/

Related

Hiding and Showing a Component

App.vue
<HeaderPart></HeaderPart>
<router-view />
<PlayerBar v-if="audio"></PlayerBar>
I want to Hide/Show this PlayerBar using functions, In main.js i have created all the function to alter the value of audio.
Vue.mixin({
data: function() {
return {
baseURL: "https://sampleurl",
authToken: "sampleauth",
watch: false,
audio: true
};
},
methods: {
playaudio(item) {
window.Amplitude.playNow(item);
},
playvideo() {
this.audio = false;
this.watch = true;
console.log(this.audio);
},
stopvideo() {
this.watch = false;
this.audio = true;
console.log(this.audio);
}
},
computed: {
...mapGetters({
cur_user: "user"
})
}
});
So Whenever playvideo, the Playerbar will be hidden, and when he close video, playerbar will be shown. in Console.log, Values are coming fine, Audio is changing to true/false but playerbar not getting hidden on frontend
try v-show instead of v-if
<PlayerBar v-show="audio"></PlayerBar>
It might the naming conflict with the Vue component "watch" option (Vue watch option)

Vue watch not triggerring on elements in array

I have two components. One of them creates instances of the other one and adds them to an array then I try to watch on it to execute some functions, but it never triggers. My structure looks something like this:
const Child = Vue.extend({
data() {
triggerMe: false
},
methods: {
triggerSomething: function() {
console.log('It's called for sure');
this.triggerMe = true;
}
}
});
const Parent = Vue.extend({
data() {
children: []
},
components: {
child: Child
},
methods: {
addChild: function() {
this.children.push(new Child());
this.children[this.children.length - 1].$watch('triggerMe', _ => console.log('Never called'));
}
}
});
It works just right if it's the only one. How to fix that?
What I found is that $watch only triggers when component is mounted to some element in document. Other approach is needed:
<scipt id="parent-template">
<child
#trigger="triggerThat"
></child>
</script>
<script>
// in Child
this.$emit('trigger', someData);
// in Parent
methods: {
triggerThat: function(data) {
console.log('I can hear you');
console.log(data);
}
}
</script>

VueJs Data Passed From Root to Child Component via Prop Results in only an observable object

I have an app which calls a web service in the created() function and populates a property of the root data object. The property is passed via a prop to a child component and using the Chrome dev tools I can see that the prop data is available on the child component.
The problem I have is that I try to set data properties in the child component using values passed via the prop I end up with undefined property data. If I use the Chrome inspection tools and add a breakpoint I can see that the prop is an observable object in the form of {__ob__: Observer} and as such, I cannot directly access any of the data. My suspicion is that the child object sets it's data properties before the web service call has completed in the root.
How can I overcome this?
I've created a JsFiddle for this:
https://jsfiddle.net/ProNotion/a8c6nqsg/
Vue.component("mycomponent", {
template: '#my-component-template',
props: ["customer_data"],
data() {
return {
form_data: {
customerEmail: this.customer_data.customerEmail1
}
}
}
});
new Vue({
el: "#app",
data() {
return {
customer: {}
};
},
methods: {
init() {
var self = this;
axios.get("https://0bb1313e-b089-432e-b6bc-250f6162d7f0.mock.pstmn.io/GetCustomerData")
.then(response => {
self.customer = response.data;
}).catch(response => {
console.error(response);
});
}
},
created() {
this.init();
}
});
Here is my HTML markup:
<div id="app">
<mycomponent :customer_data="customer" />
</div>
<script type="x-template" id="my-component-template">
<div>
<p>{{form_data.customerEmail1}}</p>
</div>
</script>
Check response data type and format
console.log(typeof response.data) // string
{ "customerEmail1": "me#example.com", } // Remove `,`
You must parse to JSON type
axios.get(...).then(response => {
self.customer = JSON.parse(response.data.replace(',', ''))
})
Set property to watch with `deep` option
[Deep watching](https://v2.vuejs.org/v2/api/#vm-watch) will be detect nested value changes inside Objects
```
Vue.component("mycomponent", {
template: '#my-component-template',
props: ["customer_data"],
data() {
return {
form_data: {}
}
},
watch: {
customer_data: {
handler (val) {
this.form_data = val;
},
deep: true
}
}
});
```
Demo: https://jsfiddle.net/ghlee/f4gewvqn

What is this.myClick() doing in this vue js method and how could I test it?

Currently in vue code I have a page that has the following line:
<MyButton :my-click="generateSomeValues"/>
generateSomeValues, generates values and these then get set on the prop in the component.
MyButton is a component and inside it, it has the following:
#click.prevent="myloginButtonClicked" (The button has a click event which called the code below)
We then have the props and a methods:
props: {
myClick: {
type: Function,
default: undefined,
},
},
methods: {
async myloginButtonClicked() {
if (typeof this.myClick !== 'undefined') {
this.myClick();
}
}
},
Im trying to understand what this.myClick() is doing and how this could be tested using Jest. Looking at it, I would have thought myClick was just a prop and not a method?
Based on the code snippet
props: {
myClick: {
type: Function,
default: undefined,
}
},
You have indeed defined myClick as a prop. It is not a method. It is, however, a prop that happens to be a function, which means that it can be called.
To test it, you can use a Mock function
const mockMyClick = jest.fn(() => {});
const wrapper = shallowMount(MyButton, {
propsData: { myClick: mockMyClick }
});
const button = wrapper.find('button') // or whatever
button.trigger('click')
expect(mockMyClick.mock.calls.length).toBe(1);

Vue.js - Calling a component's method from Vue instance

I have a created a Vue instance and a global component. How can I call the component's method from my Vue instance?
In my Vue instance I did this:
methods: {
handleTabClick: function(tab, event) {
if (tab.name == 'log') {
call loadLog function in log component
}
}
}
Then my component is defined as this:
Vue.component('jettLog', {
props: ['shop'],
data() {
return {
log: [],
logLoading : true
}
},
methods: {
loadLog: function() {
console.log("Load");
},
},
});
How can I call jettLog's function loadLog from handleTabClick()?
I see different explanations and now I am wondering what's the best way to do it?
Thanks!
Register a global event bus whixh is an empty vue instance like this:
Var EventBus = new Vue();
in your vue instance emit an event with EventVus.$emit()
methods: {
handleTabClick: function(tab, event) {
if (tab.name == 'log') {
EventBus.$emit('log-event')
}
}
}
$emit() takes two arguments:
1: event name
2. event data (optional)
In the created hook of jettLog component set up an event listener on the EventBus and perform your logic when a paticular event is emitted
Vue.component('jettLog', {
props: ['shop'],
data() {
return {
log: [],
logLoading : true
}
},
created(){
var self = this;
EventBus.$on('log-event', function(eventData){
self.loadLog();
});
},
methods: {
loadLog: function() {
console.log("Load");
},
},
});
Check this out for more info