How to add preloader and success message on form submit using vue-resource - vue.js

How to accomplish below task using vue-resource:
Include preloader text Loading... or gif image when fetching the data
from the server.
Show success message on form submit.

One way of doing this is :
<template>
<div>
<div class="loader" v-if="loader"></div>
<div>
//display fetchedData using logic you wish like v-for.....
</div>
<form>
//your form inputs
<button #click.prevent="submit">Submit</button>
</form>
</div>
</template>
<script>
export default{
data(){
return{
loader: false,
fetchedData: null
}
},
mounted(){
this.loader = true;
this.$httpget('your_url')
.then(response => {
this.fetchedData = response;
this.loader = false;
},err => {
});
},
methods:{
submit(){
this.loader = true;
this.$http.post('your_url', {your_body})
.then(response => {
this.loader = false;
},err => {
alert('form not submitted');
});
}
},
}
</script>
<style scoped>
loader {
position: absolute;
left:50%;
top:50%;
transform: translate(-50%, -50%);
border: 10px solid #f3f3f3; /* Light grey */
border-top: 16px solid #3498db; /* Blue */
border-radius: 50%;
width: 75px;
height: 75px;
animation: spin 2s linear infinite;
}
#keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
Here is the working fiddle

This was questioned by me, with the help that I got from #Vamsi, here is my solution:
Component
<loading-indicator v-if="loadingGroup" :bgAlpha="'.6'"></loading-indicator>
<script>
import LoadingIndicator from '../partials/LoadingIndicator'
export default {
data () {
return {
loadingGroup: true,
}
},
components: {LoadingIndicator},
methods: {
fetchGroup() {
let _this = this;
this.loadingGroup = true;
api._get({url: 'api/group/' + _this.$route.params.id})
.then(function (response) {
_this.groupData = response.data;
_this.loadingGroup = false;
});
}
},
mounted() {
this.fetchGroup();
}
}
</script>
My Template that's in: ../partials/LoadingIndicator.vue
<template>
<div class="pin pin-xy d-flex"
:style="{ backgroundColor: 'rgba(255, 255 ,255,' + bgAlpha + ')'}">
<div class="loading-indicator">
<div class="loading-indicator-circle"></div>
</div>
</div>
</template>
<script>
export default {
props: {
bgAlpha: String
}
}
</script>
<style lang="scss">
.pin {
position: absolute;
&-xy {
top: 0;
left: 0;
right: 0;
bottom: 0;
}
}
.d-flex {
display: flex;
}
.loading-indicator {
width: 32px;
height: 32px;
margin: auto;
overflow: hidden;
animation: animation-fadeIn 1s ease-in;
}
.loading-indicator-circle {
animation: loading-indicator-rotation 0.67s linear infinite;
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZpZXdCb3g9JzAgMCAzMiAzMic+PGxpbmVhckdyYWRpZW50IGlkPSdGYXN0TG9hZGluZ0luZGljYXRvci1saW5lYXJHcmFkaWVudCcgZ3JhZGllbnRVbml0cz0ndXNlclNwYWNlT25Vc2UnIHgxPScxLjc4MDQnIHkxPScxNi4wMzc5JyB4Mj0nMzAuMTQzOScgeTI9JzE2LjAzNzknPjxzdG9wIG9mZnNldD0nMC40MTY5JyBzdG9wLWNvbG9yPScjQ0RDRkQyJy8+PHN0b3Agb2Zmc2V0PScwLjkzNzYnIHN0b3AtY29sb3I9J3JnYmEoMjQ4LDI0OCwyNDksMCknLz48L2xpbmVhckdyYWRpZW50PjxjaXJjbGUgY3g9JzE2JyBjeT0nMTYnIHI9JzEyLjcnIHN0eWxlPSdmaWxsOiBub25lOyBzdHJva2U6IHVybCgjRmFzdExvYWRpbmdJbmRpY2F0b3ItbGluZWFyR3JhZGllbnQpOyBzdHJva2Utd2lkdGg6IDI7Jz48L2NpcmNsZT48L3N2Zz4=");
height: 100%;
width: 100%
}
#keyframes loading-indicator-rotation {
from {
transform: rotate(0deg)
}
to {
transform: rotate(360deg)
}
}
</style>

