Table sorted date not correct - date

Expected:
I'll get a datetime string from an API returned. This value "2019-08-15T15:58:14.597Z" should be displayed in a table as `DD-MM-YYYY HH:MM.
Vuetify comes with a data table component, that can sort the data ascending and descending. I also want to use this functionality for the date, to make it sort ascending and descending.
Short question: The date from the API should be saved and displayed in another "style" in the table, but the sort-functionality uses the real date object.
My current code:
<template>
<v-content class="mt-12 ml-12">
<h1 class="font-weight-black display-3">Servers</h1>
<v-data-table
class="elevation-1"
:headers="headers"
:items="columns"
:items-per-page="15"
>
</v-data-table>
<ul id="example-1">
<li v-for="(data, index) in columns.data" :key="index">
{{ data.attributes }}
</li>
</ul>
</v-content>
</template>
<script>
import instances from '../services/instances';
export default {
data() {
return {
columns: [],
headers: [
{ text: 'Server Name', value: 'attributes.name' },
{ text: 'Spieler', value: 'attributes.playerCount' },
{ text: 'Avg. FPS', value: 'attributes.details.rust_fps_avg' },
{ text: 'Wiped', value: 'attributes.details.rust_last_wipe' },
],
};
},
async created() {
const { data } = await instances.createInstance();
this.columns = data.data;
this.columns.forEach((entry) => {
const { players, maxPlayers } = entry.attributes;
entry.attributes.playerCount = `${players} / ${maxPlayers}`;
const { rust_last_wipe } = entry.attributes.details;
const mmmm = new Date(entry.attributes.details.rust_last_wipe);
entry.attributes.details.rust_last_wipe = `${mmmm.getDate()}.${mmmm.getMonth() + 1}.${mmmm.getFullYear()}`;
});
console.log(this.columns);
},
};
</script>

In this documentation there is a way to customize the columns content
Based on it we can do:
<template>
<v-content class="mt-12 ml-12">
<h1 class="font-weight-black display-3">Servers</h1>
<v-data-table
class="elevation-1"
:headers="headers"
:items="columns"
:items-per-page="15"
>
<template v-slot:item.attributes.details.rust_last_wipe="{ item }">
<span>{{ item.attributes.details.rust_last_wipe_formatted }}</span>
</template>
</v-data-table>
<ul id="example-1">
<li v-for="(data, index) in columns.data" :key="index">
{{ data.attributes }}
</li>
</ul>
</v-content>
</template>
<script>
import instances from '../services/instances'
export default {
data() {
return {
columns: [],
headers: [
{ text: 'Server Name', value: 'attributes.name' },
{ text: 'Spieler', value: 'attributes.playerCount' },
{ text: 'Avg. FPS', value: 'attributes.details.rust_fps_avg' },
{ text: 'Wiped', value: 'attributes.details.rust_last_wipe' }
]
}
},
async created() {
const { data } = await instances.createInstance()
this.columns = data.data
this.columns.forEach(entry => {
const { players, maxPlayers } = entry.attributes
entry.attributes.playerCount = `${players} / ${maxPlayers}`
const { rust_last_wipe } = entry.attributes.details
const mmmm = new Date(entry.attributes.details.rust_last_wipe)
entry.attributes.details.rust_last_wipe_formatted = `${mmmm.getDate()}.${mmmm.getMonth() + 1}.${mmmm.getFullYear()}`
})
console.log(this.columns)
}
}
</script>

Related

How to convert axios API call to vuex action dispatch?

