Show/hide + v-on:click for single page aplication vue.js - vue.js

I try to call different elements(3 html pages like home, login and chat page) in spa with a v-show/v-if with von:click v-button.
Does somebody has a clear example?

new Vue({
el: '#app',
data() {
return {
page: 'login'
}
},
methods: {
loginClick() {
this.page = 'home'
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>
<div id="app">
<div v-if="page === 'login'">
<button #click="loginClick">Login</button>
</div>
<div v-if="page === 'home'">
home page
go to chat
</div>
<div v-if="page === 'chat'">
chat page
</div>
</div>

Related

Prevent child elements for receiving click event not working

I am trying to make a modal component and dismiss it when I click outside of the component. Here is my current setup:
Auth component with click event set on a div element:
<template> <div>
<transition name="modal">
<div class="modal-mask" #click="$parent.$emit('close')">
<div class="modal-wrapper">
<div class="modal-container">
<div class="modal-header">
<slot name="header">Default Header</slot>
</div>
<div class="model-body">
<slot name="body">Default Body</slot>
</div>
<div class="modal-footer">
<slot name="footer">Default Footer</slot>
</div>
</div>
</div>
</div>
</transition> </div> </template>
SignIn component that injects necessary information:
<template>
<div>
<Auth />
</div>
</template>
Home component that uses the SignIn component:
<template>
<div class="home">
<SignIn v-if="showModal" #close="showModal = false" />
</div>
</template>
Right now when I click outside the modal it behaves ok, the close event is called.
But it is also called when I click inside the modal.
Not I tried to use #click.self , but now it doesn't work anymore even when clicking outside the modal.
<div class="modal-mask" #click.self="$parent.$emit('close')">
I am currently learning VueJs, but I don't understand how this works. I thought self will prevent propagating click event to child elements and thats it.
Anyone has an idea what is going on ?
PS: I am using this setup, because I want to have a SignIn and SignUp using the Auth component.
Either <div class="modal-wrapper"> or <div class="modal-container"> needs #click.prevent.stop
<template>
<div>
<transition name="modal">
<div class="modal-mask" #click="$parent.$emit('close')">
<div class="modal-wrapper">
<div class="modal-container" #click.prevent.stop>
<div class="modal-header">
<slot name="header">Default Header</slot>
</div>
<div class="model-body">
<slot name="body">Default Body</slot>
</div>
<div class="modal-footer">
<slot name="footer">Default Footer</slot>
</div>
</div>
</div>
</div>
</transition>
</div>
</template>
With this code you don't have to worry about click event's propagation #click.stop, for the style purpose I am using bootstrap.css but you can write your own style.
Here is the reusable component BsModal.vue
<template lang="pug">
div(v-if="showModal")
.modal.fade.d-block(tabindex='-1', role='dialog', :class="{'show': addShowClassToModal}")
.modal-dialog(role='document')
.modal-content.border-0
.modal-header.border-0
h5.modal-title
slot(name="title")
button.close(type='button', data-dismiss='modal', aria-label='Close', #click="hideModal")
span ×
.modal-body.p-0
slot
.modal-backdrop.fade(:class="{ 'show': addShowClassToModalBackdrop }")
</template>
<script>
export default {
name: 'BsModal',
props: {
showModal: {
default: false,
type: Boolean,
},
},
data() {
return {
addShowClassToModal: false,
addShowClassToModalBackdrop: false,
};
},
mounted() {
this.toggleBodyClass('addClass', 'modal-open');
setTimeout(() => {
this.addShowClassToModalBackdrop = true;
}, 100);
setTimeout(() => {
this.addShowClassToModal = true;
}, 400);
},
destroyed() {
this.toggleBodyClass('removeClass', 'modal-open');
},
methods: {
hideModal() {
setTimeout(() => {
this.addShowClassToModal = false;
}, 100);
setTimeout(() => {
this.addShowClassToModalBackdrop = false;
this.$emit('hide-modal', false);
}, 400);
},
toggleBodyClass(addRemoveClass, className) {
const elBody = document.body;
if (addRemoveClass === 'addClass') {
elBody.classList.add(className);
} else {
elBody.classList.remove(className);
}
},
},
};
</script>
And use it wherever you need by importing it:
<template lang="pug">
div
button(#click="showModal = true")
| Show Modal
bs-modal(
v-if="showModal",
:show-modal="showModal",
#hide-modal="showModal = false"
).modal
template(slot="title") Modal Title
// Modal Body content here
</template>
<script>
import BsModal from '~/components/BsModal.vue';
export default {
name: 'your component',
components: { BsModal },
data() {
return {
showModal: false,
};
},
};
</script>
If you don't like pug template language then you can convert PUG to HTML here: https://pughtml.com/

infinite loop when create infinite scroll using vue.js and laravel

good day; kindly need your support to finalize this issue i try to make infinite scrolle using laravel and vue.js and my proplem is get in finite loop to set request to data base and mu applocation hang up this is my code x component
<template>
<div class="container" style="margin-top:50px;">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header"><strong> Laravel Vue JS Infinite Scroll - ItSolutionStuff.com</strong></div>
<div class="card-body">
<div>
<p v-for="item in list">
<a v-bind:href="'https://itsolutionstuff.com/post/'+item.slug" target="_blank">{{item.title}}</a>
</p>
<infinite-loading #distance="1" #infinite="infiniteHandler"></infinite-loading>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
mounted() {
alert()
console.log('Component mounted.')
},
data() {
return {
list: [],
page: 1,
};
},
methods: {
infiniteHandler($state) {
let vm = this;
this.$http.get('/Services?page='+this.page)
.then(response => {
return response.json();
}).then(data => {
$.each(data.data, function(key, value) {
vm.list.push(value);
});
$state.loaded();
});
this.page = this.page + 1;
},
},
}
</script>
this is my route
Route::get('/Services', 'ServicesController#Services');
Problem 1
You are binding to the distance property wrong.
Solution
Instead of <infinite-loading #distance="1" #infinite="infiniteHandler"></infinite-loading>
it should be
<infinite-loading :distance="1" #infinite="infiniteHandler"></infinite-loading>
Problem 2
In the code, this.page is being incremented before $http.get is resolved.
This may result in unintentional side effects.
Solution
As per the example in docs vue-infinite-loading hacker news example you should be incrementing the page after data is loaded.

Vue component not showing no errors

I am trying to do a very simple vue example and it won't display. I've done similar things before, but this won't work.
It is an extremely simple task list. It is an input with a submit button that adds an item to a list. For some reason the component does not render at all. I am very lost am supposed to give a presentation on vue. I was hoping to use this as an example.
I'm really not sure what else to say about this, but stack overflow won't let me submit this without typing more information about the issue.
<div id="app">
<task-list></task-list>
</div>
Vue.component('task-list-item', {
props: ["task"],
template: '#task-list-item-template'
})
Vue.component('task-list', {
data: function () {
return {
taskList: [],
newTask: ''
}
},
methods: {
addTask: function () {
var self = this;
if (self.newTask !== ""
&& self.newTask !== null
&& typeof self.newTask !== "undefined") {
this.taskList.push(self.newTask);
this.newTask = "";
}
}
},
template: '#task-list-template'
})
new Vue({
el: '#app',
data: function () {
return {
}
}
})
<script id="task-list-template" type="text/x-template">
<input v-model="newTask" />
<button v-on:click="addTask()">Add Task</button>
<ul>
<task-list-item v-for="taskItem in taskList"
v-bind:task="taskItem">
</task-list-item>
</ul>
</script>
<script id="task-list-item-template" type="text/x-template">
<li>{{task}}</li>
</script>
I am getting no error messages of any kind.
I think the problem is there should be only 1 child under <script id="task-list-template" type="text/x-template"></script>. In task-list-template, you have multiple children. Try to wrap them in 1 div
<script id="task-list-template" type="text/x-template">
<div>
<input v-model="newTask" />
<button v-on:click="addTask()">Add Task</button>
<ul>
<task-list-item v-for="taskItem in taskList"
v-bind:task="taskItem">
</task-list-item>
</ul>
</div>
</script>
Demo on codepen
According to A Single Root Element
Every component must have a single root element
To fix you can do some thing like:
<script id="task-list-template" type="text/x-template">
<div>
<input v-model="newTask" />
<button v-on:click="addTask()">Add Task</button>
<ul>
<task-list-item v-for="taskItem in taskList" v-bind:task="taskItem">
</task-list-item>
</ul>
</div>
</script>

Apply v-focus to the first input field on a page

I've a Vue component in which I'm trying to autofocus the first field using v-focus. But my problem is, I've dynamic components that will be included at the top of the page. So in that case how can I apply autofocus to dynamically included component?
They key is to set ref on all your inputs to the same string like this:
<input type="text" ref="myInputs"/>
Then you will have access to an array called this.$refs.myInputs inside an event handler.
So you just need to do
this.$refs.myInputs[0].focus();
new Vue({
el: "#app",
mounted() {
this.$refs.myInputs[0].focus();
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<div id="app">
<div>
<div v-for="index in 3" :key="index">
<input ref="myInputs" type="text" />
</div>
</div>
</div>
It's hard to tell how you're adding the input(s) to the DOM, without any pseudo code from you, but this is one way to do it..
[CodePen mirror]
new Vue({
el: "#app",
data: {
inputs: ["firstName", "lastName"]
},
watch: {
inputs() {
this.$nextTick(() => {
this.focusFirstInput();
});
}
},
methods: {
focusFirstInput() {
let first = this.inputs[0];
let firstInput = this.$refs[first][0];
firstInput.focus();
},
handleClick() {
this.inputs.push("newInput");
}
},
mounted() {
this.focusFirstInput();
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js"></script>
<div id="app">
<div>
<div v-for="(input, index) in inputs" :key="index">
<input :ref="input" type="text" />
</div>
<div>
<button type="button" #click="handleClick">Click to add input</button>
</div>
</div>
</div>
I found this answer on Laracast and it worked for me. All I did was insert the code below in my dynamic form field.
this.$nextTick(() => {
let index = this.items.length - 1;
let input = this.$refs.title[index];
input.focus();
});
HTML
<div id="app">
<ul v-for="item in items">
<li>
<input :ref="'title'" v-model="item.title">
</li>
</ul>
<button v-on:click="addItem">Add Item</button>
</div>
JS
let app = new Vue({
el: '#app',
data: {
items: [
{title: 'Apple'},
{title: 'Orange'},
]
},
methods: {
addItem(){
this.items.push({title: "Pineapple"});
this.$nextTick(() => {
let index = this.items.length - 1;
let input = this.$refs.title[index];
input.focus();
});
}
}
});
Note: make sure to add :ref="'title'" into your dynamic form field.
Credits to the original author of the solution.

Vue2 v-bind not working properly on child instance?

I have a child instance of Vue and v-bind only works on the parent instance but not in the child one. I did a sample file to explain my issue.
Am I missing something?
This is my code:
var app2 = new Vue({
el: '#app2',
data: {
isSpinning2: true
},
methods: {
stop2: function() {
app2.isSpinning2 = false;
}
}
});
var app1 = new Vue({
el: '#app1',
data: {
isSpinning: true
},
methods: {
stop1: function() {
app1.isSpinning = false;
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
<div id="app1">
<i v-bind:class="{'fa-spin': isSpinning}" class="fa fa-address-book"></i>
<button v-on:click="stop1">Stop 1</button>
<div id="app2">
<i v-bind:class="{'fa-spin': isSpinning2}" class="fa fa-address-book"></i>
<button v-bind:onclick="stop2">Stop 2</button>
</div>
</div>
This is exactly what components are for. Here is a component encapsulating your spinner buttons.
console.clear()
Vue.component("spinner",{
template: `
<div>
<i v-bind:class="{'fa-spin': isSpinning}" class="fa fa-address-book"></i>
<button v-on:click="stop"><slot /></button>
</div>
`,
data(){
return {
isSpinning: true
}
},
methods: {
stop: function() {
this.isSpinning = false;
}
}
})
var app1 = new Vue({
el: '#app1',
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.2/vue.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" />
<div id="app1">
<spinner>Stop 1</spinner>
<spinner>Stop 2</spinner>
</div>