I'm pretty new to Vue, I have an app running with a few different pages. One of the pages I'm trying to have is one that displays all images that are found in a directory on my host and to have them auto reload whenever the image changes (a separate app updates these images every 10 seconds). I've been able to display all the images (although only by listing them) and I can't seem to figure out how to get the pictures to auto refresh. Below is the code, any help is much appreciated.
<template>
<div id="app" class="media-display">
<figure class="media-post" v-for="(image, index) in images" v-bind:key="image.id" >
<img :key="index" :src="getImage(index)" v-bind:alt="image" width="580" height="390" v-bind:key="index" />
</figure>
</div>
</template>
<script>
import moment from 'moment'
export default {
el: '#app',
data(){
return {
images: [
'/charts/chart1.png?rnd='+Math.random(),
'/charts/chart2.png?rnd='+Math.random(),
'/charts/chart3.png?rnd='+Math.random(),
],
id : 1,
}
},
methods: {
getImage(index) {
return this.images[index]
}
}
}
</script>
<style>
.media-post {
display: inline-block;
padding: 1vmin;
//border-radius: 2vmin;
box-shadow: 0 10px 5px rgba(0, 0, 0, 0.2);
margin: 0;
background: #FFF;
position: relative;
cursor: pointer;
}
.media-post img {
//border-radius: 1vmin;
display: block;
max-width: 100%;
height: auto;
}
.media-post figcaption {
color: #FFF;
position: absolute;
top: 0;
left: 0;
font-weight: bold;
padding: 1rem 1.5rem;
z-index: 2;
font-size: 2rem;
text-shadow: 0 1px 2px #000;
}
</style>
You just need to regenerate the random number suffix periodically. Untested:
<img
v-for="url of images"
:key="url"
:src="url + '?rnd=' + cacheKey"
/>
data() {
return {
images: [
'/charts/chart1.png',
'/charts/chart2.png',
'/charts/chart3.png',
],
cacheKey: +new Date(),
};
},
created() {
this.interval = setInterval(() => {
this.cacheKey = +new Date();
}, 60 * 1000);
},
destroyed() {
clearInterval(this.interval);
},
If it isn't clear, +new Date() returns the number of seconds since 1 January 1970 00:00:00 UTC (docs).
Related
I am attempting to conditionally render a and based on whether or not a user is signed in or not using AWS amplify and Vue 3 for the frontend. I have been able to get it to not render and then render on sign in but when I log back out the navbar elements are still there and should have disappeared. I am new to Vue, so this maybe an easy fix but am unsure. I have tried making using both computed and a watch to try and force update the computed but that is not working. Any help would be much appreciated. The code is below:
<template>
<header>
<nav class="navbar">
<router-link #click="closeMenu" to="/" class="nav-branding"
>Portal</router-link
>
<ul :class="[menuIsOpen ? 'active' : '', 'nav-menu']" v-show="isSignedIn">
<li #click="toggleMenu" class="nav-item">
<router-link to="/pensions" class="nav-link">Pensions</router-link>
</li>
<li #click="toggleMenu" class="nav-item">
<router-link to="/benefits" class="nav-link">Benefits</router-link>
</li>
<li #click="toggleMenu" class="nav-item">
<router-link to="/annual-leave" class="nav-link"
>Annual Leave</router-link
>
</li>
<li #click="signOut" class="nav-item">
<router-link to="/" class="nav-link">Sign Out</router-link>
</li>
</ul>
<div
#click="toggleMenu"
:class="[menuIsOpen ? 'active' : '', 'hamburger']"
v-show="isSignedIn"
>
<span class="bar"></span>
<span class="bar"></span>
<span class="bar"></span>
</div>
</nav>
</header>
<div :class="[menuIsOpen ? 'pushed' : 'static']"></div>
</template>
<script>
import { Auth } from "aws-amplify";
export default {
name: "NavBar",
data() {
return {
menuIsOpen: false,
};
},
methods: {
toggleMenu() {
this.menuIsOpen = !this.menuIsOpen;
},
closeMenu() {
this.menuIsOpen = false;
},
async signOut() {
try {
await Auth.signOut();
// navigate to the login page or another route
this.$router.push("/");
} catch (err) {
console.log(err);
}
},
async isUser() {
try {
await Auth.currentAuthenticatedUser();
return true;
} catch {
return false;
}
},
},
computed: {
isSignedIn() {
return this.isUser();
},
watch: {
isSignedIn() {
this.$forceUpdate();
},
},
},
};
</script>
<style>
header {
height: auto;
background-color: #0d1520;
}
li {
list-style: none;
}
a {
color: white;
text-decoration: none;
}
.navbar {
min-height: 70px;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 24px;
}
.nav-menu {
display: flex;
justify-content: space-around;
align-items: center;
gap: 60px;
}
.nav-branding {
font-size: 2rem;
color: #03e9f4;
}
.nav-branding:hover {
text-shadow: 0 0 5px #03e9f4, 0 0 25px #03e9f4, 0 0 50px #03e9f4,
0 0 100px #03e9f4;
}
.nav-link {
transition: 0.7s ease;
color: #03e9f4;
}
.nav-link:hover {
text-shadow: 0 0 5px #03e9f4, 0 0 25px #03e9f4, 0 0 50px #03e9f4,
0 0 100px #03e9f4;
}
.hamburger {
display: none;
cursor: pointer;
}
.bar {
display: block;
width: 25px;
height: 3px;
margin: 5px auto;
transition: all 0.3s ease-in-out;
background-color: #03e9f4;
}
.hamburger:hover .bar {
box-shadow: 0 0 5px #03e9f4, 0 0 25px #03e9f4, 0 0 50px #03e9f4,
0 0 100px #03e9f4;
}
#media (max-width: 768px) {
.static {
transition: all 0.5s ease-in-out;
padding-top: 0;
z-index: 0;
background-color: #151f31;
}
.pushed {
padding-top: 168px;
transition: padding 0.3s ease-in-out;
transition-delay: 0.2;
z-index: 0;
background-color: #151f31;
}
.hamburger {
display: block;
}
.hamburger.active .bar:nth-child(2) {
opacity: 0;
}
.hamburger.active .bar:nth-child(1) {
transform: translateY(8px) rotate(45deg);
}
.hamburger.active .bar:nth-child(3) {
transform: translateY(-8px) rotate(-45deg);
}
.nav-menu {
position: fixed;
left: 100%;
top: 70px;
gap: 0;
flex-direction: column;
background-color: #0d1520;
width: 100%;
text-align: center;
transition: 0.5s;
}
.nav-item {
margin: 16px 0;
}
.nav-menu.active {
z-index: 1;
left: 0;
}
}
</style>
Update
There're way too many "unknowns" in your problem thus it's difficult to give you a working answer. I'll give you hints but please read the Vue docs carefully to understand how to implement them and to also better understand the framework.
As you mentioned in the comments below, you're handling your sign in function in a "sibling" component and that both components are imported in the App.vue.
App.vue
views/
... NavBar.vue
... Login.vue
Option #1
Without the use of a state management, what you can do is move this code:
export default {
data() {
return { isSignedIn: false };
},
methods: {
async isAuthenticated() {
try {
await Auth.currentAuthenticatedUser();
this.isSignedIn = true;
} catch {
this.isSignedIn = false;
}
},
}
async mounted() {
await this.isAuthenticated();
}
}
in your App.vue, then use an emitter in the login component so that your App.vue can "listen" whenever the user has logged in successfully. You can use this.isAuthenticated() as the callback function in the emit event prop. Then, pass the this.isSignedIn state as a prop in your navbar component:
Login.vue
export default {
...
methods: {
signInUser() {
/** your sign in logic */
this.$emit('signIn')
}
}
...
}
App.vue
<!-- Sample template -->
<NavBar :show="isSignedIn" />
<Login #sign-in="isAuthenticated" />
Navbar.vue
export default {
props: ['show'] // you can then pass show in your v-show
}
Option #2
You can also conditionally render the entire navbar component. However, you need to re-structure your templates a bit:
App.vue
<Navbar v-show="isSignedIn"/>
Option #3
Ideally, App should not be responsible for managing the isSignedIn as the only components that use this state are the NavBar and Login components. With that said, consider using Pinia to manage the states between components.
I'm trying to create an image slider, which fades the images. Image 1 should fade-out at the same moment as image 2 fades in. In other words: there shouldn't be a gap between them. Right now it does nothing like fading. The code is working, as so far that when the user clicks "next", the current images disappears, 0.9s later the next image appears. There is a delay of 0.9s between them (the same amount as declared in the CSS), so somehow it recognizes the transition time. It is only not fading, after clicking the button it immediately disappears. What am I missing?
My code
<template>
<div>
<transition-group name='fade' tag='div'>
<div v-for="i in [currentIndex]" :key='i'>
<img :src="currentImg" />
</div>
</transition-group>
<a class="prev" #click="prev" href='#'>❮</a>
<a class="next" #click="next" href='#'>❯</a>
</div>
</template>
<script>
export default {
data() {
return {
images: [
'https://cdn.pixabay.com/photo/2015/12/12/15/24/amsterdam-1089646_1280.jpg',
'https://cdn.pixabay.com/photo/2016/02/17/23/03/usa-1206240_1280.jpg',
'https://cdn.pixabay.com/photo/2015/05/15/14/27/eiffel-tower-768501_1280.jpg',
'https://cdn.pixabay.com/photo/2016/12/04/19/30/berlin-cathedral-1882397_1280.jpg'
],
timer: null,
currentIndex: 0,
show: true
};
},
mounted: function () {
this.startSlide();
},
methods: {
startSlide: function () {
this.timer = setInterval(this.next, 4000);
},
next: function () {
this.currentIndex += 1;
},
prev: function () {
this.currentIndex -= 1;
},
},
computed: {
currentImg: function () {
return this.images[Math.abs(this.currentIndex) % this.images.length];
},
},
};
</script>
<style>
.fade-enter-active,
.fade-leave-active {
transition: all 0.9s ease;
overflow: hidden;
visibility: visible;
position: absolute;
width:100%;
opacity: 1;
}
.fade-enter,
.fade-leave-to {
visibility: hidden;
width:100%;
opacity: 0;
}
img {
height:600px;
width:100%
}
.prev, .next {
cursor: pointer;
position: absolute;
top: 40%;
width: auto;
padding: 16px;
color: white;
font-weight: bold;
font-size: 18px;
transition: 0.7s ease;
border-radius: 0 4px 4px 0;
text-decoration: none;
user-select: none;
}
.next {
right: 0;
}
.prev {
left: 0;
}
.prev:hover, .next:hover {
background-color: rgba(0,0,0,0.9);
}
</style>
Change .fade-enter to .fade-enter-from.
See example
The position:absolute was messing things up here... Removed this line, now it works like a charm!
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
Im trying to implement a moveable text that can also be click and call the method. However when i try to put #click its not calling the function. Can anyone help me? Thank you..
App.vue
<template>
<div id="app">
<div>
<a #click="next()">
<movable class="testmove" posTop="222" posLeft="222" shiftKey="true"
><span>Shift Key Behavior</span></movable
>
</a>
</div>
</div>
</template>
<script>
import movable from "v-movable";
export default {
name: "app",
movable,
methmethods: {
next() {
alert("Hello");
},
},
};
</script>
<style>
body {
padding: 0;
margin: 0;
font-family: Helvetica, Arial;
}
.movable {
cursor: pointer;
}
.testmove {
/* display:block;
position: absolute; */
top: 0;
height: 150px;
width: 150px;
background: #333;
color: white;
}
.modaltitle {
background: blue;
display: block;
width: 100%;
color: white;
}
</style>
Here is the code:
https://codesandbox.io/s/cold-darkness-0zm03?file=/src/App.vue
There is a typo in your code methmethods which should be corrected to methods
You can use start event for the v-movable.
#start: fires immediately after the pointerdown event on the element
Im trying insert span in placeholder, for color change. But placeholder returns only string, ow to fix that?
computed: {
customPlaceholder () {
let numLength = this.options.length;
return this.placeholder + "<span>"+numLength+"</span>"
}
}
I think you're trying to add a custom placeholder inside an input field.
to do this you need some mix of css and html.
new Vue({
el: '#editor',
data: {
input: '',
input2: 'some text'
},
computed: {
placeholderText: function () {
return `${this.input2} <span>*</span>`
}
},
methods: {
update: _.debounce(function (e) {
this.input = e.target.value
}, 300)
}
})
#editor div {
display: inline-block;
}
.input-placeholder {
position: relative;
}
.input-placeholder input {
padding: 10px;
font-size: 25px;
}
.input-placeholder input:valid + .placeholder {
display: none;
}
.placeholder {
position: absolute;
pointer-events: none;
top: 0;
bottom: 0;
height: 25px;
font-size: 25px;
left: 10px;
margin: auto;
color: #ccc;
}
.placeholder span {
color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://unpkg.com/lodash#4.16.0"></script>
<div id="editor">
<div class="input-placeholder">
<input type="text" #input="update">
<div class="placeholder" v-if="!input" v-html="placeholderText">
Email <span>*</span>
</div>
</div>
</div>
I have created this jsfiddle for my solution.
you can use css placeholder selector
input::-webkit-input-placeholder { /* Edge */
color: green!important;
}
input:-ms-input-placeholder { /* Internet Explorer 10-11 */
color: green!important;
}
input::placeholder {
color: green!important;
}
Try it, then try to remove the !important.
But information is missing. You want to change the color dynamically or not? or you want to have different colours into the same placeholder?