I'm new with Vue3JS and I am developing a todo list built from several components like that :
I need to retrieve data to display all tasks, TaskInfo component is used to display informations.
I try to convert my local component TaskInfo works well with API call (method async loadTask()) into Vuex store action/mutation, but I didn't quite understand how to process in my case, because in component TaskInfo data are always the same.
I try to load data from current taskId with :
mounted() {
this.loadTask(this.taskId);
},
But, only last item loaded, with id 26, display data loaded by getTaskinfo computed variable loaded this.$store.state.taskInfo.
Any suggestion to load component data correctly when is mouted ?
TaskInfo.vue
<template>
<div class="task-container mb-4 ml-2 rounded-lg flex flex-grow flex-col flex-nowrap items-left shadow-test border border-solid border-gray-dark">
<div class="task--labels flex p-4 border-1 border-solid border-b border-gray-dark">
<span class="font-light p-2 text-xs bg-gray-dark rounded-lg m-2"> </span>
<span class="font-light p-2 text-xs bg-gray-dark rounded-lg m-2">label 2</span>
<div class="font-bold absolute right-1">
<button
v-if="this.taskId"
#click="this.$store.dispatch('removeTask', this.taskId)" >
<svg width="24px" height="24px" viewBox="0 0 24 24" role="img" xmlns="http://www.w3.org/2000/svg" aria-labelledby="removeIconTitle" stroke="#EF5350" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none" color="#000000">
<title id="removeIconTitle">Remove</title>
<path d="M17,12 L7,12"/>
<circle cx="12" cy="12" r="10"/>
</svg>
</button>
</div>
</div>
<div
class="task--title p-4 title text-m font-normal"
:class="[ getTaskinfo.finish === true ? 'finished' : '' ]"
>
{{ getTaskinfo.title }}
</div>
<div class="task-time p-4 text-xs text text-gray1 flex items-center">
<span class="pt-1"></span>
<span class="ml-1">11h - 12h</span>
</div>
</div>
</template>
<script>
import axios from 'axios';
import { mapActions } from 'vuex'
axios.defaults.baseURL = 'http://localhost:3000';
export default {
name: 'taskInfo',
props: ['taskId'],
data() {
return {
error: '',
taskinfo: []
};
},
mounted() {
this.loadTask(this.taskId);
},
methods: {
...mapActions([
'loadTask' // map `this.incrementBy(this.taskId)` to `this.$store.dispatch('loadTask', this.taskId);`
]),
// Old API call replaced by mapActions, it's works fine.
async loadTask() {
try {
if(!this.taskId) return;
const taskInfo = await axios.get(`/task/${this.taskId}`);
if (!taskInfo.data) {
console.log('no taskInfo');
}
else {
this.taskinfo = taskInfo.data;
}
} catch (error) {
console.log('no taskInfo error');
}
},
},
computed: {
getTaskinfo() {
return this.$store.state.taskInfo;
},
}
}
</script>
Store
import { createStore } from 'vuex'
import axios from 'axios';
axios.defaults.baseURL = 'http://localhost:3000';
export default createStore( {
state: {
taskInfo: [],
error: '',
},
// Mutations have to be synchronous.
mutations: {
loadTask(state, taskInfo) {
state.taskInfo = taskInfo;
},
updateError(state, error) {
state.error = error;
},
},
actions: {
setError({ commit }, error) {
commit('updateError', error);
},
// LOAD task info.
async loadTask({ commit }, taskId) {
try {
if(!taskId) commit('updateError', 'taskId empty');
const taskInfo = await axios.get(`/task/${taskId}`);
if (!taskInfo.data) {
commit('updateError','Aucune info sur la tache');
// Set an empty task list.
commit('loadTask', []);
}
else {
console.log('task : ' + taskId);
commit('loadTask', taskInfo.data);
}
} catch (error) {
commit('updateError', error);
}
},
},
});

Re-order array items stored in a signal in Solid-js

