I am rendering some components using Vue.js (version 3.x)
<div v-for="(i, index) in fields" >
<my-component :id="index" ></my-component>
<span class="delete-icon" #click="removeField(index)" >×</span>
</div>
Where fields is simply an array like so: [0,1,2,3,4,...]
Vue renders as many components as there are elements in fields along with the spans having the delete-icon which have an event listener to fire the removeField() function, which takes the index of the rendered component and is meant to be used to remove the respective component from the DOM.
However, I am not sure how to indicate to Vue that a specific component needs to be removed - in other words, how do I tell Vue to remove the particular component who's delete icon I click, as opposed to just deleting an element from the fields array, which will re-render the set with one less component, always resulting in the last component being removed.
I am passing unique id's to the components as well, so I technically can remove the components using vanilla JS, but wondering whether Vue offers a more elegant solution.
You can create a method that will filter the fields
It is not recommended to use index as a unique key.
CODESANDBOX
removeField(index) {
this.fields = this.fields.filter((number, i) => {
return i !== index;
});
},
Related
Im currently struggling with something where i want to add a data attribute to a component and then based on when NUXT is loaded have a click event bind to all nodes that have this data attribute. Im not using v-on because i want to have this separated from current Vue logic. So example:
component a.vue is tagged with data-element
<a href="/somelink" data-element>link</a>
component b.vue also has HTM elements tagged with data-element
<button data-element>link</button>
When the app loads i need to then loop through all data-element and bind an eventlistner to them.
I tried the above method and that works to some degree but fails when reactivity sets in and the DOM is updated. I checked mixins (not recommended using VueJS3), used composition API, used a mutation observer that checked the DOM status for changes and based on new elements loaded it ads click events, looked at hooks etc but now im getting confused at what is the best way to proceed. Some solutions work to some extend but feels hacky. Or is there a completely different approach i am missing.
To be Sure that the DOM has been initialized you need to set your event listeners on "mounted" lifecycle
mounted() {
const elements = [...document.querySelectorAll('[data-element]')];
elements.forEach(element => {
element.addEventListener('click', () => { /* your code here. */ })
})
}
in my parent render function, I am rendering a list of form inputs. They can be number, slider, text, etc, and for each, I have a custom Vue component, i.e. FormInputSlider. I receive some data from an API, and then have an array of these different inputs to render. Doing a huge if/else block in my parent's render code seems unmaintainable, so what is the best/standard way of rendering a FormInputSlider component when I encounter a "form-input-slider" in the list iteration? I'm coming from a React understanding of the world for what it's worth.
If you've the component names, you could just use the built-in component tag to render the component dynamically :
<component :is="dynamicName" />
instead of :
<template v-if="dynamicName==='abc'">
<abc/>
</template>
<template v-elseif="dynamicName==='xyz'">
<xyz/>
</template>
....
We are using single file vue components and in a mousemove event handler we'd like to be able to detect if the target element is clickable.
In our Vue templates we are using v-on directives: v-on:click="someCallback".
Unfortunately there doesn't seem to be an easy way to tell for a given element if an event listener was registered for it (i.e. via v-on directive).
For this we'd like to add a custom attribute to those elements with a v-on:click directve - i.e. "clickable". But this should happen automatically.
So we'd have to either wrap Vue's own "on"-directive into a custom one or somehow hook into Vue's rendering cycle - but this seems not very straight-forward: Couldn't find the directive on the Vue instance or the Vue component object.
What we have tried:
retrieving the information about registered listeners from the target element provided by the event object passed to the event handler. But apparently browsers don't provide this information.
searching the Vue component object for some object that stores information about which event listener has been registered for which element with which handler. We were not able to find this information - but it should be somewhere, right?
Hope anyone has a nice idea on how to accomplish adding a custom attribute to elements with v-on:click directive automatically.
Thanks!
EDIT:
So we have i.e.
<div id="x" #click="someMethod" />
in our template.
But we want to add a custom attribute automatically (we dont want to add it manually for all the trillion cases):
<div id="x" clickable #click="someMethod" />
Then in the event handler for addEventListener('mousemove', handler) we would check for this attribute: if (e.target.hasAttribute('clickable'))
But any other way of accomplishing this (so being able to tell inside the handler for mousemove if the element is clickable) would be fine too.
You could create a container component and import it into all your other vue components, ensuring it's the first component in your template, like:
<template>
<v-container>
// your template here
</v-container>
</template>
<script>
// Obviously replace the path and point to your location of the component
import ComponentContainer from './common/ComponentContainer.vue'
export default {
name: 'MyClientComponent',
components: {
'v-container': ComponentContainer
}
}
</script>
And this is the container component that looks for click events and adds the clickable class:
<template>
<div class="component-container">
<slot></slot>
</div>
</template>
<script>
export default {
name: 'ComponentContainer',
mounted() {
this.$slots.default.forEach(vn => {
this.addClickableClassNames(vn);
});
},
methods: {
addClickableClassNames(vnode) {
if (vnode) {
let data = vnode.data;
if (data && data.on) {
// Check for click events and add a
// clickable class if one exists
if (data.on.click && vnode.elm && vnode.elm.classList) {
vnode.elm.classList.add('clickable');
}
}
// Now recursively check children
if (vnode.children) {
vnode.children.forEach(vn => {
this.addClickableClassNames(vn);
});
}
}
}
}
}
</script>
This works, but I wouldn't like to comment on the performance of a large dom. And you, and other devs, need to remember to import into all components, which isn't ideal. But, it's a solution that might give you other ideas on improving and making more scalable.
I can't think of a way to "automatically" add this clickable attribute. I think unfortunately you will still need to "tag" your clickable elements one by one.
I would have a directive which you can add to any element in your templates.
Directive
Vue.directive('myDirective', {
inserted(el, bindings) {
el.addEventListener('mouseover', () => {
alert(bindings.value);
})
}
});
Usage
<span v-my-directive="true">Element 1</span>
<span v-my-directive="false">Element 2</span>
You will notice that in the template when using the directive a value is being passed to it. This is then read via bindings.value. Of course based on this value you can do whatever functionality you need.
JSFiddle: https://jsfiddle.net/tc45xf82/2/
Vue Docs: https://v2.vuejs.org/v2/guide/custom-directive.html
I am a beginner in coding.
I have came across scenario in react where i can set my style dynamically based on condition.
style={{display:index > this.state.indexVal ? 'inline': 'none',}}
Can I use above concept for the getting current elements css one attribute value and changing other based on the same?
Expecting something like this
style={{display: currentElement.backgroundColor === 'red ? 'inline': 'none',}}
This is not a react way to perform checking like this and access elements directly. Since you want to "know" component state that can be changed (background color of some element in your case) - you should save it in component state (or pass to component props from parent element if you control this from parent component). Then you will be able to check this value in state or props.
You can user React Refs to access some element directly, but this is bad practice and you should do this only if you 100% can't do this with props/state:
https://reactjs.org/docs/refs-and-the-dom.html
I'm trying to call several times the component HTML but without including again the child component in the father component.
I need it to be like this:
<father-component>
<child-component #id>
<button (click) = "#id.show()">
</father-component>
The html in the child component is hidden untill i press the button.
The thing is that i want to show the html of the child component as many times as the button is pressed.
As far as i know, the #ViewChild instantiates the html only one time, so if i try to append that html to another div, it will append the same. I need to know if there's a way to Instantiate a new #ViewChild anytime i call a function.
One possible solution is to use an ngFor like this:
<father-component>
<child-component *ngFor="let i of children">
<button (click) = "increment()">
</father-component>
Then have an increment method in your component class that increments the number of children. That would allow you to have any number of child components.