vue-js-modal not showing in HTML - vue.js

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);
}

Related

Passing data from child component to parent and then to another child not working on page load but works after minor potentially unrelated change

I am new to Vuejs and come across this bug which I have no idea what I have done wrong. I am not receiving any console errors. It doesn't work on initial page load but it seems to work after I comment something out (or make a minor change). It will still then continue to work if I reverse the changes I just made and put it back to the original code. But once again on a fresh page load it won't work.
The issue: I am making a to do list and on page load when I add new tasks through the input field, the list does not appear on the page like it should be. I also console log the data array for this and it shows it is getting added to the array but is not getting rendered to the page. No console errors. In my code I will comment out some other data property (there are 2 additional ones below todosList in the TodoList.vue file that are currently not being used yet) and save and then the tasks will automatically appear on the page. So I think oh ok that might be the issue so with this new minor change I decide to refresh the page to see if it works as expected. Nope it doesn't so I then uncomment out what I previously commented out and save and the list appears again. But once again if I refresh the page it doesn't work. It only seems to be if I make a change inside the data function in the TodoList.vue file.
Additional info: The data is stored in the parent todos[] (App.vue), updated/pushed to array in a child (TodoCreate.vue) and sent back to the parent using $emit. This data is then sent through to another child (TodoList.vue) using props so that it can be rendered on the page.
Wondering if there is something that is not quite right in my code which is causing this to bug out like that. I will include everything in case it is something that looks unrelated to me but could be causing it.
Here is also a link to a code sandbox where the issue can be replicated by following the instructions on the page https://codesandbox.io/s/adding-new-todo-not-working-properly-jwwex?file=/src/components/TodoList.vue
main.js
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
App.vue
<template>
<div :class="currentMode">
<the-header #modeToggled="updateMode($event)"></the-header>
<main>
<todo-create #addedTodos="updateTodos"></todo-create>
<todo-list :todos="todos"></todo-list>
</main>
</div>
</template>
<script>
import TheHeader from './components/TheHeader.vue';
import TodoCreate from './components/TodoCreate.vue';
import TodoList from './components/TodoList.vue';
export default {
name: 'App',
components: {
TheHeader,
TodoCreate,
TodoList,
},
data() {
return {
currentMode: {
dark_mode: true,
light_mode: false
},
todos: [],
}
},
methods: {
updateMode(mode) {
this.currentMode = mode;
},
updateTodos(data) {
this.todos = data;
console.log(this.todos);
},
toggleCompleted() {
}
},
// provide() {
// return {
// todos: this.todos,
// };
// }
}
</script>
TheHeader.vue
<template>
<h1>To-do App</h1>
<div>
<label for="toggle-mode" aria-label="Toggle light and dark mode"></label>
<input type="checkbox" id="toggle-mode" #change="toggleMode">
</div>
</template>
<script>
export default {
emits: ['modeToggled'],
data() {
return {
toggleState: false,
}
},
methods: {
toggleMode() {
this.toggleState = !this.toggleState;
this.$emit('modeToggled', this.modeClasses);
}
},
computed: {
modeClasses() {
return {
dark_mode: !this.toggleState,
light_mode: this.toggleState
}
}
}
}
</script>
TodoCreate.vue
<template>
<div>
<label for="newtodo" class="sr-only">Create new to do</label>
<input type="text" id="newtodo" placeholder="Create a new todo..." v-model="todoval" v-on:keyup.enter="addTodo" >
</div>
</template>
<script>
export default {
emits: ['addedTodos'],
data() {
return {
todoval: '',
taskNumber: 0,
todos: [],
};
},
methods: {
addTodo() {
const val = this.todoval;
const taskNumber = this.taskNumber;
this.todos.push({ taskID: taskNumber, value: val, complete : 'not-completed'});
this.todoval = '';
this.taskNumber++;
console.log(this.todos);
this.$emit('addedTodos', this.todos);
},
}
}
</script>
TodoList.vue
<template>
<ul class="todo-items" :class="filterClass">
<li class="drop-zone" v-for="(listItem, index) in todosList" :class="listItem.complete" :key="listItem.taskID"
#drop='onDrop($event, index)'
#dragover.prevent
#dragenter.prevent>
<div class="drag-el" draggable="true"
#dragstart='startDrag($event, index)'>
<label :for="'checkbox-'+index" :aria-label="'Mark task ' + listItem.value + ' as completed'"></label>
<input type="checkbox" :id="'checkbox-'+index" #change="toggleCompleted(index, listItem.value, listItem.complete, listItem.taskID)">
<input type="text" disabled :value="listItem.value">
<img src="../assets/icon-cross.svg" #click="removeTask(index)">
</div>
</li>
</ul>
</template>
<script>
export default {
props: {
todos: Object,
filterClass: String
},
// inject: ['todos'],
data() {
return {
todosList: this.todos,
// completedTodos: [],
// activeTodos: [],
};
},
// watch: {
// todosList(data) {
// data.filter(function(todo) {
// if(todo.completed == 'completed') {
// completedTodos.push(todos);
// }
// });
// }
// },
methods: {
startDrag: (evt, item) => {
evt.dataTransfer.dropEffect = 'move'
evt.dataTransfer.effectAllowed = 'move'
evt.dataTransfer.setData('itemID', item)
},
onDrop (evt, list) {
const itemID = evt.dataTransfer.getData('itemID');
const movedData = this.todosList[itemID];
this.todosList.splice(itemID,1);
this.todosList.splice(list,0, movedData);
},
toggleCompleted() {
// still need to write this method
},
removeTask() {
// still need to write this method
}
}
}
</script>

