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.
Related
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
i assign a name inside the data. and in my mounted i want to save the variable in tthe data. however i try this.listcount = cc inside the mounted. but when i try to display listcount in my template it is giving me 0 which this value is the one i assign in data. i want the value to 2 not 0. can anyone help me thank you. this is the code.
https://codesandbox.io/s/pedantic-noyce-viq8q?file=/src/App.vue:0-713
App.vue
<template>
<div id="app">List data: {{ listcount }}</div>
</template>
<script>
import $ from "jquery";
export default {
name: "App",
components: {},
data() {
return {
listcount: 0,
};
},
mounted() {
$(document).ready(function () {
var cc = 2;
this.listcount = cc;
console.log("LIST DATA", this.listcount);
});
},
computed: {
total() {
console.log("TOTAL", this.listcount);
return this.listcount;
},
},
};
</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>
There is no need for the document.ready call within mounted(). this.listcount = cc works fine.
If you insist, the other issue is that .ready(function () { changes the scope of this, so you would at least need to use a fat arrow. Otherwise this will be the closure. With a fat arrow, this will be the Vue component. Observe:
$(document).ready(() => {
var cc = 2;
this.listcount = cc;
console.log("LIST DATA", this.listcount);
});
The transition element of vue only works with display:none but not visibility:hidden, is there any way to make it work with visibility? I want to get the clientWidth of the element before it shows up, with display:none I can't get that value.
By the way I'm using vue3.
Here is the reproduction demo:
https://codesandbox.io/s/competent-hermann-b1s5q
I'm going to assume, for the sake of argument, that you genuinely do need to use visibility for hiding and that other potential solutions (such as opacity) won't work in your real use case, possibly because they don't prevent user interactions with the element.
However, the assertion in the question is slightly misleading. It isn't really a difference between display and visibility. The real difference here is that the display case is using v-show, which includes special handling for transitions.
The current source code for v-show can be seen here:
https://github.com/vuejs/vue-next/blob/d7beea015bdb208d89a2352a5d43cc1913f87337/packages/runtime-dom/src/directives/vShow.ts
A similar approach can be used to construct a directive that uses visibility. Below is an example. It is based on the code for v-show but I've cut it back to just the code required for this particular use case:
const visible = {
updated(el, { value, oldValue }, { transition }) {
if (!value === !oldValue) {
return
}
if (value) {
transition.beforeEnter(el)
el.style.visibility = ''
transition.enter(el)
} else {
transition.leave(el, () => {
el.style.visibility = 'hidden'
})
}
}
}
Vue.createApp({
data() {
return {
show: true
};
},
methods: {
toggle() {
this.show = !this.show;
}
},
directives: {
visible
}
}).mount('#app')
#app {
text-align: center;
}
.tooltip-enter-active {
transition: transform 0.4s ease-out, opacity 0.3s ease-out;
}
.tooltip-leave-active {
transition: transform 0.35s ease-in, opacity 0.28s ease-out;
}
.tooltip-enter-from {
transition: none;
}
.tooltip-enter-from,
.tooltip-leave-to {
transform: translateY(-30px) scale(0.96);
opacity: 0;
}
<script src="https://unpkg.com/vue#3.0.2/dist/vue.global.prod.js"></script>
<div id="app">
<transition name="tooltip">
<div v-visible="show">
Using visibility
</div>
</transition>
<button #click="toggle">toggle message</button>
</div>
I did also have to make a small CSS change to give the enter transition a kick:
.tooltip-enter-from {
transition: none;
}
You'd probably be better off without <transition> in this case:
const app = Vue.createApp({
data() {
return {
show: true,
};
},
methods: {
toggle() {
const tooltip = this.$refs.tooltip;
this.show = !this.show;
tooltip.classList.toggle("tooltip-show");
},
},
mounted() {
console.log('Tooltip-width: ', this.$refs.tooltip.clientWidth);
},
});
app.mount('#app')
#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;
}
.tooltip {
opacity: 0;
transform: translateY(-30px) scale(0.96);
transition: transform 0.35s, opacity 0.25s;
}
.tooltip-show {
opacity: 1;
transform: translateY(0) scale(1);
}
<script src="https://unpkg.com/vue#3.0.2/dist/vue.global.js"></script>
<div id="app">
<div class="tooltip" ref="tooltip">This will work!</div>
<button #click="toggle">toggle tooltip</button>
</div>
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.
I do auto expanding textarea.
The principle is this: I create a hidden div in which I place the input text, then in the updated () method I define the height of the div and apply the value to the textarea.
But there is one problem - there is text twitching, because First, the text crawls up, and then when the field is expanded, it returns to its place. As if the updated () method works late. By the same principle, I made a text field in the ReactJS there was no such effect.
What can do with it?
How it works: https://jsbin.com/zakavehewa/1/edit?html,css,js,console,output
<template>
<div class="textarea_wrap">
<textarea class="text_input textarea" v-model="value" ref="textarea"></textarea>
<div v-if="autoRow" class="text_input textarea shadow" ref="shadow">{{ value }}!</div>
</div>
</template>
<script>
export default {
props: {
autoRow: {
type: Boolean,
default: false
},
default: String
},
data () {
return {
value: this.default,
}
},
mounted() {
this.updateHeight()
},
updated() {
this.updateHeight()
},
methods: {
updateHeight() {
if (this.autoRow && this.$refs.shadow) {
this.$refs.textarea.style.height = this.$refs.shadow.clientHeight + 5 + 'px'
}
}
}
}
</script>
<style lang="scss" scoped>
.textarea_wrap {
position: relative;
}
.textarea {
line-height: 1.5;
min-height: 31px;
width: 100%;
font-family: inherit;
}
.shadow {
position: absolute;
left: -9999px;
pointer-events: none;
white-space: pre-wrap;
word-wrap: break-word;
resize: none;
}
</style>
Checkout https://github.com/wrabit/vue-textarea-autogrow-directive, it caters for rendering in hidden divs plus copying and pasting text.