Related

how to set a trey of letters`style differently in a v-for loop?

It is similar to the question of whose URL I`ll put below but with one difference, I want not one letter or word style conditionally presented but a trey of letters, I wish all the letters to be in
a different color, is there any solution to it?
the tricky thing is the v-for loop based on an array whose length is not constant.
Change the style of a substring in a v-for loop
example:
I want this input tag letters full of different random colored letters
here is the full code:
<template>
<div
class="nice-input"
:class="{'nice-input--shaked': animated, 'nice-input--caret': !caret}"
>
<input
:id="id !== undefined ? id : 'input-'+name"
v-model="value"
type="text"
:name="name"
autocomplete="off"
>
<label :for="id !== undefined ? id : 'input-'+name">
<span v-for="word in arr" :key="word.sa" class="nice-input__animate"
:style="randomColor">{{ word }}</span>
</label>
</div>
</template>
<script>
export default {
name: 'weirdInput',
props: ['name', 'id'],
data () {
return {
animated: false,
caret: true,
value: '',
randomColor: '#000000',
}
},
computed: {
arr: function () {
return this.value.split('')
},
},
watch: {
value: {
handler: function (after, before) {
const self = this
this.caret = false
if (after.length > before.length) {
if (this.value.slice(-1) === ' ') return false
setTimeout(function () {
self.animated = true
setTimeout(function () {
self.animated = false
self.caret = true
}, 300)
}, 750)
}
this.randomColor = 'color: #' + Math.floor(Math.random() * 16777215).toString(16)
},
},
},
}
</script>
<style scoped>
.nice-input {
position: relative;
}
.nice-input input {
border:none;
border-radius:4px;
padding:7px 10px;
font-family: 'Lato', sans-serif;
font-size:14px;
box-shadow: rgba(0,0,0,.05) 0 5px 20px;
letter-spacing:0;
width:1000px;
color: transparent;
font-weight:900;
caret-color: #555;
}
.nice-input input:focus {
outline:none;
box-shadow: rgba(0,0,0,.1) 0 5px 20px;
}
.nice-input label {
position: absolute;
top: 6px;
left: 10px;
letter-spacing:0;
font-size:0;
}
.nice-input span {
font-family: 'Lato', sans-serif;
font-size:14px;
font-weight:900;
}
.nice-input__animate {
animation: print .75s 1 ease-in-out;
}
.nice-input--shaked {
animation: shake .2s 1 ease-in-out;
}
.nice-input--caret {
caret-color: transparent;
}
#keyframes print {
from{
position:absolute;
transform: scale(100);
}
99% {
position:absolute;
}
to {
position:relative;
}
}
#keyframes shake {
from,
to {
}
50% {
transform:scale(0.97);
}
}
</style>

Vue 3 Composition API component doesn't work reactivate class

I have a button and I just want it animate on a click - make as it been pressed. It works if I make optional component as below:
<template>
<button class="button" :class="{'shadow__click': classButton}" #click="buttonClass">
Tell me already!
</button>
</template>
<script>
export default {
data(){
return {
classButton : false
}
},
methods: {
buttonClass(){
this.classButton = true;
setTimeout(() => {
this.classButton = false
}, 1000)
}
}
}
</script>
<style lang="less">
.button{
cursor: pointer;
padding: 12px 24px;
position: relative;
border-radius: 5px;
background-color: #38b2ac;
border: none;
color: #fff8e1;
}
.shadow__click{
animation: click 1s
}
#keyframes click {
0% {
top: 0;
left: 0;
}
25% {
top: 5px;
left: 1px;
}
50% {
top: 10px;
left: 3px;
}
75% {
top: 5px;
left: 1px;
}
100% {
top: 0;
left: 0;
}
}
</style>
but it doesn't want to work when I do Composition way and I don't see a problem but it simply doesn't work (( I console.loged function and it goes to function changes the value of a variable, but class is not applying. Is it a Vue 3 bug?
<script>
import { ref } from "vue"
setup(){
let classButton = ref(false);
function buttonClass(){
classButton = true;
setTimeout(() => {
classButton = false
}, 1000)
}
return { classButton, buttonClass}
}
</script>
You should mutate the ref using the value field :
let classButton = ref(false);
function buttonClass(){
classButton.value = true;
setTimeout(() => {
classButton.value = false
}, 1000)
}

