Custom event on each ref within for loop Vuejs - javascript

I have a loop of data and have a modal for each loop
<div ref="vuemodal-{{loop.index}}">
It's a bootstrap modal and to each of them I want to bind an event whenever I close the modal
mounted(){
Object.keys(this.$refs).map((val) => $(this.$refs[val]).on("hidden.bs.modal",
this.doSomethingOnHidden))
}
I don't seem to access the object properly

First thing, your ref is not dynamic, it's a string. So make it dynamic by binding this using the bind (v-bind or :) operator.
Second thing, mustaches cannot be used inside HTML attributes, so {{loop.index}} won't work.
Here is the demo-
new Vue({
el: "#app",
mounted() {
Object.keys(this.$refs).map((val) => console.log(val))
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div v-for="index in 5" :ref="`vuemodal-${index}`">
It's a bootstrap modal and to each of them I want to bind an event whenever I close the modal
</div>
</div>

Related

Toggling hide/show in vue-cli

I have two components.
The first one is a header with an image.
The second one is a div with a list.
I need to click the image (in the first component) to show the list (second component) and click it again to hide it.
The div should replace all the content below the image (so I can click the image again to hide the div and show default content).
Please see the picture for details. Links to the code below.
The code:
Component with the picture which has to work with v-on:click
https://github.com/Mike-OxHuge/shop-on-vue-cli/blob/master/src/components/main-header.vue
Component which supposed to appear/disappear when I click the picture in component 1
https://github.com/Mike-OxHuge/shop-on-vue-cli/blob/master/src/components/menu.vue
Component which supposed to be shown by default, but disappear or being covered by component 2
https://github.com/Mike-OxHuge/shop-on-vue-cli/blob/master/src/components/main-page-content.vue
App.vue
https://github.com/Mike-OxHuge/shop-on-vue-cli/blob/master/src/App.vue
And main.js
https://github.com/Mike-OxHuge/shop-on-vue-cli/blob/master/src/main.js
You need to read up on on click handlers in Vue, and binding the v-dom to data variables.
Here's an example:
JS:
var vue = new Vue({
el:"#app",
data: {
isShowing:false,
}
})
v-dom:
<div id="app">
<iframe v-show="isShowing" src="http://www.weather.gov/"
frameborder="0"
></iframe>
<button #click="isShowing ^= true">Click Me</button>
</div>
Demo: https://codepen.io/michael_coder/pen/WReNNm
var app = new Vue({
el: '#app',
data: {
Hide: false
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button v-on:click="Hide = !Hide">Click Me</button>
<div v-if="!Hide"><ul><li>One</li>
<li>Two</li><li>Three</li><li>four</li><li>five</li><li>six</li></ul></div>
</div>

Getting data from declared component in vue

Currently trying to wrap my head around Vue and templates.
I read that you pass data from child -> parent with $emit()
app.js
Vue.component('tweet-postbox', require('./components/tweetPostBox.vue').default);
const app = new Vue({
el: '#app',
methods: {
addTweet (tweet) {
//from the tweetPostBox.vue postTweet method
console.log(tweet)
}
}
});
tweetPostBox.vue
<template>
<div class="post-box">
<div class="w-100 d-flex align-items-center">
<div class="profile-image rounded-circle"></div>
<input v-model="message" type="text" id="tweetText" placeholder="Whats happening?">
</div>
<div class="controls d-flex align-items-center w-100">
<button class="btn btn-primary ml-auto" #click="postTweet" id="postTweet">Tweet</button>
</div>
</div>
</template>
<script>
export default {
data: function () {
return {
message: ''
}
},
methods: {
postTweet: async function(){
let response = await axios.post('/post', {
message: this.message
})
//How to get this response data to the main vue instance?
this.$emit('addTweet', response);
}
}
}
</script>
I'm trying to get the value into my app.js from the component file... but nothing is console logged. Where am I going wrong?
Update: Added HTML
<div class="container" id="app">
<tweet-postbox></tweet-postbox>
</div>
You should just need to change the template to:
<div class="container" id="app">
<tweet-postbox #add-tweet="addTweet"></tweet-postbox>
</div>
The #add-tweet part registers an event listener for the add-tweet event. I've used kebab case to avoid browser case-sensitivity problems. You'd need to emit the event with the same name, this.$emit('add-tweet', response). See the offical documentation to confirm that kebab case is the way to go.
The ="addTweet" parts assigns the method addTweet as the listener.
https://v2.vuejs.org/v2/guide/events.html#Method-Event-Handlers
Found this great answer on another post:
https://stackoverflow.com/a/47004242/2387934
Component 1:
<!-- language: lang-js -->
this.$root.$emit('eventing', data);
Component 2:
<!-- language: lang-js -->
mounted() {
this.$root.$on('eventing', data => {
console.log(data);
});
}
First of all, please fix your coding style, there's a lot of issues including indentation errors, try using eslint maybe.
Second, let's break this.$emit('addTweet') down a bit:
this is in that line refers to the instance of the Vue component, thus an instance of TweetPostBox.
When you call $emit with a addTweet, you're dispatching an event within the component.
Now that addTweet is dispatched, what's going to happen next? Vue is going to find all event handlers that handle addTweet and executes them.
Now in your case, you do not have any event handlers for this event. addTweet in your parent component is simply a local function in that component, it is not an event listener by any means.
To register an event listener, Vue provides # syntax, and you're already using that with #click, so just like you are doing #click="dosomething" (thus registering an event handler for onClick you need to use #add-tweet. (#addTweet also works but it is against coding style standards, Vue automatically handles the conversion for you)

Listen for Foundation event in Vue.js?

I'm building an app in Vue.js with the general structure of:
<app>
<filters-component></filters-component>
<div class="off-canvas-content">
<nav-component></nav-component>
<div class="row card-grid">
<card-component v-for="item in items">
<modal-component v-if="launchModal === true"></modal-component>
</card-component>
</div>
</div>
</app>
This allow me to render the modal on the DOM only if a data element of launchModal is set to true (after clicking the button to launch the modal). This works great, but I need to do the reverse when it's closed.
According to Foundation's documentation, the Reveal (modal) component should emit an event called closed.zf.reveal when it's closed.
How do I listen for this event on the parent element (card-component) and then change launchModal to false, when it's called?
Thanks!
Essentially this will likely boil down to, in your modal-component (add these to the script in Modal.vue)
methods:{
onModalClosed(){
this.$emit("modal-closed")
}
},
mounted(){
this.$el.addEventListener('closed.zf.reveal', this.onModalClosed)
},
beforeDestroy(){
this.$el.removeEventListener('closed.zf.reve‌​al', this.onModalClosed)
}
Or something to that effect, depending on what element emits the event. If some other element emits the closed.zf.reveal event, then you could add a ref="modal" to it and then use this.$refs.modal.addEventListener and this.$refs.modal.removeEventListener.
Then you could just
<modal-component v-if="launchModal === true"
#modal-closed="launchModal = false">
</modal-component>
Edit
So the issue with listening to the event is that Foundation is using jQuery to fire the event. That means that you cannot listen for it using native methods (addEventListener), you have to listen to it with jQuery. So the modified code from above would be this:
methods:{
onModalClosed(){
this.$emit("modal-closed")
}
},
mounted(){
$(this.$el).on('closed.zf.reveal', this.onModalClosed)
},
beforeDestroy(){
$(this.$el).off('closed.zf.reve‌​al', this.onModalClosed)
}
And this does, in fact, catch the event. The problem is that Foundation, for whatever reason, moves the modal outside of the Vue and appends it to the bottom of the document when the modal is initialized. That causes Vue to throw an error when launchModal is set to false because the modal is no longer inside the Vue, and Vue complains when it tries to remove it from the DOM.
That being the case, I suggest you use your v-if inside the modal for the things that are rendering very slowly. That will result in a component like this.
Vue.component("modal", {
props:["show"],
template: "#modal-template",
watch:{
show(newVal){
if (newVal)
$(this.$el).foundation("open")
}
},
methods:{
onModalClosed(){
this.$emit("modal-closed")
}
},
mounted() {
new Foundation.Reveal($(this.$el))
$(this.$el).on("closed.zf.reveal", this.onModalClosed);
},
beforeDestroy() {
$(this.$el).off("closed.zf.reveal", this.onModalClosed);
}
});
And the template is
<template id="modal-template">
<div class="reveal" data-reveal>
<div v-if="show">
Stuff that is expensive to render
</div>
<button class="close-button" data-close aria-label="Close modal" type="button">
<span aria-hidden="true">×</span>
</button>
</div>
</template>
And here is the working example.

How to add v-model dynamically created html in vue.js?

I use vue.js. I want add vue model in selector, who created using jquery plugin.
My code:
<div class="app">
<div class="wysiwyg-wrap"></div>
</div>
<script>
new Vue({
el: '.app',
data: {
wysiwygText: 'text demo'
},
created: function(){
$('.wysiwyg-wrap').trumbowyg();
}
});
</script>
Jquery plugin trumbowyg create inside '.wysiwyg-wrap' many html elements. How to add in one of this elements v-model binding? I want type in my redactor and save result in 'wysiwygText' from vue model.
Thank you for any answers!
This is a great use-case for custom directives. Here is an example of a custom directive that adds jQuery functionality to Vue: https://vuejs.org/examples/select2.html
You can create a simple directive
Vue.directive('wysiwyg-wrap', function () {
$(this.el).trumbowyg();
});
Then attach it to any element
<div class="app">
<div v-wysiwyg-wrap></div>
</div>

Polymer 1.0 can't access elements within nested <template> element

I am using Polymer 1.0 and I am building a small accordion example. I have data binding to the accordion text fine, I just want to change the icon of the accordion when I click it.
Below is my code
<dom-module id="ab-accordion">
<template>
<iron-ajax
auto
handle-as="json"
on-response="handleResponse"
debounce-duration="300"
id="ajaxreq"></iron-ajax>
<template is="dom-repeat" id="accordion" items="{{items}}" as="item">
<div class="accordion" on-click="toggleParentAcc">
<div id="accordion_header" class="accordion__header is-collapsed">
<i class="icon icon--chevron-down"></i>
<span>{{item.name}}</span>
</div>
<div id="standard_accordion_body" class="accordion__body can-collapse">
<div class="accordion__content">
<content id="content"></content>
</div>
</div>
</div>
</template>
</template>
<script>
Polymer({
is: "ab-accordion",
//Properties for the Element
properties: {
accordian: Object,
childaccordions: Object,
// Param passed in from the element - Set if the accordion is open by default.
open: String,
data: String,
reqUrl: {
type: String,
value: "https://example.com/service.aspx"
},
},
ready: function () {
this.items = [];
},
attached: function () {
// Run once the element is attached to the DOM.
},
toggleParentAcc: function (event) { // Toggle the classes of the accordions
//This is where I want to toggle the class
this.$.accordion_header.classList.toggle('is-collapsed');
if (typeof event !== 'undefined') {
event.stopPropagation(); // Stop the click from going up to the parent.
}
},
handleResponse: function (e) {
this.items = e.detail.response.sports;
}
});
</script>
</dom-module>
Basically inside the toggleParentAcc function I want to toggle the class of the div with ID accordion_header. But I just get undefined or null.
I have tried the following two lines:
this.$.accordion_header // #1
this.$$('#accordion_header') // #2
How I access that element inside the dom-repeat?
UPDATE: I can't even access the elements within the when inside the attached function.
attached: function(){
this.$.accordion_header // This is null?!
this.$$('#accordion_header'); // this is also null!
}
https://www.polymer-project.org/1.0/docs/devguide/local-dom.html#node-finding
Note: Nodes created dynamically using data binding (including those in dom-repeat and dom-if templates) are not added to the this.$ hash. The hash includes only statically created local DOM nodes (that is, the nodes defined in the element’s outermost template).
I think it would be better if you'd use Polymer.dom(this.root) instead. Also I'd advice you to not use static IDs in dom-repeat as they are meant to be unique. Use classes instead.
Looks like you might be encountering Event Retargeting which happens when events "bubble" their way up the DOM tree. Read this documentation to learn more.
When I encountered this, I solved it by using something like:
var bar = Polymer.dom(event).path[2].getAttribute('data-foo');
inside my Polymer() function.
To figure it out in your case, you should go to the console and search the DOM tree / event log to locate your target. If you have trouble locating the correct area of the console, post a comment and I might be able to help further.
I eventually figured out a way of doing this without having to select elements in the nested template.
<template id="accord_template" is="dom-repeat" items="{{items}}" as="item">
<ab-accordion-row id="[[item.id]]" name="[[item.name]]" open="[[item.open]]">
</ab-accordion-row>
</template>
ab-accordion is another element, I just feed it the data and I can then change the classes based on the params.
<div id="accordion" class="accordion" on-click="toggleAccordion">
<div class$="{{getClassAccordionHeader(open)}}">
<i class="icon icon--chevron-down"></i>
<span>{{name}}</span>
</div>
<div id="standard_accordion_body" class$="{{getClassAccordionChild(open)}}">
<div class="accordion__content">
<content></content>
</div>
</div>
</div>
try with this.
toggleParentAcc: function (event) { // Toggle the classes of the accordions
//This is where I want to toggle the class
var header = event.target.parentElement;
Polymer.dom(header).classList.toggle('is-collapsed');
// rest of your code
}

Categories

Resources