Displaying multiple instances of a component in Vue.js - vue.js

I'm completely new to vue.js and as part of my learning I'm just trying to create a button, that when each time this button is pressed, an annotation (a draggable text box where users can input their text) will be created. This is what I currently have :
<template>
<div id="app">
<button v-on:click="addAnnotation()">Add annotation</button>
<div v-for="(annotation, index) in annotations" :key="index">
<component :is="annotation">
</component>
</div>
</div>
</template>
<script>
import DraggableAnnotation from "./components/DraggableAnnotation.vue";
export default {
name: "App",
components: {
DraggableAnnotation,
},
data: function () {
return {
annotations: [],
};
},
methods: {
addAnnotation: function () {
this.annotations.push({ DraggableAnnotation });
},
},
};
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
I'm not very sure if my implementation is correct. Each time the button is pressed, I want to create an annotation, and add it to the list of annotations. Then, I'm trying to render the list of annotations using v-for to display all the annotations to the user, but I'm not sure what to pass in as the key as I don't have any props for the DraggableAnnotation component.
When I press my addAnnotation button, I get this error in the console log.
vue.runtime.esm.js?2b0e:619 [Vue warn]: Failed to mount component: template or render function not defined.
found in
---> <Anonymous>
<App>
<Root>
Edit : defined index in here
<div v-for="(annotation, index) in annotations" :key="index">

I think it error cuz index not definded
<div v-for="annotation in annotations" :key="index">
instead
<div v-for="(annotation, index) in annotations" :key="index">

Related

Vuejs - vue2-editor not accepting all tags

I have implemented the vue2-editor, and here is the below code
<div id="app">
<vue-editor v-model="content"></vue-editor>
<div v-html="content"></div>
</div>
</template>
<script>
import { VueEditor } from "vue2-editor";
export default {
components: {
VueEditor
},
data() {
return {
content: "<p>Some initial content</p>"
};
}
};
</script>
<style>
#app {
font-family: "Avenir", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
/* text-align: center; */
color: #2c3e50;
margin-top: 60px;
}
</style>
If the content is wrapped with <p> tag then the editor shows the content correctly, if it is wrapped with <div> tag then it throws an exception,
Exception: Uncaught TypeError: Cannot read properties of undefined (reading 'emit')
Few more tags with same issue (<title>,<small>,<span>)
sandbox link for reference: https://codesandbox.io/s/liz23?file=/src/App.vue:0-553
Thanks
Check this codesanbox I made: https://codesandbox.io/s/stack-72533246-vue2editor-div-bug-z8qqp8?file=/src/App.vue
It looks that is a bug of the library, meanwhile it gets fixed you can use this workaound.
import { VueEditor, Quill } from "vue2-editor";
const Block = Quill.import("blots/block");
Block.tagName = "DIV";
Quill.register(Block, true);
Refer to this github issue thread for more info: https://github.com/davidroyer/vue2-editor/issues/63

Can't get the value of Date Range Picker (Vuejs)

