Vue - Call Function from facebook button inside Vue-Component - vue.js

trying to implement the Facebook-Login button inside of a vue component. How can I call a function on the the surrounding component from the custom attribute "onLogin" from the button?
HTML
<complogin :page="page" inline-template v-if="page=='login'">
<div data-max-rows="1" onlogin="fbLogin()" data-size="medium" data-show-faces="false" data-auto-logout-link="false" class="div fb-login-button"></div>
</complogin>
JS
Vue.component('complogin',{
data: function() {
return { foo: "bar" }
},
methods: {
fbLogin:function() {
console.log("Hello!")
},
}
})

Found a solution:
Attached the Component-Method via lifecycle hook to the window event and then just called it.
loaded: function() {
window.componentMethod = this.componentMethod
}

Related

How to destroy Cloudinary Product Gallery using the VueJS2 beforedestroy() hook?

Need some guidance on how to property destroy a widget and remove it from memory when user navigates away to another page.
I have a method that loads a widget like so on a component's mounted() hook:
Widget.vue:
<template>
<div id="myGallery"></div>
</template>
<script>
export default {
mounted() {
this.loadWidget()
},
beforeDestroy() {
......???
},
methods: {
loadWidget() {
const myGallery = window.cloudinary.galleryWidget({
container: '#myGallery',
cloudName: 'XXXX',
mediaAssets: [
{
publicId:
'pic.jpg'
}
]
})
myGallery.render()
}
}
}
</script>
The widget's doc says I can use the following method to erase the widget from memory:
myGallery.destroy();
How would I do that here in the code? Do I need to make another instance of the widget and then append the .destroy() method to it? I don't understand.

Bind click from instance instead of html tag

In vue is possible to bind button click directly from vue instance?
I have this button:
<el-button #click="alert_me" class="gf-button" type="primary" style="margin-left: 16px;">Button</el-button>
I wan't to remove #click="alert_me" and do like i would normally do with jquery but with vue.
Is it possible?
My Vue Instance:
new Vue({
el: "#app",
data: {
},
methods: {
alert_me() {
alert('Hello from vue!');
}
},
});
Thanks
If you need to attach a click event listener programmatically, it is possible with the classic javascript api:
<template>
<el-button class="gf-button" type="primary">Button</el-button>
</template>
<script>
export default {
mounted () {
// jquery would also work if it's installed.
document.getElementByClassName('gf-button').addEventListener('click', this.alert_me)
},
methods: {
alert_me() {
console.log('alert')
}
}
}
</script>
You could avoid the manual element query from the document with the Vue $refs object.
<template>
<el-button ref="myButton" class="gf-button" type="primary">Button</el-button>
</template>
<script>
export default {
mounted () {
this.$refs.myButton.addEventListener('click', this.alert_me)
},
methods: {
alert_me() {
console.log('alert')
}
}
}
</script>
But if you need that event as soon as the Vue component is created, I wouldn't recommend doing this. It kinda oversee the shadow dom optimisation of Vue.
The #click="" syntax provided is the best way to attach a click listener to an html element.
You can make use of addEventListener and call it in mounted life cycle.
mounted() {
document.querySelector('#element').addEventListener('click', event =>
{
//handle click
}
)
}

Nuxt.js global events emitted from page inside iframe are not available to parent page

I'm trying to create a pattern library app that displays components inside iframe elements, alongside their HTML. Whenever the contents of an iframe changes, I want the page containing the iframe to respond by re-fetching the iframe's HTML and printing it to the page. Unfortunately, the page has no way of knowing when components inside its iframe change. Here's a simplified example of how things are setup:
I have an "accordion" component that emits a global event on update:
components/Accordion.vue
<template>
<div class="accordion"></div>
</template>
<script>
export default {
updated() {
console.log("accordion-updated event emitted");
this.$root.$emit("accordion-updated");
}
}
</script>
I then pull that component into a page:
pages/components/accordion.vue
<template>
<accordion/>
</template>
<script>
import Accordion from "~/components/Accordion.vue";
export default {
components: { Accordion }
}
</script>
I then display that page inside an iframe on another page:
pages/documentation/accordion.vue
<template>
<div>
<p>Here's a live demo of the Accordion component:</p>
<iframe src="/components/accordion"></iframe>
</div>
</template>
<script>
export default {
created() {
this.$root.$on("accordion-updated", () => {
console.log("accordion-updated callback executed");
});
},
beforeDestroy() {
this.$root.$off("accordion-updated");
}
}
</script>
When I edit the "accordion" component, the "event emitted" log appears in my browser's console, so it seems like the accordion-updated event is being emitted. Unfortunately, I never see the "callback executed" console log from the event handler in the documentation/accordion page. I've tried using both this.$root.$emit/this.$root.$on and this.$nuxt.$emit/this.$nuxt.$on and neither seem to be working.
Is it possible that each page contains a separate Vue instance, so the iframe page's this.$root object is not the same as the documentation/accordion page's this.$root object? If so, then how can I solve this problem?
It sounds like I was correct and there are indeed two separate Vue instances in my iframe page and its parent page: https://forum.vuejs.org/t/eventbus-from-iframe-to-parent/31299
So I ended up attaching a MutationObserver to the iframe, like this:
<template>
<iframe ref="iframe" :src="src" #load="onIframeLoaded"></iframe>
</template>
<script>
export default {
data() {
return { iframeObserver: null }
},
props: {
src: { type: String, required: true }
},
methods: {
onIframeLoaded() {
this.getIframeContent();
this.iframeObserver = new MutationObserver(() => {
window.setTimeout(() => {
this.getIframeContent();
}, 100);
});
this.iframeObserver.observe(this.$refs.iframe.contentDocument, {
attributes: true, childList: true, subtree: true
});
},
getIframeContent() {
const iframe = this.$refs.iframe;
const html = iframe.contentDocument.querySelector("#__layout").innerHTML;
// Print HTML to page
}
},
beforeDestroy() {
if (this.iframeObserver) {
this.iframeObserver.disconnect();
}
}
}
</script>
Attaching the observer directly to the contentDocument means that my event handler will fire when elements in the document's <head> change, in addition to the <body>. This allows me to react when Vue injects new CSS or JavaScript blocks into the <head> (via hot module replacement).