How to run a function in Vue.js when state changes

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

How to build a bridge of events between different components in Vue?

I need to solve it
1) click mainMidLeft component
2) after clicked, to move slideLeftTop component
http://joxi.ru/ZrJBvERH1JVa8r
The problem I dont quite understand how to do this in right way..
Is it okay to create in mainMidLeft a method where I will do somethik like this:
move: () => {
document.querySelector(`.slideLeftTop`).style.position .....
}
The best practice is to use Vuex State manager with computed methods (getters) and watchers
I have made a working example for you on jsfiddle.
https://jsfiddle.net/n4e_m16/wujafg5e/4/
For more info on how vuex works please go to Here
Please let me know if you need more help :)
const store = new Vuex.Store({
state: {
mainMidLeftState: false,
},
getters: {
mainMidLeftState: state => state.mainMidLeftState,
},
mutations: {
toggleMainMidLeft: (state, payload) => {
state.mainMidLeftState = !state.mainMidLeftState
},
},
})
Vue.component('main-mid-left', {
data() {
return {
}
},
computed: {
mainMidLeftState() {
return this.$store.state.mainMidLeftState
},
},
methods: {
toggleMainMidLeft() {
this.$store.commit('toggleMainMidLeft')
// alert(this.mainMidLeftState)
},
}
})
Vue.component('slide-left-top', {
data() {
return {
}
},
computed: {
mainMidLeftState() {
return this.$store.state.mainMidLeftState
},
},
watch: {
mainMidLeftState: function(val) {
alert("YES, computed property changed and alert have been triggered by watcher in slide top left component")
}
},
methods: {
}
})
const app = new Vue({
el: '#app',
store,
})
div {
color: black;
}
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vuex#3.0.1/dist/vuex.js"></script>
<div id="app">
<!-- inlining the template to make things easier to read - all of below is held on the component not the root -->
<main-mid-left inline-template>
<div>
<h4>
main mid left
</h4>
<button v-on:click="toggleMainMidLeft()">toggle Main Mid Left State</button>
<div v-show="mainMidLeftState == true">State is true</div>
<div v-show="mainMidLeftState == false">State is false</div>
</div>
</main-mid-left>
<slide-left-top inline-template>
<div>
<h4>
slide left top
</h4>
<div v-show="mainMidLeftState == true">State is true</div>
<div v-show="mainMidLeftState == false">State is false</div>
</div>
</slide-left-top>
</div>
If you don't want to use vuex, you can create a new Vue instance as an event bus (I believe this is mentioned somewhere in the Vue tutorial):
const EventBus = new Vue()
Import EventBus to where you need it and you can send an event by:
EventBus.$emit('event-name', data)
And add the following script in created() of your receiver component:
EventBus.$on('event-name', ($event) => {
// Do something
})
I hope this helps |´・ω・)ノ

Vuejs : how to hide a component <div> on keyup escape?

