I have this html:
<template>
<div>
<div class="card text-center">
<h2><strong>Title: </strong>{{story.title}}</h2>
<p><strong>Score: </strong>{{story.score}}</p>
<p><strong>Author: </strong>{{story.by}}</p>
<p><strong>Date: </strong>{{ formatDate(story.time) }}</p>
<!--<p><strong>Date: </strong>{{ dateFormat(new Date(story.time*1000),v) }}</p> -->
<NuxtLink :to="`/${story.id}`">
<p class="btn my-4">View details</p> </NuxtLink>
</div>
</div>
</template>
and this scripts:
<script setup>
const { story } = defineProps(['story'])
export default {
data () {
return {
time: 0,
}
},
methods: {
formatDate(time) {
date = new Date(time).format("YYYY/MM/DD")
return date
}
}
}
</script>
I am not getting this kinda date format: "YYYY/MM/DD".
Please help how can I do that in Nuxt.js?
AFAIK, there isn't a single function call to achieve that. I would suggest you re-writing your formatDate using Date.toLocaleString to have full control of your format.
In addition, do not use export default in your <script setup>. It's not a valid syntax.
<script setup>
// Your other setup
function formatDate(time) {
const date = new Date(time);
// Get year, month, and day part from the date
const year = date.toLocaleString("default", { year: "numeric" });
const month = date.toLocaleString("default", { month: "2-digit" });
const day = date.toLocaleString("default", { day: "2-digit" });
return `${year}/${month}/${day}`;
}
</script>
You could use dayjs (tested with Nuxt 2.15)
yarn add #nuxtjs/dayjs
export default {
// ...
modules: [
'#nuxtjs/dayjs'
],
<template>
<div>
{{ $dayjs(new Date()).format('YYYY/MM/DD') }}
</div>
</template>
Related
I am new to the version of Vue that introduced the "script setup" tag. I would like to trigger a watcher to update the season property and then calculate a new value based on that input. You can see it happening here:
watch(season, () => {
getStandings(season)
}, { immediate: true })
I am trying to trigger the watcher from within a function that is triggered by a child component emitting an event with data. You can see it here:
// in the template
<SeasonSelect :season="season" #changedOption="onChangeSelection"/>
// and then in the script
function onChangeSelection(selection: number) {
console.log(selection, 8);
this.season = selection; // i tried this, this is what I expected to work
}
What I want is for the selection parameter to make it into the watcher. If it goes there, the watcher should take care of the computation as I intend.
Please tell me directly how to handle this. There isn't a lot of findable info about how to handle the special case where "script setup" is used.
Here is the full code for this component, which is the main component called index.vue:
<script setup lang="ts">
import { useDPC } from '#/composables/useDPC';
import { CONFIGS } from '#/configs';
import SeasonSelect from '../components/SeasonSelect.vue';
function onChangeSelection(selection: number) {
console.log(selection, 8);
this.season = selection;
}
const season = $ref<number>(CONFIGS.SEASONS.THE_INTERNATIONAL_11)
const { standings, getStandings, isLoading } = useDPC()
watch(season, () => {
getStandings(season)
}, { immediate: true })
</script>
<template>
<div>
<SeasonSelect :season="season" #changedOption="onChangeSelection"/>
<h1>DPC Standings for {{ season }} season</h1>
<h2 v-if="isLoading">
Loading...
</h2>
<ul v-else>
<div v-for="team in standings" :key="team.team_id">
<Team :team="team"/>
</div>
</ul>
</div>
</template>
edit: please note that the official documentation page about "script setup" doesn't mention how to implement a watcher or update a watcher, and everything is different in a "script setup" file
edit2: Per request of Boussadjra Brahim here is the code for SeasonSelect
<script>
import { CONFIGS } from '../configs';
console.log(CONFIGS, 4);
export default {
data() {
return {
seasons: Object.assign({},CONFIGS.SEASONS)
}
},
props: {
season: Number
},
methods: {
onChange(event) {
console.log(event.target.value, 16)
this.$emit("changedOption", event.target.value);
}
}
}
</script>
<template>
<select v-model="season" name="season" #change="onChange($event)">
<option v-for="(value, key) in seasons" :key="key" :value="value">
{{ key }}
</option>
</select>
</template>
Third edit: Like yo even this detailed breakdown doesnt mention anything about using watched properties in the "script setup" version of a SFC
When you are using <script setup>, it does not mean that you don't need to import necessary stuffs to your codes. You did not import watch in your parent component that uses <script setup>. Also this.season is not correct in <script setup>. Here is a basic example on how to manage your <select> operation in a <script setup> way:
parent component:
<template>
<div>
<SeasonSelect :season="season" #changedOption="onChangeSelection"/>
<h1>DPC Standings for {{ season }} season</h1>
<!-- <h2 v-if="isLoading">-->
<!-- Loading...-->
<!-- </h2>-->
<!-- <ul v-else>-->
<!-- <div v-for="team in standings" :key="team.team_id">-->
<!-- <Team :team="team"/>-->
<!-- </div>-->
<!-- </ul>-->
</div>
</template>
<script setup>
// import { useDPC } from '#/composables/useDPC';
// import { CONFIGS } from '#/configs';
// -------------------------------------------
/* importing things that are necessary from vue */
// -------------------------------------------
import {watch, ref} from "vue";
import SeasonSelect from '../components/SeasonSelect.vue';
const season = ref("value1");
function onChangeSelection(selection) {
console.log(selection, 8);
season.value = selection;
}
// const season = $ref<number>(CONFIGS.SEASONS.THE_INTERNATIONAL_11)
// const { standings, getStandings, isLoading } = useDPC()
watch(season, () => {
console.log("season watch")
})
</script>
<style scoped>
</style>
child component:
<template>
<select v-model="season" name="season" #change="onChange($event)">
<option v-for="(value, key) in seasons" :key="key" :value="value">
{{ key }}
</option>
</select>
</template>
<script>
// import { CONFIGS } from '../configs';
// console.log(CONFIGS, 4);
export default {
name: "SeasonSelect",
data() {
return {
/* I used a simple object to demonstrate the way you can implement this code. */
seasons: {
key1: "value1",
key2: "value2",
key3: "value3"
}
}
},
props: {
season: String
},
methods: {
onChange(event) {
console.log(event.target.value, 16)
this.$emit("changedOption", event.target.value);
}
}
}
</script>
<style scoped>
</style>
I also removed some features like using lang="ts", because they are not related to your question here.
I need to create a countdown for the expiration date in the VUE 3 application. I need the format of YYYY/MM/DD. my search results were using Moment.js.
After installation using:
npm i moment
I could not figure out the right way to use it.
My Code :
<template>
<p> Remaining time: {{moment(moment(30-9-2022) - moment(new Date())}}
</p>
</template>
<script>
import moment from "moment";
export default {
methods:{
moment,
}
}
</script>
const { createApp, reactive, onMounted } = Vue
const app = createApp({
data() {
const remaining = reactive({})
onMounted(() => {
setInterval(() => {
setTime()
}, 1000)
const eventTime = moment('2023.05.04 10:03:00', 'YYYY.MM.DD HH:mm:ss')
function setTime() {
let currenTime = moment()
remaining.days = moment.duration(eventTime.diff(currenTime)).asDays()
remaining.hours = moment.duration(eventTime.diff(currenTime)).get('hours')
remaining.minutes = moment.duration(eventTime.diff(currenTime)).get('minutes')
remaining.seconds = moment.duration(eventTime.diff(currenTime)).get('seconds')
}
})
return {
remaining
}
}
})
app.mount('#app')
<script src="https://unpkg.com/vue#3/dist/vue.global.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.4/moment.min.js"></script>
<div id="app">
<div v-if="Object.keys(remaining).length > 0">
{{ remaining.days.toFixed(0) }} :
{{ remaining.hours.toFixed(0) }} :
{{ remaining.minutes.toFixed(0) }} :
{{ remaining.seconds.toFixed(0) }}
</div>
</div>
I could not find the right usage for this particular problem through my research, so I thought it would be useful to publish it.
The Right Syntax :
<template>
<p>
{{ getRmainTime() }}
</p>
</template>
<script>
import moment from "moment";
export default {
methods:{
getRmainTimeToExp() {
return `${moment
.duration(moment(this.user.exp_time) - moment(new
Date())).years()} years,
${moment.duration(moment(this.user.exp_time) -
moment(new Date())).months()} months,
${moment.duration(moment(this.user.exp_time) -
moment(new Date())).days()} days`;
},
}
}
</script>
```
I hope it is okay that I included my full code. Otherwise it would be difficult to understand my question.
I have made a composable function for my Vue application, which purpose is to fetch a collection of documents from a database.
The composable looks like this:
import { ref, watchEffect } from 'vue'
import { projectFirestore } from '../firebase/config'
const getCollection = (collection, query) => {
const documents = ref(null)
const error = ref(null)
let collectionRef = projectFirestore.collection(collection)
.orderBy('createdAt')
if (query) {
collectionRef = collectionRef.where(...query)
}
const unsub = collectionRef.onSnapshot(snap => {
let results = []
snap.docs.forEach(doc => {
doc.data().createdAt && results.push({ ...doc.data(), id: doc.id })
})
documents.value = results
error.value = null
}, (err) => {
console.log(err.message)
document.value = null
error.value = 'could not fetch data'
})
watchEffect((onInvalidate) =>{
onInvalidate(() => unsub());
});
return {
documents,
error
}
}
export default getCollection
Then I have a component where I store the data from the database
<template>
<div v-for="playlist in playlists" :key="playlist.id">
<div class="single">
<div class="thumbnail">
<img :src="playlist.coverUrl">
</div>
<div class="info">
<h3>{{ playlist.title }}</h3>
<p>created by {{ playlist.userName }}</p>
</div>
<div class="song-number">
<p>{{ playlist.songs.length }} songs</p>
</div>
</div>
</div>
</template>
<script>
export default {
// receiving props
props: ['playlists'],
}
</script>
And finally, I output the data inside the main Home component, where I use the documents and error reference from the composable file.
<template>
<div class="home">
<div v-if="error" class="error">Could not fetch the data</div>
<div v-if="documents">
<ListView :playlists="documents" />
</div>
</div>
</template>
<script>
import ListView from '../components/ListView.vue'
import getCollection from '../composables/getCollection'
export default {
name: 'Home',
components: { ListView },
setup() {
const { error, documents } = getCollection('playlists')
return { error, documents }
}
}
</script>
That is all well and good.
But now I wish to add data from a second collection called "books", and the idea is to use the same composable to fetch the data from that collection as well,
but the problem is that inside the Home component, I cannot use the references twice.
I cannot write:
<template>
<div class="home">
<div v-if="error" class="error">Could not fetch the data</div>
<div v-if="documents">
<ListView :playlists="documents" />
<ListView2 :books="documents" />
</div>
</div>
</template>
export default {
name: 'Home',
components: { ListView, ListView2 },
setup() {
const { error, documents } = getCollection('playlists')
const { error, documents } = getCollection('books')
return { error, documents }
}
}
This will give me an error because I reference documents and error twice.
So what I tried was to nest these inside the components themselves
Example:
<template>
<div v-for="playlist in playlists" :key="playlist.id">
<div class="single">
<div class="thumbnail">
<img :src="playlist.coverUrl">
</div>
<div class="title">
{{ playlist.title }}
</div>
<div class="description">
{{ playlist.description }}
</div>
<div>
<router-link :to="{ name: 'PlaylistDetails', params: { id: playlist.id }}">Edit</router-link>
</div>
</div>
</div>
</template>
<script>
import getCollection from '../composables/getCollection'
export default {
setup() {
const { documents, error } = getCollection('playlists')
return {
documents,
error
}
}
}
</script>
This does not work either.
I will just get a 404 error if I try to view this component.
So what is the correct way of writing this?
Try out to rename the destructed fields like :
const { error : playlistsError, documents : playlists } = getCollection('playlists')
const { error : booksError, documents : books } = getCollection('books')
return { playlistsError, playlists , booksError , books }
I'm not sure what I'm doing wrong here. Can anyone point out my mistake?
dateMixin.js
`
import moment from 'moment'
export const dateMixin = {
methods: {
convertDate (date, format) {
return moment(date, 'YYYY-MM-DD').format(format)
}
}
}
`
component.vue
here date = 2019-06-11T15:28:07.226938-07:00
`
<template>
<div>
{{ convertDate(date, 'MM/DD/YYYY hh:mm A') }}
</div>
</template>
<script>
import { dateMixin } from '../mixins/dateMixin.js'
export default {
mixins: [dateMixin]
}
</script>
`
Current output: 06/11/2019 12:00 AM
Expected output: 06/11/2019 03:28 PM
Thanks!
You hard-coded the parsing format in your mixin ('YYYY-MM-DD'), on this line:
return moment(date, 'YYYY-MM-DD').format(format)
Which reads the first part of your date string (2019-06-11T15:28:07.226938-07:00) as YYYY-MM-DD and disregards the rest. If you remove the parsing format string and allow moment to read the entire date, i.e:
return moment(date).format(format)
...it will output correctly. See it working:
Vue.config.productionTip = false;
Vue.config.devtools = false;
new Vue({
el: '#hook',
template: '#appTemplate',
data: () => ({
date: '2019-06-11T15:28:07.226938-07:00'
}),
computed: {
getTimezone() {
return moment.tz.guess();
}
},
methods: {
convertDate(date, format) {
return moment(this.date).format(format)
},
convertToUtc(date, format) {
return moment(this.date).utc().format(format)
}
}
})
dl { font-family: monospace; }
dd { margin-bottom: 1rem; }
dt { font-weight: bold; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.25/moment-timezone-with-data.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script type="text/template" id="appTemplate">
<div id="app">
<dl>
<dt v-text="getTimezone"></dt>
<dd v-text="convertDate(date, 'MM/DD/YYYY hh:mm A')"></dd>
<dt v-text="`UTC`"></dt>
<dd v-text="convertToUtc(date, 'MM/DD/YYYY hh:mm A')"></dd>
</dl>
</div>
</script>
<div id="hook"></div>
Otherwise it will read only the date, and default the time to 00:00:00.000000 (which is why you're seeing 12:00 AM).
Currently I am using as
<template> ... </template>
<script src="test1.js"> </script>
Where all my business logics are written in test1.js. Now I need to use test2.js with same template. I need to reuse the template with different component.
My current code goes like this...
common.vue
<template>
<div>
{{ page }}
</div>
<template>
<script src="../scripts/test1.js"></script>
Where in test1.js
export default {
name: 'home',
components: {
test: test
},
data() {
return {
page: "test1",
}
}
}
But also i need to use common.vue in my test2.js. Which i am not able to import both the .js seperately.
In angular we can write the fallowing late bindig which binds .html and .js(in ui.router)
.state('home', {
url:'/',
templateUrl: 'templates/home.html',
controller: 'HomeController'
})
Is there something similar to this?
You can share the template through the id attribute.
Vue.component('my-comp1', {
template: '#generic',
data () {
return {
text: 'I am component 1'
}
}
})
Vue.component('my-comp2', {
template: '#generic',
data () {
return {
text: 'I am component 2'
}
}
})
new Vue({
el: '#app'
})
<div id="app">
<my-comp1></my-comp1>
<my-comp2></my-comp2>
</div>
<template id="generic">
<div>
<p>{{ text }}</p>
</div>
</template>
<script src="https://unpkg.com/vue"></script>