I'm trying to create a button in my program that toggles on a number of other things and removes itself once it's clicked. The relevant HTML is as follows:
<div id="app">
<button #click="reveal" v-if="!showlists">Start</button>
<ul v-if="showlists">
<list v-for="name in chosenNames" v-bind:name="name"></list>
</ul>
</div>
In this, the unordered list should be shown once the variable "showlists" is true and the button should be removed once "showlists" is true. My Vue app looks like this:
let app = new Vue({
el: "#app",
data: {
showlists: false,
chosenNames: [
{ text: "name1" },
{ text: "name2" },
{ text: "name3" },
]
},
methods: {
reveal: function() {
showlists = true;
}
}
})
Based on this, the "showlists" variable starts as false, and the program works as intended with the button showing and the list hidden. Once the button is clicked, the function runs and showlists is then set to true (I confirmed this in my troubleshooting efforts). However, once this occurs, the DOM does not dynamically update and instead just remains as it was at the start.
Sorry if this is something really basic, I'm very new to Vue and still trying to learn :)
Any and all help would be appreciated.
You have to use the "this" keyword in your "revel" method before showlists like this.showlists = true; variable in your "Vue" instance.
For example, you can write like as follows
<div id="app">
<button #click="reveal" v-if="!showlists">Start</button>
<ul v-if="showlists">
<list v-for="(name, index) in chosenNames" :name="name" :key="'list-'+index"></list>
</ul>
</div>
And for new "Vue" instance
let app = new Vue({
el: "#app",
data: {
showlists: false,
chosenNames: [
{ text: "name1" },
{ text: "name2" },
{ text: "name3" },
]
},
methods: {
reveal: function() {
this.showlists = true;
}
}
})
I hope that might solve the problem :)
your code has 4 bug:
v-bind is set element's attribute, not innerHTML
showlists need change to this.showlists
showlists = true; is always set to true
list isn't valid html tag, you need li
below is right code:
<div id="app">
<button #click="reveal" v-if="!showlists">Start</button>
<ul v-if="showlists">
<li v-for="name in chosenNames" v-html="name"></li>
</ul>
</div>
<script>
let app = new Vue({
el: "#app",
data: {
showlists: false,
chosenNames: [
{ text: "name1" },
{ text: "name2" },
{ text: "name3" },
]
},
methods: {
reveal: function() {
this.showlists = !this.showlists;
}
}
})
</script>
Related
I am looking for some good ideas on how to filter an array that contains a stack trace. I have a database table that has four columns, one with the stack trace error messages, one that shows the priority of the error, one that shows the date the error was registered and finally a column that displays an custom made error message, which I have placed on multiple try-blocks around my system.
On the frontend I am fetching the data with axios and placing it inside an object called errors. Then in my computed properties I create an array of fields that contain the individual columns from the database and their data. I use the Bootstrap table to output it.
<template>
<b-container>
<b-card class="mt-4">
<h5>{{ $t('events') }}</h5>
<b-table
:items="errors"
:fields="fields"
:per-page="[5, 10]"
sort-desc
primary-key="id"
/>
</b-card>
</b-container>
</template>
<script>
import {errorService} from '#/services/error';
import moment from 'moment';
export default {
components: {
CommonTable,
flapper
},
data() {
return {
errors: null,
};
},
computed: {
fields() {
return [
{
key: 'priority',
label: this.$t('errorLogs.priority'),
sortable: true
},
{
key: 'creationDateTime',
label: this.$t('creationDateTime'),
formatter: date => moment(date).locale(this.$i18n.locale).format('L'),
sortable: true
},
{
key: 'stackTrace',
label: this.$t('errorLogs.stackTrace'),
sortable: true
},
{
key: 'errorMessage',
label: this.$t('message'),
sortable: true
},
]
},
},
methods: {
load(){
errorService.getErrorLogs().then(result => {
this.errors = result.data
})
}
},
created() {
this.load()
}
};
</script>
It works as it should, but the output for the stack trace takes up way too much space in the table column.
Ideally it should only show
org.springframework.web.method.annotation.MethodArgumentTypeMismatchException
and then if the user wants more detail they can click on the stack trace and get the full version in a pop up or something.
I am guessing the easiest solution would be to filter the stack trace, so that it does not show any text beyong the : sign.
But how would I implement this in the setup that I currently have?
I am guessing in computed properties I need add a method to the stackTrace field.
So:
{
key: 'stackTrace',
label: this.$t('errorLogs.stackTrace'),
sortable: true
function: this.filteredStackTrace()
},
And then create a new method.
filteredStackTrace(){
this.errors.stackTrace.filter(some filter...)
}
Maybe something like following snippet:
const app = Vue.createApp({
data() {
return {
st: `Exception in thread "main" java.lang.NullPointerException
at com.example.myproject.Book.getTitle(Book.java:16)
at com.example.myproject.Author.getBookTitles(Author.java:25)
at com.example.myproject.Bootstrap.main(Bootstrap.java:14)`,
expanded: false
};
},
computed: {
firstLine() {
return this.st.split('\n')[0]
},
allLines() {
return this.st.split('\n').filter((item, idx) => idx !== 0).toString()
}
},
})
app.mount('#demo')
<script src="https://unpkg.com/vue#3/dist/vue.global.prod.js"></script>
<div id="demo">
{{ firstLine }}
<button #click="expanded = !expanded">all</button>
<div v-if="expanded">{{ allLines }}</div>
</div>
i have a v-for loop as my selection for dialog boxes I want to open
<v-card #click="page.model = true">
page.model is my v-model for a v-dialog
data() {
return {
dialog1: false,
dialog2: false,
pages: [
{
id: "1",
model: "dialog1",
},
{
id: "2",
model: "dialog2",
},
],
};
},
howcome #click="page.model = true" doesn't work but #click=dialog1 = true " does?
I also tried :#click="page.model = true" and #click="${page.model} = true"
Thank you in advance
So we dont see your modal HTML so I presume something like this:
<v-card v-for="page in pages" #click="changePage(page.id)">
<modal v-model="pages[0].isOpen">[CONTAIN OF PAGE 1]</modal>
<modal v-model="pages[1].isOpen">[CONTAIN OF PAGE 2]</modal>
data() {
return {
pages: [
{
id: "1",
model: "dialog1",
isOpen: false,
},
{
id: "2",
model: "dialog2",
isOpen: false,
},
],
},
methods: {
changePage(id) {
// close all other modal page
this.pages.each(el => el.isOpen = false);
// open the good one
const index = this.pages.findIndex(el => el.id == id);
this.pages[index].isOpen = true;
}
},
I have done something similar. Call a method and pass index of item. In the method you can then access the specific model via index
<v-card #click="updateDialog(index)">
updateDialog(i): {
this.pages[i].model = true
}
I have a list of gallery images and I want to be able to select an image to make it preview: true By default, there always has to be a preview image.
What I am trying to is
Click the image
Remove the preview on all images
Make the clicked image a preview
Update API
This is the code I'm trying to get to work:
export default {
props: ['images'],
methods: {
makePreview (image, index) {
// Loop through images and remove preview
this.images.map((image, index) => {
image.preview = false
})
// Set clicked image to preview
image.preview = true
// Update API
this.updateImages()
}
}
}
When I console.log(image) in the method, I do get the correct image object to update, but this is not updating the images prop.
EDIT:
Image object:
{
alt: "New venue"
id: 111
large: "https://radnomcdn.com/large.jpg"
order: 2
preview: false
thumb: "https://radnomcdn.com/thumb.jpg"
}
I'm not sure what we are missing here in your question, this code seems to work. The only main difference I see is I commented out the updateImages method.
Technically, this is a situation where you are mutating a property, which is generally frowned upon. The intention is that properties are passed down and changes to those properties are emitted as events back to the parent. However, objects and arrays can be mutated.
const images = [{
alt: "New venue",
id: 111,
large: "https://radnomcdn.com/large.jpg",
order: 2,
preview: false,
thumb: "https://radnomcdn.com/thumb.jpg"
},
{
alt: "New venue",
id: 222,
large: "https://radnomcdn.com/large.jpg",
order: 3,
preview: false,
thumb: "https://radnomcdn.com/thumb.jpg"
},
{
alt: "New venue",
id: 333,
large: "https://radnomcdn.com/large.jpg",
order: 4,
preview: false,
thumb: "https://radnomcdn.com/thumb.jpg"
}
]
Vue.component("display-images", {
template:`
<div>
<div v-for="image, index in images">
{{image.preview}}
<button #click="makePreview(image, index)">Preview</button>
</div>
</div>
`,
props: ['images'],
methods: {
makePreview(image, index) {
// Loop through images and remove preview
this.images.map((image, index) => {
image.preview = false
})
// Set clicked image to preview
image.preview = true
// Update API
//this.updateImages()
}
},
mounted(){
this.makePreview(this.images[0])
}
})
new Vue({
el: "#app",
data:{
images
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.js"></script>
<div id="app">
<display-images :images="images"></display-images>
<div style="margin-top:5em">{{images}}</div>
</div>
I am new to Vue.JS (2) and I am trying to learn components now. I try to use a component in another component with data from a data method (I know you can't use the property Data in a component). My code now:
HTML
<div id="root">
<h1>Tasks</h1>
<list></list>
</div>
JS
Vue.component('list', {
template: '<task v-for="task in tasks">{{ task.task }}</task>',
data: function() {
return {
tasks: [
{task: 'Go to the store', completed: false},
{task: 'Go to the shop', completed: true},
{task: 'Go to the mall', completed: true}
]
};
}
});
Vue.component('task', {
template: '<li><slot></slot></li>'
});
new Vue({
el: '#root'
});
this will return a white screen. If I remove the data, and just use a string in the task template it shows the string, so the component "task" is working in the component "list".
Vue.component('list', {
template: '<task>Test</task>',
});
Vue.component('task', {
template: '<li><slot></slot></li>'
});
new Vue({
el: '#root'
});
So it seems like there is something wrong with displaying the data to the view with the method/data. I've tried many things but I just can't get it right.
Any help would be great, thanks in advance
As documented here,
components must contain exactly one root node.
Putting a v-for on the top-level element makes it repeated. If you wrap that element in a div, it works.
It looks like you may get around that limitation if you want to write your own render function.
Vue.component('my-list', {
template: '<div><task v-for="task in tasks">{{ task.task }}</task></div>',
data() {
return {
tasks: [{
task: 'Go to the store',
completed: false
},
{
task: 'Go to the shop',
completed: true
},
{
task: 'Go to the mall',
completed: true
}
]
};
}
});
Vue.component('task', {
template: '<li><slot></slot></li>'
});
new Vue({
el: '#root'
});
<script src="//cdnjs.cloudflare.com/ajax/libs/vue/2.2.5/vue.min.js"></script>
<div id="root">
<h1>Tasks</h1>
<my-list></my-list>
</div>
I am trying to replicate the TODO MVC in VueJs.
(Please checkout this codepen : http://codepen.io/sankalpsingha/pen/gwymJg )
I have created a component called 'todo-list' with the following code :
Vue.component('todo-list',{
template: '#todo-list',
props: ['todo'],
data: function() {
return {
// Let us set up a isEditing Boolean so that we can know if the user
// will edit something and we need to change the state to reflect it.
isEditing: false,
}
},
methods: {
enableEditing: function() {
this.isEditing = true;
},
editTodo: function(todo) {
// todo.todo = todo.todo.trim();
this.isEditing = false;
},
removeTodo: function(todo) {
//this.todos.$remove(todo); // --> This part is not working?
}
}
});
However, I have the data defined in the app instance :
var app = new Vue({
el: '#todo-section',
data: {
newTodo: '',
todos: [
{
id: 1,
todo: 'Go to the grocery',
completed: false,
},
{
id: 2,
todo: 'See the movie',
completed: true,
},
{
id: 3,
todo: 'Jack Reacher : Tom Cruise',
completed: false,
}
]
},
methods: {
addTodo: function() {
// This will not allow any empty items to be added.
if(this.newTodo.trim() == '') {
return;
}
this.todos.push({
todo: this.newTodo.trim(),
completed: false,
});
this.newTodo = '';
}
}
});
I am not able to delete a single Todo from the list. My guess is that I have to send a emit message to the app instance and put up a listener there to delete the data from it? How do I delete the data?
When I tried to delete by clicking the x button in your codePen example, I see the error: this.$parent.todos.$remove is not a function.
I have not looked deeply into your code. But attempting to access parent component methods using this.$parent is not a good idea. Reason: a component can be used anywhere, and assuming that it will have a $parent with a particular property or method is risky.
As you suggested in your question, you need to use $emit from the child component to delete the data.
There was another similar question here few days ago, for which I created a jsFiddle: https://jsfiddle.net/mani04/4kyzkgLu/
The child component has some code like:
<button #click="$emit('delete-row')">Delete</button>
This sends out an event to parent component. Parent component can subscribe to that event using v-on as seen in that jsFiddle example.
Here is that other question for reference: Delete a Vue child component
It's preferable to use your methods (DeleteTodo, EditTodo...) in your parent.
var app = new Vue({
el: '#app',
data: {
newTodo: '',
todos: [{
id: 1,
title: 'Go to the grocery',
completed: false
}, {
id: 2,
title: 'See the movie',
completed: true
}, {
id: 3,
title: 'Jack Reacher : Tom Cruise',
completed: false
}]
},
methods: {
addTodo: function() {
this.todos.push({
todo: this.newTodo.trim(),
completed: false
});
this.newTodo = ''
},
deleteTodo: function(todo) {
this.todos = this.todos.filter(function(i) {
return i !== todo
})
}
}
});
<div id="app">
<ul>
<li v-for="todo in todos">{{ todo.title }}
<button #click.prevent="deleteTodo(todo)">
Delete
</button>
</li>
</ul>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.3/vue.js"></script>