Add dynamic date format to Momen.js in Vue - vue.js

I need to display the date format depending on saved settings. I call axios to check saved date format:
axios
.get('/api/user/')
.then(response => (this.date_format = response.data.dateformat))
Then I added filters:
filters: {
moment: function (date) {
return moment(date).format(this.date_format);
}
},
And display it in HTML:
<p class="card-title"> Date of Birth:
<span[[dob | moment]]</span>
</p>
If I display [[data_format]] it shows correctly "DD/MM/YYYY". But when I try to console.log(this.date_format) console shows Undefined.
How to replace FormatNeeded in return moment(date).format(FormatNeeded); with this.date_format?

I finally found out how to solve the problem. When I call API, I set date_format to a global variable (probably not the correct term):
axios
.get('/api/user/')
.then(response => (this.date_format = response.data.dateformat))
.then(response => (window.date_format = this.date_format))
Then in filters, I use window.date_format not this.date_format:
filters: {
moment(date) {
var format_elem = window.date_format;
return moment(date).format(format_elem);
}
}

Related

vuejs dynamically adding class

Using vuejs 3. In the vuejs app, I have:
data(){
return{
return_prohibited:false
}
}
return_prohibited turns to true when the server returns an error message from a fetch request:
fetch(myUrl,this.myInit)
.then(response => response.json())
.then(data => {
if (data.message) {
this.credits = []
this.debits = []
return_prohibited = true
} // cut for brievity
Html file:
<button #click="previousMonth" id="bouton_mois_prec" :class="{interdit:return_prohibited}" >précédent</button>
I was expecting that the css class interdit would be added to the button each time that return_probibited is true, as per these explanations. But nothing happens.
You should append this. in front of return_prohibited - otherwise you will get errors in the console.

How can I put a data inside the <template> in a component with Vue

I am trying to do a pagination but I can not put the dynamic total I am doing like this:
<v-pagination v-model="currentPage"
:page-count="total"
:classes="bootstrapPaginationClasses"
:labels="paginationAnchorTexts"
></v-pagination>
How you can see the total os in the :page-count, it is a dynamic total because I am getting data from database, my vue code is this one:
<script>
import vPagination from 'vue-plain-pagination';
export default {
created() {
this.getPosts();
},
methods: {
getPosts() {
fetch('/api/bank')
.then(response => response.json() )
.then(json => {
this.posts = json.data.data;
this.total = json.data.last_page;
this.current_page = json.data.current_page;
});
}
},
components: { vPagination },
data: function() {
return {
postsSelected: "",
posts: [],
currentPage: 1,
total: this.total,
bootstrapPaginationClasses: {
ul: 'pagination',
li: 'page-item',
liActive: 'active',
liDisable: 'disabled',
button: 'page-link'
},
paginationAnchorTexts: {
first: 'Primera',
prev: '«',
next: '»',
last: 'Última'
}
}
}
}
</script>
How you can see I am using fetch to get the data from database and then I am split it in different information like total and the I am using this information inside the data: function() {}.
How you can tell total it's like this: total: this.total because I want to get the total number but when I do that I am getting this error:
[Vue warn]: Invalid prop: type check failed for prop "pageCount". Expected Number with value NaN, got Undefined
and I think that it is because:
total: this.total in the data function() {} is bad or:
how can I put the dynamic variable total inside the
How could I fix it?
Thanks!
If you want to know the data retrieved from the API, you can console log the data returned like this:
getPosts() {
fetch('/api/bank')
.then(response => response.json() )
.then(json => {
console.log(json.data)
this.posts = json.data.data;
this.total = json.data.last_page;
this.current_page = json.data.current_page;
});
}
Also, you should not have data attribute and props attribute with the same name! So change the total data attribute to another name and initialize it with a value of 0 instead.
In fact, you don't need to care about passing the Prop total at all as your method getPosts is not dependent on the Prop! So you may just have total: 0 in data and that should fix your issues

How to create custom start/end date Vue custom validator

I am trying to create a custom Vue validator. I have reviewed their docs https://vuelidate.netlify.com/#custom-validators, as well as a very useful tutorial https://vuejsdevelopers.com/2018/08/27/vue-js-form-handling-vuelidate/.
However, I still don't see a clear example of how to do the following:
I have two datepicker input fields, a start and end date. I want to be able to create a validator which can
Check both dates in tandum to make sure that the end date is not before the start date
Have a single validation message based on this (aka: we don't want one field with 'Start date can't be before end date' and the other with 'End date can't be before start date')
This type of functionality (or using other fields values inside a different one) is basically what the core sameAs validator (see below) has:
import { ref, withParams } from './common'
export default (equalTo) =>
withParams({ type: 'sameAs', eq: equalTo }, function(value, parentVm) {
return value === ref(equalTo, this, parentVm)
})
I have tried to mimic this, but its not working...
import { ref, withParams } from 'vuelidate/lib/validators/common.js'
export default (endDate) =>
withParams({ type: 'dateRange', eq: endDate }, function(value, parentVm) {
console.log('parentVm', parentVm);
return value < ref(endDate, this, parentVm)
})
Its not even logging my console.log. Here is the code calling it
<date-picker id="financial-start-date" v-model="$v.start_date.$model" :config="datepickerConfig"></date-picker>
<date-picker id="financial-end-date" v-model="$v.end_date.$model" :config="datepickerConfig"></date-picker>
Validations:
validations: {
transaction_id: {
},
start_date: {
},
end_date: {
dateRange: dateRange('startDate')
}
},
Can be solved using the following code:
first create custom validator:
const isAfterDate = (value, vm) => {
return new Date(value).getTime() > new Date(vm.startDate).getTime();
};
Second, call the validator within validations:
endDate: {
required,
isAfterDate
}

How to build a VUE link in a method using vue-router

I'm new using VUE.JS and I'm in love with it! I love the vue-router and router-link! They are awesome!
Now I have a table populated by data coming from axios and I would like to build a link using this data in a custom method to have the team name clickable.
Here the template:
<BootstrapTable :columns="table.columns" :data="table.data" :options="table.options"></BootstrapTable>
Axios returns ID, name and other data used to update the table as here
Basically, I need to update the values in my table using the axios's received data. Something like:
team: '<a v-bind:href="club/'+team.id+'">'+team.team+'</a>',
or
team: '<router-link :to="club/'+team.id+'">'+team.team+'</router-link>',
But obviously it dosn't works...
How can a build a link?
I fixed it using custom column event and formatter in columns table setting:
{
field: 'match',
title: 'Match',
formatter (value, row) {
return `${value}`
},
events: {
'click a': (e, value, row, index) => {
e.preventDefault();
this.$router.push(`/matches/${row.pos}`)
}
}
},
Another solution:
Just in case of JSON code having links instead of table config is adding click listener in mounted() and a well formatted dataset in JSON HTML link:
team: "<a href=\"/club/"+team.id+"\" data-to='{\"name\": \"team\",\"params\":{\"teamId\":"+ team.id+"}}'>"+ team.team+"</a> "+userCode
Here the listener:
mounted() {
window.addEventListener('click', event => {
let target = event.target;
if (target && target.href && target.dataset.to) {
event.preventDefault();
const url = JSON.parse(target.dataset.to);
//router.push({ name: 'user', params: { userId: '123' } })
this.$router.push(url);
}
});
}
This might be shorter solution for your issue :
routes = [
{
component : 'club',
name : 'club',
path : '/club/:teamid'
}
]
<a #click="$router.push({ name: 'club', params: { teamid: team.id}})">team.team</a>

Vue JS fire a method based on another method's output unique ID

I'm trying to render a list of notes and in that list I would like to include the note's user name based on the user_id stored in the note's table. I have something like this, but at the moment it is logging an error stating Cannot read property 'user_id' of undefined, which I get why.
My question is, in Vue how can something like this be executed?
Template:
<div v-for="note in notes">
<h2>{{note.title}}</h2>
<em>{{user.name}}</em>
</div>
Scripts:
methods:{
fetchNotes(id){
return this.$http.get('http://api/notes/' + id )
.then(function(response){
this.notes = response.body;
});
},
fetchUser(id){
return this.$http.get('http://api/user/' + id )
.then(function(response){
this.user = response.body;
});
}
},
created: function(){
this.fetchNotes(this.$route.params.id)
.then( () => {
this.fetchUser(this.note.user_id);
});
}
UPDATE:
I modified my code to look like the below example, and I'm getting better results, but not 100% yet. With this code, it works the first time it renders the view, if I navigate outside this component and then back in, it then fails...same thing if I refresh the page.
The error I am getting is: [Vue warn]: Error in render: "TypeError: Cannot read property 'user_name' of undefined"
Notice the console.log... it the returns the object as expected every time, but as I mentioned if refresh the page or navigate past and then back to this component, I get the error plus the correct log.
Template:
<div v-for="note in notes">
<h2>{{note.title}}</h2>
<em>{{note.user.user_name}}</em>
</div>
Scripts:
methods:{
fetchNotes(id){
return this.$http.get('http://api/notes/' + id )
.then(function(response){
this.notes = response.body;
for( let i = 0; i < response.body.length; i++ ) {
let uId = response.body[i].user_id,
uNote = this.notes[i];
this.$http.get('http://api/users/' + uId)
.then(function(response){
uNote.user = response.body;
console.log(uNote);
});
}
});
},
}
It looks like you're trying to show the username of each note's associated user, while the username comes from a different data source/endpoint than that of the notes.
One way to do that:
Fetch the notes
Fetch the user info based on each note's user ID
Join the two datasets into the notes array that your view is iterating, exposing a user property on each note object in the array.
Example code:
let _notes;
this.fetchNotes()
.then(notes => this.fetchUsers(notes))
.then(notes => _notes = notes)
.then(users => this.joinUserNotes(users, _notes))
.then(result => this.notes = result);
Your view template would look like this:
<div v-for="note in notes">
<h2>{{note.title}}</h2>
<em>{{note.user.name}}</em>
</div>
demo w/axios
UPDATE Based on the code you shared with me, it looks like my original demo code (which uses axios) might've misled you into a bug. The axios library returns the HTTP response in a data field, but the vue-resource library you use returns the HTTP response in a body field. Attempting to copy my demo code without updating to use the correct field would cause the null errors you were seeing.
When I commented that axios made no difference here, I was referring to the logic shown in the example code above, which would apply to either library, given the field names are abstracted in the fetchNotes() and fetchUsers().
Here's the updated demo: demo w/vue-resource.
Specifically, you should update your code as indicated in this snippet:
fetchInvoices(id) {
return this.$http.get('http://localhost/php-api/public/api/invoices/' + id)
// .then(invoices => invoices.data); // DON'T DO THIS!
.then(invoices => invoices.body); // DO THIS: `.data` should be `.body`
},
fetchCustomers(invoices) {
// ...
return Promise.all(
uCustIds.map(id => this.$http.get('http://localhost/php-api/public/api/customers/' + id))
)
// .then(customers => customers.map(customer => customer.data)); // DON'T DO THIS!
.then(customers => customers.map(customer => customer.body)); // DO THIS: `.data` should be `.body`
},
Tony,
Thank you for all your help and effort dude! Ultimately, with the help from someone in the Vue forum, this worked for me. In addition I wanted to learn how to add additional http requests besides the just the user in the fetchNotes method - in this example also the image request. And this works for me.
Template:
<div v-if="notes.length > 0">
<div v-if="loaded === true">
<div v-for="note in notes">
<h2>{{note.title}}</h2>
<em>{{note.user.user_name}}</em>
<img :src="note.image.url" />
</div>
</div>
<div v-else>Something....</div>
</div>
<div v-else>Something....</div>
Script:
name: 'invoices',
data () {
return {
invoices: [],
loaded: false,
}
},
methods: {
fetchNotes: async function (id){
try{
let notes = (await this.$http.get('http://api/notes/' + id )).body
for (let i = 0; notes.length; i++) {
notes[i].user = (await this.$http.get('http://api/user/' + notes[i].user_id)).body
notes[i].image = (await this.$http.get('http://api/image/' + notes[i].image_id)).body
}
this.notes = this.notes.concat(notes)
}catch (error) {
}finally{
this.loaded = true;
}
}