Extending Vue Lifecycle Hooks

I have a special application where I would like to run a method on every component when it is mounted. So I could have the method as a global mixin or something and then simply do..
mounted(){
this.mySpecialMethod();
}
However, I was wondering if it is possible to simply extend Vues mounted hook so the method is always run on every component when it is mounted. I have not been able to find in info on this.
If you really want to have everything call your mounted hook, you can use a global mixin.
Below, we have the mixin myMixin that will log to console every time something is mounted or destroyed. When you run the example, you can see that every time the plus button is clicked, it runs both the component's mounted hook as well as the mixin's hook.
If you want to extend this so that it can be reusable as a library, you can create a plugin out of it.
const foo = {
template: "<div #click='onClick'>hello</div>",
mounted() {
console.log("Foo's mounted");
},
methods: {
onClick() {
console.log("click");
}
}
}
const myMixin = {
mounted() {
console.log("I've been mounted");
},
destroyed() {
console.log("I've been destroyed");
}
};
Vue.mixin(myMixin);
const app = new Vue({
el: "#app",
data() {
return {
foos: []
};
},
components: {
foo
},
methods: {
add() {
this.foos.push("fizz");
},
remove() {
this.foos.pop();
}
}
});
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app">
<button #click="add()">+</button><button #click="remove()">-</button>
<ul>
<li v-for="f in foos">
<foo></foo>
</ul>
</div>

Vue directive not triggering method

I have a custom vue directive.
Vue.directive('click-outside', {
bind: function (el, binding, vnode) {
document.addEventListener(clickHandler, (event) => {
const clickedInsideDropdown = el.contains(event.target);
if (!clickedInsideDropdown && el.classList.contains(openClass)) {
vnode.context.$emit(binding.expression);
}
});
}
});
I then initialize it with the dropdown template:
<template>
<div class="dropdown" :class="{ '-is-open': open }" v-click-outside="close">
<span #click="toggle">
<slot name="toggle"></slot>
</span>
<slot name="menu"></slot>
</div>
</template>
The supporting logic is functioning as expected as well:
<script>
export default {
data: function () {
return {
open: false
}
},
methods: {
close: function () {
this.open = false;
console.log('close');
},
toggle: function () {
this.open = !this.open;
console.log('toggle');
}
}
}
</script>
The Problem
The event should fire when the current dropdown _is open and none of the items inside of it are clicked - which is does (console logging confirms this). However, the $emit is not triggering the close method for some reason.
The event is being emitted in the Vue devtools as expected.
Vue version 2.5.3
Credits to Linus Borg who answered my question for me on the forum. Was just understanding the purpose of events incorrectly.
Events are usually used to communicate from a child component to a parent component, so triggering an event ‘close’ in a componet will not run a method of that name in that component.
If you want that, you have to actually register a listener to that event:
created () {
this.$on('close', this.close /*the name of the method to call */)
}
However, this isn’t really necessary in your case. you are already passing the close method to the directive, so you can run it directly:
Vue.directive('click-outside', {
bind: function (el, binding, vnode) {
document.addEventListener(clickHandler, (event) => {
const clickedInsideDropdown = el.contains(event.target);
if (!clickedInsideDropdown && el.classList.contains(openClass)) {
binding.value()
// alternartively, you could also call the method directly on the instance, no need for an event:
vnode.context.[expression]()
// but that wouldn't really be elegant, agreed?
}
});
}
});