How to display a component when page is loading in nuxt

I am quite new to nuxt, and I need help here.
async asyncData({ params, route }) {
const { data } = await axios.get(
`${process.env.baseUrl}/homes/?search=${
params.search
}&home_status=${1}`
)
return {
homes: data.results,
}
}
I am trying to populate my component with data(using asyncData), but I want my skeleton loader to show if my page is loading. How do I do that in nuxt?
Here is the code for my skeleton loader;
<template>
<div class="placeholder-container">
<div class="placeholder wave">
<div class="square"></div>
<div class="line"></div>
<div class="line"></div>
<div class="line"></div>
</div>
</div>
</template>
<style scoped>
.placeholder-container {
width: 35rem;
margin: 15px auto 15px auto;
}
.placeholder {
padding: 10px;
width: 100%;
// border: 1px solid lightgrey;
display: flex;
flex-direction: column;
}
.placeholder div {
background: #e8e8e8;
}
.placeholder .square {
width: 100%;
height: 22rem;
border-radius: 1rem;
margin: 0 0 10px;
}
.placeholder .line {
height: 12px;
margin: 0 0 10px 0;
}
.placeholder .line:nth-child(2) {
width: 120px;
}
.placeholder .line:nth-child(3) {
width: 180px;
}
.placeholder .line:nth-child(4) {
width: 150px;
}
.placeholder.wave div {
animation: wave 1s infinite linear forwards;
-webkit-animation: wave 1s infinite linear forwards;
background: #f6f7f8;
background: linear-gradient(to right, #eeeeee 8%, #dddddd 18%, #eeeeee 33%);
background-size: 800px 104px;
}
#keyframes wave {
0% {
background-position: -468px 0;
}
100% {
background-position: 468px 0;
}
}
#-webkit-keyframes wave {
0% {
background-position: -468px 0;
}
100% {
background-position: 468px 0;
}
}
</style>
What I normally do without using nuxt, is to create a data variable(loading=true), and change it to false after I finish making the api call, but since asyncData runs in the server, how do I make that work? I will also appreciate it if there is a better way of doing something like this
Placeholder
To display a placeholder component on a particular page
during loading, switch from asyncData to the fetch hook, which exposes the $fetchState.pending flag that is set to true when complete:
<template>
<div>
<MyLoading v-if="$fetchState.pending" />
<MyContent v-else :posts="posts" />
</div>
</template>
<script>
export default {
data() {
return {
posts: []
}
},
async fetch() {
const { data } = await this.$axios.get(...)
this.posts = data
}
}
</script>
Customizing loading progress bar
Nuxt provides a default loading progress bar that appears at the top of the app while a page is loading. You could customize the progress bar's appearance:
// nuxt.config.js
export default {
loading: {
color: 'blue',
height: '5px'
}
}
Or you could specify your own custom loading component instead:
// nuxt.config.js
export default {
loading: '~/components/MyLoading.vue'
}
demo

How can I bind the animation duration in Vue.js?

