How to Vue.js auto click? - vue.js

After searching the products on the search button, I have to list them in the background and make a selection.
Codes:
//The area I want clicked each time
<div v-for="(item, index) in filteredProducts" #click="addToList(item)"></div>
How can I get it to take action automatically?

You Can Use computed
See This Answer
computed: {
addToListComputed (item) {
addToList(item)
},
},

What do you mean "take action?" Do you want to run addToList(item) function every time someone searches a product?
You can use a watcher or computed property and run that addToList function when filteredProducts change, instead of a click event. I don't see why you would want to "auto click" on a div.
If you could further explain your question and provide a larger codebase, I could help better.

Related

Scrolling to v-expansion-panel opening

I'm trying to build a mobile small application using v-expansion-panels to display a list.
The idea is that when the user adds a new item in such list it will open the new panel and scroll down to such new panel.
I found a goTo() method in the $vuetify variable, unfortunatly the v-expansion-panels transition (the "opening") take some time and the goTo() won't completely scroll down because of the scrollbar height changes.
So from my understanding I need to detect the end of the transition (enter/afterEnter hook).
Per the vuetifyjs documentation, I could hope to have a "transition" property on my component. (Which isn't the case anyway). But such property is only a string so I can't hook into it.
My other idea is to, somehow, find the transition component and hook into it. Unfortunatly I have trouble understanding el/vnode and the way vuejs is building is tree like the vue-devtool show and I can't get the transition component. When debugging (in the enter hook callback of the transition) it is like the component/el/vnode has a parent but isn't the child of anybody.
Is there a way to do what I'm looking for?
Here is a jsfiddler of what I currently do: https://jsfiddle.net/kdgn80sb/
Basically it is the method I'm defining in the Vue:
methods: {
newAlarm: function() {
const newAlarmPanelIndex = this.alarms.length - 1;
this.alarms.push({title: "New line"});
this.activePanelIndex = newAlarmPanelIndex;
// TODO:
this.$vuetify.goTo(this.$refs.alarmPanels[newAlarmPanelIndex]);
}
}
Firstly you should open manually panel and then use goTo with a timeout.
It works for me, just give some time to a panel to open.
<v-expansion-panels v-model="alarmPanels">
<v-expansion-panel>
<div id="example" />
</v-expansion-panel>
</v-expansion-panels>
this.alarmPanels.push(0); // Use index of expansion-panel
setTimeout(() => {
this.$vuetify.goTo(`#${resultId}`);
}, 500);

bind click event to child component using v-bind

I created a simple Minesweeper game and when it comes to the decision, which cell to render there are three possibilities:
Unrevealed cell
Revealed mine cell
Revealed neutral cell
I created a row component that renders all the cells contained by the row.
<template>
<div>
<component
v-for="(cell, columnIndex) in row"
:key="columnIndex"
v-bind="getCellProps(cell, columnIndex)"
:is="getComponentCell(cell)"
/>
</div>
</template>
<script>
// imports here
export default {
components: {
UnrevealedCell,
RevealedNeutralCell,
RevealedMineCell
},
props: {
row: Array,
rowIndex: Number
},
methods: {
getCellProps: function(cell, columnIndex) {
if(cell.revealed) {
if (cell.isMine) {
return {};
} else {
return {
mineNeighbours: cell.mineNeighbours
};
}
} else {
return {
unrevealedCell: cell,
x: columnIndex,
y: this.rowIndex,
cellClicked: this.onCellClicked
};
}
},
getComponentCell: function(cell) {
if(cell.revealed) {
if (cell.isMine) {
return RevealedMineCell;
} else {
return RevealedNeutralCell;
}
} else {
return UnrevealedCell;
}
},
onCellClicked: function(x, y) {
debugger;
}
}
}
</script>
Unfortunately my cellClicked event is not working. The child component is able to emit the event correctly but my onCellClicked doesn't get executed. I think this is because I can't write
cellClicked: this.onCellClicked
as it would normally be
#cellClicked
Without the # the attribute might get added as a component property. How can I fix this to listen to the emitted cellClicked event?
A few thoughts occur.
Firstly, the reason this isn't working is because v-bind is used to set component props and element attributes. The # prefix is a shorthand for v-on, so it isn't a prop or attribute in this sense, it's a directive in its own right. v-on does support an object version, just like v-bind, so you can do something like v-on="getCellEvents(cell, columnIndex)" and return a suitable object for each cell type. This is probably the cleanest direct answer to your original question. Less clean and less direct answers are also available...
You could implement this by making cellClicked a prop of the child cell and then calling it as a callback function rather than emitting an event. Not saying you should, but you could. That would work with the code you posted above completely unchanged.
Another alternative is just to add the event listener for all cells. Include #cellClicked="onCellCicked" in the template without worrying about the cell type. If the other cell types don't emit that event then nothing will happen. Vue doesn't know what events a component can fire, you can listen for anything.
Further thoughts...
Your cell template is a bit anaemic. I know people generally advise keeping logic out of the template but in your case I'd say you've probably taken it too far and it just makes things harder to understand. There are two ways you could address this:
Rewrite your component to use a render function instead. Templates exist because humans find them easier to read than render functions but in your case you've got all the logic in JavaScript anyway. The template isn't really adding anything and going all-in with a render function would probably be easier to understand than what you have currently.
Move the logic into the template. I don't see any obvious reason not to do it that way from the code you've posted. I'll post an example at the end.
Either of these two approaches would remove the problem you had adding an event listener.
A final thought on the click events is that you could use event propagation to handle them instead. Add a single click listener on a suitable element of the surrounding component and don't listen for events on the cells/rows at all. The single listener could then establish which cell was clicked (potentially fiddly) and whether anything needs to be done about it. While this would increase the coupling between the components I would imagine that it wouldn't really matter as these components aren't really reusable elsewhere anyway. I'm not recommending this as an approach at this stage but it is worth keeping in mind whenever you find yourself creating large numbers of repetitive components that all need the same events. In your scenario it would probably only make sense if you start to run into performance problems, and even then there will likely be better ways to fix such problems.
So, I promised an example of the template approach:
<template>
<div>
<template v-for="(cell, columnIndex) in row">
<unrevealed-cell
v-if="!cell.revealed"
:key="columnIndex"
:unrevealed-cell="cell"
:x="columnIndex"
:y="rowIndex"
#cellClicked="onCellClicked"
/>
<revealed-mine-cell
v-else-if="cell.mine"
/>
<revealed-neutral-cell
v-else
:mineNeighbours="cell.mineNeighbours"
/>
</template>
</div>
</template>
I'm not sure why the UnrevealedCell needs the x and y but if it's just so that it can emit them as part of the event then you might want to consider registering the listener as #cellClicked="onCellClicked(columnIndex, rowIndex)" and then there's no need to emit the co-ordinates from the cell. I also wonder whether you need 3 separate components for these cells. My gut reaction is that one component would be more appropriate with the row component not needing to have any understanding of the individual cells at all.

bootstrap pagination vue.js doesn't update function until after two clicks

I know this is a very specific question but was wondering if someone could help me. So right now I am currently using a module from bootstrap located on this link right here:
https://bootstrap-vue.js.org/docs/components/pagination/
I am currently having the trouble where I am trying to dispatch an action that is reliant on the currentPage variable.
<bPagination align="right" :total-rows="100" v-model="currentPage" :per-page="10" #click.native =" updatePage(currentPage); loadParticipants()"></bPagination>
currentPage is set to 1 as of right now like so:
data() {
return {
currentPage: 1,
tag: '',
tags: [],
....
Here is a method so that I can update the page within my VueX store. So first I dispatch a method within my Vue file:
updatePage: function(page){ this.$store.dispatch('updatePage', page); console.log("Hit me") },
And then within my VueX store I use this function to commit:
updatePage({ commit }, updatePage) { commit('updatePage', updatePage); }
I then use a mutation to finally update the state data:
updatePage: (state, updatePage) => { state.page = updatePage; }
So what I want it to do is that everytime I click on a page number I get to update the page number within the vuex store immediately when you press on any of the page numbers. However, it decrements and increments a step behind everytime I click on a page number.
examples:
If I click page number 2 twice it will update the store data to page number 2
And if you click page 2 and then page 1 it will show that the store's page number is at page 2.
So it is pretty much a step behind. If anyone can push me to the right direction that would help so much. Thanks!
Never mind I solved it I pretty much used #input rather than using a click event.
<bPagination align="right" :total-rows="100" v-model="currentPage" :per-page="10" #input =" updatePage(currentPage); loadParticipants()"></bPagination>

How to refactor repetitive attributes in Vue.js

Suppose I have a form and many fields in it. I want to subscribe to change for each form field. I will have to add #change="doSome" for every field. If I have many fields it gets somewhat repetitive. How do I refactor it?
You can listen for the change event on the form tag itself instead of listening on the individual inputs.
<form #change="doSomething"> will run the function doSomething() when something inside the form has changed eg: if you type in an input and release focus
In the doSomething function, you want to find out what element changed. We get this info from the event parameter provided from the input event:
methods: {
doSomething(event) {
this.lastEvent = event.target.value;
}
}
You can see this in effect on this Codepen example
If the form element is a child of an element inside a component like so:
<template>
<div>
<form></form>
</div>
</template>
the #changeevent-listener will not work as there is nothing that changes on the root element (div) on the component.
In this case, we need to add the .native modifier, like so: #change.native="doSomething".

How to add a click function to an imported component in Vue

So I have a Vue2 app. I have create a component "u-button"
when i import this and use it in another component, I want to be able to add a click function to it. However at the moment it looks for a function on the u-button component rather than the component it is being used in.
so for example, in the below if i click the first button nothing happens, if i click the second button i get the console log.
<template>
<div>
<u_button #click="clicked">Click me</u_button>
<button #click="clicked">Click me</button>
</div>
</template>
<script>
import u_button from '../components/unify/u_button'
export default {
components: {
u_button
},
methods: {
clicked() {
console.log("Working!");
}
}
}
</script>
However if i add a method on the u-button component, then it calls that. So how can i get my below example to work ? The only thing I can think of is to wrap it in another div and add the click function to that. but I'm wondering if there is a better way?? I dont want to use events to do this either as that gets messy very quickly.
As you can imagine having a reusable button that when clicked always performs the same function is a bit pointless.
It's because of the nature of components for example if we had a (virtual) iframe component which had a button in it and we'd like to detect click event on it we might name the event click and listen for it in the parent component; therefore, Vue introduced a feature called event modifiers for example in Vue, We have .native modifier (you can read more about the Vue modifiers here)
Now, to make your code work, You should add a .native after #click like this:
<u_button #click.native="clicked">Click me</u_button>
By the way, it's better to develop a naming convention for yourself It'd become handy when your projects get larger.