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
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);
}
},
},
});
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
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>
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)
}
}
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>