So I have a vuejs inline event that works great for one, but I want to expand it. Right now it checks for a specific property and the event changes based on what property is there. Issue is, I prefer to keep this inline instead of moving it all to a method in the model. So i guess this would become a big tenerary operation? So it checks to see if oil is there, if so it will trigger the click method. If its food, it will trigger that click2 method and anything else will trigger the click3 method. My goal is to keep this inline.
new Vue({
el: "#app",
data: {
todos: [
{ text: "food", done: false },
{ text: "pasta", done: false },
{ text: "oil", done: true },
{ text: "cheese", done: true }
]
},
methods: {
click:function(){
alert ("one clicked");
},
click2:function(){
alert ("two clicked");
},
click3:function(){
alert ("anything else");
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button v-on:click="todos.text==='tractor'?click(todos): todos.text==='jack'?click2(todos):click(todos)">
click me
</button>
</div>
Just send the todo as a param and do the logic there:
// template
<button v-on:click="click(todo)">
Then inside your component script:
methods: {
click(todo){
switch (todo.text){
case 'oil':
//Call a method or do logic here
this.handleOilTodo();
break;
....
Related
I checked the demo and confirmed it does work in the demo way. But I need to use the components in HTML but not in webpack-ed a modal.vue template. So I wrote the following:
<body>
<div id="app">
<modal name="test">
test
</modal>
</div>
<script src="{% static 'js/tmpdev.bundle.js' %}" type="text/javascript"></script>
<script>
Vue.use(VueJsModal, {
dialog: true,
dynamicDefaults: {
draggable: true
}
})
new Vue({
el: '#app',
components: {
},
data() {
return {
};
},
mounted() {
window.addEventListener('keydown', (e) => {
if (e.key == 'Escape') {
this.toggle();
}
});
this.$modal.show('test');
},
methods: {
toggle() {
this.$modal.show('test');
}
},
filters: {
},
});
</script>
</body>
The above should display the modal window on the screen immediately when I reloaded the tab or additionally when I pressed "esc" key. But for some reason this doesn't show the modal. The console log has no error on it, I can see <div id="modals-container"></div> on the HTML source, the this.$modal.show('test'); part is executed without an error. So I have no clue now. What is wrong with the code above? Thanks.
You should add Escape key EventListener code in the created life cycle hook.
window.addEventListener('keydown', this.toggle);
In methods object :
toggle(event) {
if (event.keyCode === 27) {
this.$modal.show('test');
console.log('esc key pressed');
}
}
Once you done with the requirement, You can destroy this event to prevent memory leaks.
destroyed: function() {
document.removeEventListener('keydown', this.toggle);
}
I'm looking to run a function when the state changes in my Vue app.
In my component I'm able to get the boolean state of isOpen. I'm looking to run a function that adds focus to my form input when the modal opens and isOpen is set to true. I've tried using a watcher but with no luck. I'm opening my modal by calling :class="{ 'is-open': search.isOpen }" in the html and showing it via css. Any help would be most appreciated.
data() {
return {
isFocussed: this.$store.state.search,
//checks the state of isOpen?
}
},
computed: {
search() { return this.$store.state.search },
},
watch: {
isFocussed() {
this.formfocus()
},
},
methods: {
formfocus() {
document.getElementById('search').focus()
},
please check my snippet which shows the good way to work in Vue.js, you can work with refs which is very helpful instead of document.getElementById()
new Vue({
el: '#app',
data: {
isOpen: false,
},
computed: {
},
watch: {
isOpen(){
if(this.isOpen){
this.$nextTick( function () {
this.formfocus();
}
);
}
}
},
methods: {
formfocus(){
this.$refs.search.focus();
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.js"></script>
<div id="app">
<button v-on:click="isOpen = !isOpen">show modal</button>
<div v-if="isOpen">
<input ref="search" type="text" placeholder="search">
</div>
</div>
EDIT: i have added a conditional if on the watch, i hope this solves the problem
I am not sure what your template looks like but here is how I set focus on a conditional element.
element
<input type="text" class="search-input" v-model="search" :class="{'collapsed-search': hideInput}" placeholder="Enter keyword" ref="search">
notice the ref="search" on the input.
here is the method when the input condition is true
toggleSearch() {
this.hideInput = !this.hideInput
this.search = ''
setTimeout(() => {
this.$refs.search.focus()
}, 1000)
}
this.$refs.search.focus() is called after the element has been fully created which is the purpose of the setTimeout
I have some text:
Hover me
on positioning the cursor over the text, I would like it to change to:
I'm being hovered
on moving the cursor off, the text should change back to:
Hover me
I can do this with CSS, but I can't figure out how to do it with Vue?
Something like this should work.. easiest if you use a computed property.
CodePen mirror: https://codepen.io/oze4/pen/XQapNP
new Vue({
el: "#app",
data: {
hover: false
},
computed: {
message() {
return this.hover === true ? "I'm being hovered" : "Hover me";
}
},
methods: {
handleHover(s){
this.hover = s;
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<div id="app">
<p #mouseover="handleHover(true)" #mouseleave="handleHover(false)">
{{ message }}
</p>
</div>
You need to define the output you want and a boolean for the hover state, I've called it "hoover"
data: () => ({
hoover: false
}),
computed: {
tyext() {
if (this.hoover === false) {
return "Hover Me"
}
return "I'm being hovered"
}
}
Then in the template you can have event listeners to change the boolean.
<p #mouseenter="hoover = true" #mouseleave="hoover = false">{{ tyext }}</p>
You typically wouldn't have logic like this in your template and would instead call a function like this #mouseenter="changeHoover" and change the state but I showed this for brevity, which was kind of pointless as I keep banging on like this.
How can I $watch changes to specific properties of a list item? For instance in the below code, I want to know whenever the Done property on any of the TODO list items changes.
I see from the docs that I can watch subproperties of objects, like myObjects.done in the code below, but I am not sure about the syntax for lists.
I should also mention I would prefer to $watch the data instead of putting event handlers in the UI, and function calls in any spot that changes the property
var vm = new Vue({
el: "#app",
data: {
myObject: { done: true },
todos: [
{ text: "Learn JavaScript", done: false },
{ text: "Learn Vue", done: false },
{ text: "Play around in JSFiddle", done: true },
{ text: "Build something awesome", done: true }
]
},
});
//This works wonderfully on non list items
vm.$watch("myObject.done", function(val)
{
console.log("myObject.done changed", val);
});
//How do I monitor changes to the done property of any of the todo items?
vm.$watch("todos[*].done", function(val)
{
console.log("todos.done changed", val);
})
JSFiddle here: http://jsfiddle.net/eywraw8t/376544/
With your current approach, you'd have to deep-watch the array and do some heavy computations in order to figure out the changed element. Check this link for the example:
Vue - Deep watching an array of objects and calculating the change?
I think the better approach would be using change event handler:
<input type="checkbox" v-model="todo.done" #change="onTodoChange(todo, $event)">
JSFiddle: http://jsfiddle.net/47s0obuc/
To watch specific property, I'd create another component for the list item and pass the item as value to watch the changes from that component.
Vue.component("TaskItem", {
template: `
<li
class="task-item"
:class="{ done: complete }"
>
<p>{{ task.description }}</p>
<input type="checkbox" v-model="complete">
</li>
`,
props: ["task"],
computed: {
complete: {
set(done) {
this.$emit("complete", this.task, done);
// we force update to keep checkbox state synced
// in case if task.done was not toggled by parent component
this.$forceUpdate();
},
get() {
return this.task.done;
}
}
}
});
new Vue({
el: "#app",
template: `
<div>
<ul class="task-list">
<TaskItem
v-for="(task, i) in tasks"
:key="i"
:task="task"
#complete="complete"
/>
</ul>
<button #click="completeFirstTask">Complete first task</button>
</div>
`,
data() {
return {
tasks: [
{ description: "Get milk", done: false },
{ description: "Barber shop", done: true },
{ description: "Fix sleep cycle", done: false }
]
};
},
methods: {
complete(item, done) {
item.done = done;
},
completeFirstTask() {
this.tasks[0].done = true;
}
}
});
https://codesandbox.io/s/wqrp13vp25
I used this and it works for me.
var vm = new Vue({
el: "#app",
data: {
myObject: { done: true },
todos: [
{ text: "Learn JavaScript", done: false },
{ text: "Learn Vue", done: false },
{ text: "Play around in JSFiddle", done: true },
{ text: "Build something awesome", done: true }
]
},
watch:{
todo: function(val) {
console.log ("This TODO is Done", val)
}
});
<template>
<div class="mainDiv" v-for="(index, todo) from todos">
<div>{{todo.text}}</div>
<input type="checkbox" v-model="todo[index].done">
</div>
</template>
I have a simple table where I would like to handle click elements:
<div class="row"
v-bind:class="{selected: isSelected}"
v-for="scanner in scanners"
v-on:click="scannerFilter">
{{scanner.id}} ...
</div>
JS:
new Vue({
el: "#checkInScannersHolder",
data: {
scanners: [],
loading: true
},
methods: {
scannerFilter: function(event) {
// isSelected for current row
this.isSelected = true;
// unselecting all other rows?
}
}
});
My problem is unselecting all other rows when some row is clicked and selected.
Also, I would be interested to know, it it is possible accessing the scanner via some variable of the callback function instead of using this as I might need to access the current context.
The problem is you have only one variable isSelected using which you want to control all the rows. a better approach will be to have variable: selectedScanner, and set it to selected scanner and use this in v-bind:class like this:
<div class="row"
v-bind:class="{selected: selectedScanner === scanner}"
v-for="scanner in scanners"
v-on:click="scannerFilter(scanner)">
{{scanner.id}} ...
</div>
JS
new Vue({
el: "#checkInScannersHolder",
data: {
scanners: [],
selectedScanner: null,
loading: true
},
methods: {
scannerFilter: function(scanner) {
this.selectedScanner = scanner;
}
}
});
I was under the impression you wanted to be able to selected multiple rows. So here's an answer for that.
this.isSelected isn't tied to just a single scanner here. It is tied to your entire Vue instance.
If you were to make each scanner it's own component your code could pretty much work.
Vue.component('scanner', {
template: '<div class="{ selected: isSelected }" #click="toggle">...</div>',
data: function () {
return {
isSelected: false,
}
},
methods: {
toggle () {
this.isSelected = !this.isSelected
},
},
})
// Your Code without the scannerFilter method...
Then, you can do:
<scanner v-for="scanner in scanners"></scanner>
If you wanted to keep it to a single VM you can keep the selected scanners in an array and toggle the class based on if that element is in the array or not you can add something like this to your Vue instance.
<div
:class="['row', { selected: selectedScanners.indexOf(scanner) !== 1 }]"
v-for="scanner in scanners"
#click="toggle(scanner)">
...
</div>
...
data: {
return {
selectedScanners: [],
...
}
},
methods: {
toggle (scanner) {
var scannerIndex = selectedScanners.indexOf(scanner);
if (scannerIndex !== -1) {
selectedScanners.splice(scannerIndex, 1)
} else {
selectedScanners.push(scanner)
}
},
},
...