Using Vue 2.5 I'm trying to hide a component if the esc key is pressed.
Inspired by the documentation on key modifiers, I wrote the following code but with no effect (for the moment I'm not hiding, just displaying a message):
Vue.component('my-component', {
data: function () {
return {isVisible:true,
message:'no key pressed'}
},
template: '<div v-on:keyup.esc="myMethod" v-if="isVisible">This is my component<div>{{message}}</div>',
methods:{
myMethod : function(){
this.message = 'esc key pressed'
//My hiding action...
}
}
})
new Vue({
el: '#app',
data: {
}
})
Fiddle
EDIT : looks like the issue is related to the fact I'm trying to implement this on a regular div, not on an input as it's usually used
I think you should add
created: function() {
document.addEventListener('keyup', this.myMethod);
}
And in your method:
myMethod(event) {
if (event.keyCode === 27) {
this.message = 'esc key pressed'
console.log('esc key pressed');
}
}
Here is working example: https://jsfiddle.net/uzxugzo7/9/
Also, don't forget to destroy it, to prevent memory leaks
destroyed: function() {
document.removeEventListener('keyup', this.myMethod);
}
To make static elements accessible to keyboard event use tabindex
<div v-on:keyup.esc="myMethod" tabindex="0" v-if="isVisible">This is my component<div>{{message}}</div>
I would use something like
mounted() {
window.addEventListener('keyup', e => {
if (e.keyCode === 27) {
* Note keyCode 27 is ESC
// do stuff here
}
})
}
Assuming you want to detect whenever the escape key is pressed anywhere inside the webpage, add an eventlistener to your application (not components) when your application is mounted (not created). For demonstration's purpose, I made use of event bus to show how the components receives the escape-key-pressed event.
(To test this snippet, click the output window first)
Vue.component('custom-component', {
template: `
<div>
<div v-show="show">
Hide when Escape Key is pressed.
</div>
<button v-on:click="reset()">Reset</button>
</div>`,
data() {
return {
show: true
}
},
created() {
window.eventBus.$on('escape-key-pressed', (val) => {
this.show = false
alert("escape key is pressed")
})
},
methods: {
reset() { this.show = true }
}
});
window.eventBus = new Vue({})
const app = new Vue({
el: "#app",
mounted() {
window.addEventListener('keyup', this.handler);
},
destroyed() {
window.removeEventListener('keyup', this.handler)
},
methods: {
handler() {
// If escape key is pressed...
if (event.keyCode == 27) {
window.eventBus.$emit('escape-key-pressed', true);
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.min.js"></script>
<div id="app">
Example
<custom-component></custom-component>
</div>

Can't emit from one child to another

I have component, inside it I am doing emiting:
methods: {
sendClick(e)
{
bus.$emit('codechange', this.mycode);
console.log(this.selectedLanguage);
this.$parent.sendCode();
this.$parent.changeView();
}
}
In the parent component I am hadling data:
var app = new Vue({
el: '#app',
data: {
currentView: 'past-form',
mycode: ''
},
methods:
{
changeView()
{
console.log(this.mycode);
},
sendCode()
{
console.log("Code is sended to server");
this.currentView = 'view-form';
bus.$emit('codechange', this.mycode);
}
},
created()
{
bus.$on('codechange', function(mycode){
console.log("test"); // this works
this.mycode = mycode; // this works
}.bind(this));
}
})
Handling in parent work fine. But on clicking on sendCode() I want to send data to third component. The third component code:
Vue.component('view-form', {
template: `
<div class="ViewCodeContainer">
<div class="ViewCode">my code here</div>
<code> {{mycode}} </code>
<div class="ViewCodeMenu">my menu here</div>
</div>`,
data() {
return {
mycode: ''
}
},
created()
{
bus.$on('codechange', function(mycode){
console.log("hererere");
console.log(mycode);
this.mycode = mycode;
}.bind(this));
console.log("test");
}
})
But handling of code does not working. Block console.log("hererere"); is not executed. What I am doing wrong?
#Wostex is correct in this case. Essentially, your view-form component doesn't exist when the event is emitted. It doesn't exist until you change the view, which you are doing in the event handler. So there is no way for it to receive the event because your handler doesn't exist.
If your dynamic component is a child of the parent, just pass the code as a property.
<component :is="currentView" :mycode="mycode"></component>
And update your view-form component.
Vue.component('view-form', {
props:["mycode"],
template: `
<div class="ViewCodeContainer">
<div class="ViewCode">my code here</div>
<code> {{code}} </code>
<div class="ViewCodeMenu">my menu here</div>
</div>`,
data() {
return {
code: this.mycode
}
}
})
Here is a working example.