What I am trying to build (and not use an existing solution) is an indeterminate loader with the following template:
<template>
<div class="slider">
<div class="line" :style="getLineStyle"></div>
<div class="subline inc"></div>
<div class="subline dec"></div>
</div>
</template>
I then use a getter to add the styles for the div with the line class (which works fine).
#Prop({ default: "hsl(0, 0%, 90%)" }) private backColor!: string;
...
public get getLineStyle(): any {
return {
"background-color": "black",
position: "absolute",
opacity: "0.4",
background: this.backColor,
width: "150%",
height: "100%"
};
}
I also have the following CSS:
<style lang="scss" scoped>
.slider {
position: relative;
height: 2px;
overflow-x: hidden;
}
.subline {
position: absolute;
background: #4a8df8;
height: 100%;
}
.inc {
animation: increase 2s infinite;
}
.dec {
animation: decrease 2s 0.5s infinite;
}
#keyframes increase {
from {
left: -5%;
width: 5%;
}
to {
left: 130%;
width: 100%;
}
}
#keyframes decrease {
from {
left: -80%;
width: 80%;
}
to {
left: 110%;
width: 10%;
}
}
</style>
What I want to do is turn the .inc and .dec classes to property getters as well so that I can bind the animation duration (currently set to 2s) to a property.
I initially tried modifying the template to:
<template>
<div class="slider">
<div class="line" :style="getLineStyle"></div>
<div class="subline inc" :style="getAnimateIncreaseStyle"></div>
<div class="subline dec" :style="getAnimateDecreaseStyle"></div>
</div>
</template>
With the following getters:
public get getAnimateIncreaseStyle() {
return {
animation: "increase 2s infinite"
};
}
public get getAnimateDecreaseStyle() {
return {
animation: "decrease 2s 0.5s infinite"
};
}
Only to realise that animations cannot work when added inline.
I cannot think of any other way of doing this. Any ideas?
This is how i bind the animation duration on my progress bar in vue 3, as the previous response. It's necessary removes the scope on style
<template>
...
<div v-if="duration > 0" class="w-full bg-gray-200 -mb-1">
<div
:class="`progress-bar h-0.5 progress-bar-${themeColor}`"
:style="animation"
></div>
</div>
...
</template>
<script lang="ts">
...
props: {
duration: {
type: Number,
default: 10,
},
themeColor: {
type: String,
required: false,
default: "blue",
},
},
computed: {
animation(): string {
return `animation: ${this.duration}s linear theme-color, ${this.duration}s ease-out enter forwards`;
},
},
...
<script>
<style lang="scss">
.progress-bar {
#apply bg-gray-300;
transform-origin: right;
&.progress-bar-blue {
#apply bg-gradient-to-r from-blue-600 to-blue-300;
}
&.progress-bar-green {
#apply bg-gradient-to-r from-green-600 to-green-300;
}
&.progress-bar-yellow {
#apply bg-gradient-to-r from-yellow-600 to-yellow-300;
}
&.progress-bar-red {
#apply bg-gradient-to-r from-red-500 to-red-300;
}
}
#keyframes theme-color {
100% {
background-position: 0%;
}
0% {
background-position: 100%;
}
}
#keyframes enter {
100% {
transform: scaleX(0);
}
0% {
transform: scaleX(1);
}
}
</style>

Vue js get parent property AFTER it's ready method has ran