Going through the SolidJS tutorial and playing around with the code. I'm at https://www.solidjs.com/tutorial/flow_for?solved.
Updated code below: Why doesn't clicking the button update the DOM order?
import { render } from 'solid-js/web';
import { createSignal, For } from 'solid-js';
function App() {
const [cats, setCats] = createSignal([
{ id: 'J---aiyznGQ', name: 'Keyboard Cat' },
{ id: 'z_AbfPXTKms', name: 'Maru' },
{ id: 'OUtn3pvWmpg', name: 'Henri The Existential Cat' }
]);
const rearrange = () => {
const curCats = cats();
setCats([curCats[1],curCats[2],curCats[0]]);
console.log(cats().map(cat => cat.name));
};
return (
<>
<ul>
<For each={cats()}>{(cat, i) =>
<li>
<a target="_blank" href={`https://www.youtube.com/watch?v=${cat.id}`}>
{i() + 1}: {cat.name}
</a>
</li>
}</For>
</ul>
<button onClick={rearrange}>Rearrange</button>
</>
);
}
render(() => <App />, document.getElementById('app'))
Your example works as expected, check out the link:
https://playground.solidjs.com/?hash=454601752&version=1.4.1

Why my submit form is not working in Vue3 with BalmUI

I'm trying to make a register page and my sybmit doesn't work. Does anyone know how to fix it? This is my code
<template>
<div>
<h1>Register</h1>
<ui-form
type="|"
item-margin-bottom="16"
action-align="center"
v-on:submit.prevent="register()"
>
<template #default="{ actionClass }">
<ui-form-field>
<ui-textfield
v-model="email"
required
helper-text-id="email-field-helper-text"
input-type="email"
>
Email Address
</ui-textfield>
<ui-textfield-helper
v-if="controls.helperText"
id="email-field-helper-text"
:visible="controls.isVisible"
>
Must be like me#example.com
</ui-textfield-helper>
</ui-form-field>
<ui-form-field>
<label>Password:</label>
<ui-textfield
v-model="password"
input-type="password"
required
pattern=".{8,}"
helper-text-id="pw-validation-msg"
:attrs="{ autocomplete: 'current-password' }"
>Choose password</ui-textfield
>
<ui-textfield-helper id="pw-validation-msg" visible validMsg>
Must be at least 8 characters long
</ui-textfield-helper>
</ui-form-field>
<ui-form-field :class="actionClass">
<ui-button raised nativeType="submit" type="submit">Submit</ui-button>
<ui-button outlined>Cancel</ui-button>
</ui-form-field>
</template>
</ui-form>
<div>
{{ mesaj }}
</div>
</div>
</template>
I've also tried this instead of v-on:submit.prevent="register()"
#submit.prevent="register()" or #submit.prevent.native
Also, if I put #click=register() on my button it does register all the values, even if they are not valid
I've figured it out using validators https://next-material.balmjs.com/#/data-input/validator
This is the code:
<template>
<div>
<h1>Register</h1>
<ui-form type="|" item-margin-bottom="16" action-align="center">
<template #default="{ actionClass }">
<ui-form-field>
<ui-textfield
v-model="formData.email"
required
helper-text-id="email-field-helper-text"
input-type="email"
>
Email address
</ui-textfield>
<ui-textfield-helper
v-if="controls.helperText"
id="email-field-helper-text"
:visible="controls.isVisible"
>
Must be like me#example.com
</ui-textfield-helper>
</ui-form-field>
<ui-form-field>
<label>Password:</label>
<ui-textfield
v-model="formData.password"
input-type="password"
required
pattern=".{8,}"
helper-text-id="pw-validation-msg"
:attrs="{ autocomplete: 'current-password' }"
>Choose password</ui-textfield
>
<ui-textfield-helper id="pw-validation-msg" visible validMsg>
Must be at least 8 characters long
</ui-textfield-helper>
</ui-form-field>
<ui-alert v-if="message" state="error">{{ message }}</ui-alert>
<ui-form-field :class="actionClass">
<ui-button raised #click="register()">Submit</ui-button>
<ui-button outlined>Cancel</ui-button>
</ui-form-field>
</template>
</ui-form>
<div>
{{ mesaj }}
</div>
</div>
</template>
<script>
import utils from "../utils.js";
import { useValidator, helpers } from "balm-ui";
const validations = {
email: {
label: "Email",
validator: "required, email",
email: {
validate(value) {
return /^(([^<>()\]\\.,;:\s#"]+(\.[^<>()\]\\.,;:\s#"]+)*)|(".+"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,24}))$/.test(value);
},
message: "Email address should be like me#example.com",
},
},
password: {
label: "Password",
validator: "required, password, minRule",
password: {
validate(value) {
return /^\w+$/.test(value);
},
message: "%s must be a letter, digit or underline",
},
minRule: {
validate(value) {
return value.trim().length >= 8;
},
message: "%s minLength >= 8",
},
},
};
export default {
name: "Register",
required: {
validate(value) {
return !helpers.isEmpty(value);
},
message: "%s is required",
},
data() {
return {
balmUI: useValidator(),
validations,
formData: {
email: "",
password: "",
},
mesaj: "",
message: "",
controls: {
helperText: true,
isVisible: true,
},
};
},
methods: {
register() {
let result = this.balmUI.validate(this.formData);
let { valid, message } = result;
this.message = message;
console.log(`Vrei sa te inregistrezi cu email: ${this.formData.email}`);
console.log(utils.url);
if (valid) {
let data = {
email: this.formData.email,
password: this.formData.password,
};
let requestParameters = utils.globalRequestParameters;
requestParameters.method = "POST";
requestParameters.body = JSON.stringify(data);
fetch(utils.url + "user", requestParameters).then((res) => {
res.text().then((res) => (this.mesaj = res));
});
}
},
},
};
</script>
<style></style>

Function Query.where() requires a valid third argument, but it was undefined when trying to view the page

I'm trying to implement rating system in my shopping app, but received error console when trying to open the page.
The error on console are:
Function Query.where() requires a valid third argument, but it was undefined. - it points to:
this.stars = this.starService.getProductStars(this.movieId)
AND
const starsRef = this.afs.collection('stars', ref => ref.where('movieId', '==', movieId));
Below is my code:
rating.page.html: (where I put my 2 of components which are TestRate and Star-Review)
<ion-content>
<app-testrate></app-testrate>
<app-star-review-component></app-star-review-component>
</ion-content>
testrate.component.html:
<div *ngIf="movie | async as m">
<h1>
{{m.title}}
</h1>
<img [src]="m.image" width="100px">
<p>
{{m.plot}}
</p>
<star-review [movieId]="movieId" [userId]="userId"></star-review>
</div>
testrate.component.ts:
export class TestrateComponent implements OnInit {
userDoc: AngularFirestoreDocument<any>;
movieDoc: AngularFirestoreDocument<any>;
user: Observable<any>;
movie: Observable<any>;
constructor(private afs: AngularFirestore) { }
ngOnInit() {
this.userDoc = this.afs.doc('users/test-user-3')
this.movieDoc = this.afs.doc('movies/battlefield-earth')
this.movie = this.movieDoc.valueChanges()
this.user = this.userDoc.valueChanges()
}
get movieId() {
return this.movieDoc.ref.id
}
get userId() {
return this.userDoc.ref.id
}
}
star-review.component.html:
<h3>Average Rating</h3>
{{ avgRating | async }}
<h3>Reviews</h3>
<div *ngFor="let star of stars | async">
{{ star.userId }} gave {{ star.movieId }} {{ star.value }} stars
</div>
<h3>Post your Review</h3>
<fieldset class="rating">
<ng-container *ngFor="let num of [5, 4, 3, 2, 1]">
full star
<input (click)="starHandler(num)"
[id]="'star'+num"
[value]="num-0.5"
name="rating"
type="radio" />
<label class="full" [for]="'star'+num"></label>
half star
<input (click)="starHandler(num-0.5)"
[value]="num-0.5"
[id]="'halfstar'+num"
name="rating"
type="radio" />
<label class="half" [for]="'halfstar'+num"></label>
</ng-container>
</fieldset>
star-review.component.ts:
export class StarReviewComponentComponent implements OnInit {
#Input() userId;
#Input() movieId;
stars: Observable<any>;
avgRating: Observable<any>;
constructor(private starService: StarService) { }
ngOnInit() {
this.stars = this.starService.getProductStars(this.movieId)
this.avgRating = this.stars.pipe(map(arr => {
const ratings = arr.map(v => v.value)
return ratings.length ? ratings.reduce((total, val) => total + val) / arr.length : 'not reviewed'
}))
}
starHandler(value) {
this.starService.setStar(this.userId, this.movieId, value)
}
}
star.service.ts:
export class StarService {
constructor(private afs: AngularFirestore) { }
// Star reviews that belong to a user
getUserStars(userId) {
const starsRef = this.afs.collection('stars', ref => ref.where('userId', '==', userId));
return starsRef.valueChanges();
}
// Get all stars that belog to a Product
getProductStars(movieId) {
const starsRef = this.afs.collection('stars', ref => ref.where('movieId', '==', movieId));
return starsRef.valueChanges();
}
// Create or update star
setStar(userId, movieId, value) {
// Star document data
const star: Star = { userId, movieId, value };
// Custom doc ID for relationship
const starPath = `stars/${star.userId}_${star.movieId}`;
// Set the data, return the promise
return this.afs.doc(starPath).set(star)
}
}

How to use setFieldValue and pass the value as props between components

I'm trying to use ant design forms in my sample registration form, when i'm trying to use setFieldsValue it throws error as "Cannot use setFieldsValue unless getFieldDecorator is used". But I had already used getFieldDecorator in my code.Here is a sample of my code.
handleChange = (e) => {
const fname = e.target.name;
const fvalue = e.target.value;
this.props.setFieldsValue({
fname: fvalue
});
}
render(){
const { getFieldDecorator } = this.props.form
return (
<Row gutter={4}>
<Col className="reg-personal-details-grid-column" span={24}>
<FormItem {...formItemLayout} label="First Name">
{getFieldDecorator("firstName", {
rules: [
{
required: true
}
]
})(
<Input
placeholder="First Name"
required
name="firstName"
onChange={this.handleChange}
/>
)}
</FormItem>
</Col>
</Row>
)
}
fname is not defined in getFieldDecorator.
You should do this:
handleChange = (e) => {
const fname = e.target.name;
const fvalue = e.target.value;
this.props.form.setFieldsValue({
[fname]: fvalue
});
}
You can create your own function to do this:
export const setFieldValue = (
form: FormInstance,
name: NamePath,
value: string
): void => {
if (form && form.getFieldValue(name)) {
const fixname: string[] = [];
if (typeof name == 'object') {
name.forEach((node: string) => {
fixname.push(node);
});
} else {
fixname.push(String(name));
}
let fieldsValue: unknown;
fixname.reverse().forEach((node: string) => {
fieldsValue = {
[String(node)]: fieldsValue != undefined ? fieldsValue : value,
};
});
form.setFieldsValue(fieldsValue);
}
};
and you can use like that
setFieldValue(form, 'phone', '1111111111');
or
setFieldValue(form, ['phones', 'mobile'], '2222222222');
In Antd v4, you only required to call the setFieldsValue in useEffect function.
const [form] = Form.useForm()
useEffect(() => {
//companyInfo -> dynamic obj fetched from API
form.setFieldsValue(companyInfo);
}, [companyInfo, form]);
Then in Form use the form prop as follow:
<Form
//passing form prop
form={form}
labelCol={{ span: 8 }}
wrapperCol={{ span: 14 }}
onFinish={onFinish}
labelAlign="left"
colon={false}
>
you can use it like this
const formRef = useRef(null);
useEffect(() => {
formRef.current?.setFieldsValue({
name: data?.firstName,
});
}, [data]);
return (
<Form ref={formRef}>
<Form.Item name="name">
<Input/>
</Form.Item>