I am a newbie in VueJS. I want to get the value of the date range that is selected and console.log it when the user clicks the button. However, whenever I click the button, the value printing in console is null. Kindly Help.
This is the code:
App.vue
<template>
<div id="app">
<VueRangedatePicker v-model="datepicker"></VueRangedatePicker>
<button class="button" #click="showdata()" value="Test">Normal</button>
</div>
</template>
<script>
import VueRangedatePicker from "vue-rangedate-picker";
export default {
name: "App",
components: {
VueRangedatePicker
},
data() {
return {
datepicker: null
};
},
methods: {
showdata() {
console.log("DATE PICKER", this.datepicker);
}
}
};
</script>
<style>
#app {
font-family: "Avenir", Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
Code can be accessed here.
For v-model to work, a component needs to have a prop called value and emit an input event.
VueRangedatePicker doesn't have that prop or event. Instead, it emits a selected event when it updates. You can listen for it using #selected. For example:
<VueRangedatePicker #selected="updateDatePicker"></VueRangedatePicker>
methods: {
showdata() {
console.log("DATE PICKER", this.datepicker);
console.log("start: " + this.datepicker.start);
console.log("end: " + this.datepicker.end);
},
updateDatePicker(value) {
console.log("updating datepicker value");
this.datepicker = value;
}
See updated code here.

Why does this event actually trigger?

Im in a vue project using routing, its a tutorial: https://www.youtube.com/watch?v=Wy9q22isx3U
The repo with the full code is here:
https://github.com/bradtraversy/vue_crash_todolist
My Home.vue looks like this:
<template>
<div id="app">
<AddTodo v-on:add-todo="addTodo"/>
<Todos v-bind:todos="todos" v-on:del-todo="deleteTodo"/>
</div>
</template>
<script>
import AddTodo from '../components/AddTodo'
import Todos from '../components/Todos'
export default {
name: 'home',
components: {
Todos,
AddTodo
},
data() {
return {
todos: [
{
id: 1,
title: "Todo one",
completed: false
},
{
id: 2,
title: "Todo two",
completed: true
},
{
id: 3,
title: "Todo three",
completed: false
}
]
}
},
methods: {
deleteTodo(id){
this.todos = this.todos.filter(todo => todo.id != id)
},
addTodo(newTodo){
this.todos = [...this.todos, newTodo]
}
}
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
Whats important here is the Todos element in the markup.
Its a complex component which imported TodoItem itself.
Now Todos looks like this:
<template>
<div>
<div v-bind:key="todo.id" v-for="todo in todos">
<TodoItem v-bind:todo="todo" v-on:del-todo="$emit('del-todo', todo.id)"/>
</div>
</div>
</template>
<script>
import TodoItem from './TodoItem.vue'
export default {
name: "Todos",
components: {
TodoItem
},
props: ["todos"]
}
</script>
<style scoped>
</style>
And it imported TodoItem, which is here:
<template>
<div class="todo-item" v-bind:class="{'is-complete':todo.completed}">
<p>
<input type="checkbox" v-on:change="markComplete">
{{todo.title}}
<button #click="$emit('del-todo', todo.id)"class="del">x</button>
</p>
</div>
</template>
<script>
export default {
name: "TodoItem",
props:["todo"],
methods: {
markComplete(){
this.todo.completed = !this.todo.completed
}
}
}
</script>
<style scoped>
.todo-item {
background: #f4f4f4;
padding: 10px;
border-bottom: 1px #ccc dotted;
}
.is-complete {
text-decoration: line-through;
}
.del {
background: #ff0000;
color: #fff;
border: none;
padding: 5px 9px;
border-radius: 50%;
cursor: pointer;
float: right;
}
</style>
Now what confuses me is the syntax surrounding the emitted events.
In TodoItem, I have this emitted event from the button:
<button #click="$emit('del-todo', todo.id)"class="del">x</button>
Now this is completely understandable for me because we have the event trigger specified with "#click".
This is then exported to parent, Todos.vue, and there we can see this:
<TodoItem v-bind:todo="todo" v-on:del-todo="$emit('del-todo', todo.id)"/>
Here Im starting to get confused.
Again, in the long syntax, a event trigger is defined:
v-on:del-todo
But del-todo is not an event trigger. Its neither click, nor change, nor input.
So how can this code even work? What does vue.js imply when it encounters code like above?
My confusion then gets even worse in Home.vue
<Todos v-bind:todos="todos" v-on:del-todo="deleteTodo"/>
For the third time, an event trigger is specified.
And for the second time, this event trigger doesn't specify a "native" trigger like click.
Now I already wrapped my head around this and I could at least beat SOME sense into it.
In Todos.vue and Home.vue, the specified events seem to execute when del-todo has fired. So they are like callbacks, they take the return value of del-todo.
In Todos.vue, triggering del-todo emits del-todo to its parent, Home.vue.
Is that correct?
Home.vue then triggers deleteTodo when del-todo is fired.
However, deleteTodo requires an id to be handed over through the parameter, but interestingly, <Todos v-bind:todos="todos" v-on:del-todo="deleteTodo"/> doesnt.
Still, the function works. So how does id ever arrive in deleteTodo?
A similar problem arises in TodoItem.vue. Here, del-todo is called, but actually we haven't any sort of declaration of this function anywhere in the script inside TodoItem.vue. So again, what does vueJS imply when it encounters a situation where a function is emitted/called which wasn't defined anywhere?
In Vue.js there are not only the events click change and input, it also allows you to define custom events. All you have to do ist throw an event in the child-component with $emit('my-custom-event', param1, param2) and catch it in the direct parent-component with v-on:my-custom-event="handler" (you can also write #my-custom-event, thats synonym). The handler is a function that takes the parameters passed when emitting the event. Your handler in the Todos-component catches the event del-todo and throws a new event with the same name. The Home-component catches that event and has its function deleteTodo defined as its handler, so this function is being called (the id that was passed with the event is the parameter for deleteTodo).
What is happening is that each time you click there is an event AND value emitted to the parent component, the parent component has a listener v-on:del-todo means it is listening on the del-todo event, once it is triggered/handled it emits it again one level up until it reaches the component where you want to actually manipulate the data (delete the item based on id).
Note: the value is implicitly passed into the handler function deleteTodo so even though it is not explicitly there (i.e. deleteTodo($event) it is there.

VueJS + Nuxtjs Unexpected Token 'export'

So i have this code as my index page and It was working, but a couple minutes later it just stopped.
the error is:
SyntaxError
Unexpected token export
Within the script section, If i remove my import then the error will go away, but I need to import it and use it. It was working with the package being imported, but I have looked this code up and down I have no idea what the heck is going on.
Anyone have any suggestions? Am I dumb and have missed something so simple?
<template>
<section class='container'>
<img class='my-4' src="~/assets/images/carousel/1.png" alt="card" />
<div class='text-center mx-auto my-4'>
<button> Send a card </button>
<p class='subtle my-4'> Or </p>
<button class='btn-blue'> Open a card </button>
</div>
<div id="qrcode"></div>
</section>
</template>
<script>
import qrcode from 'qrcode-generator-es6'; <<<<<<<<< SYNTAX ERROR AROUND HERE
export default{
data : function(){
return {};
},
methods : {
},
mounted : function(){
const qr = new qrcode(0, 'M');
qr.addData('https://app.voxicard.com/?v=vx-9FEFCA66-F592-4FF5-97B8-93B2FD78666D');
qr.make();
document.getElementById('qrcode').innerHTML = qr.createSvgTag({
margin : 0,
cellColor : function(){
return "#48658B";
},
});
},
};
</script>
<style>
#qrcode {
width: 200px;
height: 200px;
background-color: red;
}
img {
display: block;
max-height: 500px;
text-align: center;
margin: auto;
}
button {
font-size: 125%;
}
</style>
In your build property in nuxt.config.js you'll need to add a transpile block that targets this library:
build: {
transpile: [
'qrcode-generator-es6'
]
}
This is due to the fact that nuxt expects libraries to export as CJS modules and not ES6 modules.
In nuxt.config.js replace export default { on module.exports = {

vuejs v-show in a custom vue component invokes TypeError: Cannot read property '_withTask' of undefined

I have made a following custom vue component "editable-image".
<template>
<span style="position: relative; text-align: center; color: white; cursor: pointer; margin-right: 10px;" #mouseover="iconShown = true" #mouseleave="iconShown = false" #click="click">
<img :style="`width: ${width};`" :src="imageUrl"/>
<v-icon style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);" large color="grey" v-show="iconShown">edit</v-icon>
</span>
</template>
<script>
export default {
props: ['imageUrl', 'width', 'click'],
data() {
return {
iconShown: false
}
}
}
</script>
Then, in my main component, import the above "editable-image" and let the mouse over, TypeError: Cannot read property '_withTask' of undefined occurs.
I noticed that the v-show is the main cause of the problem, but despite many attempts, it has not been solved.
Solved
The problem is click props.
In the main component, I should have entered the click props correctly!