I know I can use inherit to allow a child component to grab it's parent's properties, BUT the thing is.. is that I need to grab the property AFTER the parent ready method has ran. I'm having this issue everywhere in order to get width and height of parent components that are set in the ready method.
var Carousel = Vue.component('carousel', {
template: '#carousel',
replace: true,
data: function() {
return {
current: 1,
slideWidth: 600,
count: 6,
style: {
width: 600,
viewport: 600,
marginLeft: 0
}
}
},
computed: {
styles: function() {
return {
width: this.style.width + 'px',
marginLeft: this.style.marginLeft + 'px'
}
},
viewport: function() {
return {
width: this.style.viewport + 'px'
}
},
rounds: Math.floor(this.count / this.show)
},
props: ['show', 'slideMargin'],
ready: function() {
this.slideWidth = $(this.$el).width();
this.count = this.$children.length;
this.style.width = (this.slideWidth * this.count) + (this.slideMargin * (this.count * 2));
this.style.viewport = (this.slideWidth * this.show) + (this.slideMargin * (this.show * 2));
}
});
var CarouselSlide = Vue.component('carouselslide', {
template: '#slide',
replace: true,
data: function() {
return {
style: {
width: 200
}
}
},
computed: {
styles: function() {
return {
width: this.style.width + 'px'
}
}
},
ready: function() {
this.style.width = this.$parent.$get('slideWidth');
}
});
new Vue({
el: '#testimonials'
});
#testimonials {
width: 50%;
margin: 0 auto;
position: relative;
float: left;
min-height: 1px;
padding-left: 1.25rem;
padding-right: 1.25rem;
display: block;
}
h3 {
color: #b50937;
text-transform: uppercase;
margin: 0 0 20px;
font-size: 1.75rem;
}
.carousel {
position: relative;
overflow: hidden;
}
.carousel .slides {
overflow: hidden;
margin: 0 auto;
}
.carousel .slides .viewport {
overflow: hidden;
-webkit-transform: translateZ(0);
transform: translateZ(0);
transition: all 800ms cubic-bezier(0.77, 0, 0.175, 1);
transition-timing-function: cubic-bezier(0.77, 0, 0.175, 1);
}
.carousel .slides .slide {
position: relative;
display: block;
float: left;
margin: 0 2px;
}
.carousel .slides .slide .box {
background-color: #d1dbe5;
box-sizing: border-box;
padding: 15px 20px;
}
.view-all {
text-align: right;
}
.arrows {
position: relative;
text-align: right;
width: 100%;
}
.arrows .arrow {
background-color: #d3d3d3;
color: #fff;
padding: 2px 13px;
position: static;
transition: 0.4s ease-in-out;
display: inline-block;
cursor: pointer;
}
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css" rel="stylesheet"/>
<script src="http://cdnjs.cloudflare.com/ajax/libs/vue/0.12.13/vue.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script type="x-template" id="carousel">
<div class="carousel">
<div class="slides" v-style="viewport">
<div class="viewport" v-style="styles">
<content></content>
</div>
</div>
<div class="view-all">View all <i class="fa fa-angle-double-right"></i></div>
<div class="arrows">
<div class="arrow prev" v-on="click: prevSlide"><i class="fa fa-chevron-left"></i></div>
<div class="arrow next" v-on="click: nextSlide"><i class="fa fa-chevron-right"></i></div>
</div>
</div>
</script>
<script type="x-template" id="slide">
<div class="slide" v-style="styles">
<content></content>
</div>
</script>
<section id="testimonials">
<h3>What People Are Saying About Us</h3>
<carousel show="1" slide-margin="2">
<carouselslide>
<div class="phrase">
<div class="box">
We were looking to upgrade our equipment when we came across Ventrac. It was "wow" for
us, why did we suffer for the first six years with these other pieces of equipment when we could of had this.
</div>
</div>
</carouselslide>
<carouselslide>
<div class="phrase">
<div class="box">
We were looking to upgrade our equipment when we came across Ventrac. It was "wow" for
us, why did we suffer for the first six years with these other pieces of equipment when we could of had this.
</div>
</div>
</carouselslide>
</carousel>
</section><!-- END #TESTIMONIALS -->
Here is my Vue code since it's the only part that's relevant, although you can see what I'm having issues with upstairs ^^ (the snippet)
var Carousel = Vue.component('carousel', {
template: '#carousel',
replace: true,
data: function() {
return {
current: 1,
slideWidth: 600,
count: 6,
style: {
width: 600,
viewport: 600,
marginLeft: 0
}
}
},
computed: {
styles: function() {
return {
width: this.style.width + 'px',
marginLeft: this.style.marginLeft + 'px'
}
},
viewport: function() {
return {
width: this.style.viewport + 'px'
}
},
rounds: Math.floor(this.count / this.show)
},
props: ['show', 'slideMargin'],
ready: function() {
this.slideWidth = $(this.$el).width();
this.count = this.$children.length;
this.style.width = (this.slideWidth * this.count) + (this.slideMargin * (this.count * 2));
this.style.viewport = (this.slideWidth * this.show) + (this.slideMargin * (this.show * 2));
}
});
var CarouselSlide = Vue.component('carouselslide', {
template: '#slide',
replace: true,
data: function() {
return {
style: {
width: 200
}
}
},
computed: {
styles: function() {
return {
width: this.style.width + 'px'
}
}
},
ready: function() {
this.style.width = this.$parent.$get('slideWidth');
}
});
new Vue({
el: '#testimonials'
});
The reason I need to get it from the parent is because the clientWidth includes padding which I can't. So I can't do $(this.$el).width() in the data or computed properties data since $el is not available yet. From my child, I need to get this width AFTER the ready method has fired.
Thanks for any insight.
Without looking too closely at your code, my first thought to get parent data in the child is:
computed: {
val: this.$parent.val;
}
But I'm not certain that will work for you. Alternatively you might be able to change your parent's ready method to compiled so it runs before the child.