When I try assigning a moment to a new variable it changes the value without me modifying it.
I have tried everything from forcing the use of UTC and settings timezones. It just keeps changing the value.
#Component({
selector: 'app-appointment-create',
templateUrl: './appointment-create.component.html',
styleUrls: ['./appointment-create.component.css']
})
export class AppointmentCreateComponent implements OnInit {
appointment: CreateAppointmentDto;
form: FormGroup;
private formSubmitted: boolean;
tasks: Task[];
availability: Availability[];
task: number;
availablemoments: Moment[];
constructor(
private titleService: Title,
private router: Router,
private appointmentService: AppointmentService,
private taskService: TasksService,
private webLogger: WebloggerService,
private availabilityService: AvailabilityService,
) {
this.appointment = new CreateAppointmentDto();
}
dateFilter = (d: Moment): boolean => {
return this.availability.filter(s => s.timestampstart.isSame(d, 'day')).length >= 1;
}
ngOnInit() {
this.titleService.setTitle('New Appointment');
this.taskService.getActiveTasks().subscribe(value => {this.tasks = value; });
this.form = new FormGroup({
timestampstart: new FormControl(this.appointment.timestampstart, Validators.required),
daystart: new FormControl(this.appointment.timestampstart, Validators.required),
location: new FormControl(this.appointment.location, Validators.required),
description: new FormControl(this.appointment.description, Validators.required),
paid: new FormControl(false, Validators.required),
serviceType: new FormControl(this.appointment.serviceTypeId, Validators.required),
client: new FormControl(this.appointment.clientId, Validators.required),
assignedUser: new FormControl(this.appointment.assignedUserId, Validators.required),
});
}
onSubmit(event) {
event.preventDefault();
this.formSubmitted = true;
if (this.form.valid) {
this.form.disable();
this.appointment.timestampstart = this.form.get('timestampstart').value;
this.appointment.location = this.form.get('location').value;
this.appointment.description = this.form.get('description').value;
this.appointment.paid = this.form.get('paid').value;
this.appointment.serviceTypeId = this.form.get('serviceType').value;
this.appointment.clientId = this.form.get('client').value;
this.appointment.assignedUserId = this.form.get('assignedUser').value;
this.appointmentService.createNewAppointment(this.appointment)
.subscribe(value => { this.router.navigate([`/dashboard/appointment/${value.id}/edit`]); });
} else {
this.webLogger.error('The form is invalid, please check the values');
}
}
selectTask($event: Event) {
this.task = Number(this.form.get('serviceType').value);
this.availabilityService.getAvailabilityForTask(this.task).subscribe(value => {
this.availability = value;
});
}
setTime($event: Event) {
this.availablemoments = [];
const dayAvailability: Availability[] = this.availability.filter(
s => s.timestampstart.isSame(moment(this.form.get('daystart').value), 'day'));
const currentDate = dayAvailability.reduce((prev, curr) => prev.timestampstart < curr.timestampstart ? prev : curr).timestampstart;
dayAvailability.forEach(value => {
while (value.timestampend.isAfter(currentDate)) {
if (!this.availablemoments.includes(moment(currentDate))) {
this.availablemoments.push(moment(currentDate));
}
currentDate.add(30, 'minutes');
}
});
}
}
this.availability is a list of Availability objects which include start and end moments
I expect the second console log to return the same as the first console log.
UPDATE:
The Availability class looks like this:
export class Availability {
id: number;
timestampstart: Moment;
timestampend: Moment;
location: string;
description: string;
paid: boolean;
payment: Invoice;
serviceType: Task;
client: Client;
assignedUser: User;
static serialize(data: any): Availability {
const user: Availability = Object.assign(new this(), data);
if (data.hasOwnProperty('timestampstart')) {
user.timestampstart = moment(data.timestampstart);
}
if (data.hasOwnProperty('timestampend')) {
user.timestampend = moment(data.timestampend);
}
if (data.hasOwnProperty('serviceType')) {
user.serviceType = Task.serialize(data.serviceType);
}
if (data.hasOwnProperty('client')) {
user.client = Client.serialize(data.client);
}
if (data.hasOwnProperty('assignedUser')) {
user.assignedUser = User.serialize(data.assignedUser);
}
return user;
}
}
I figured it out, since I was using Angular, I assigned the formControlName to the wrong HTML tag (the option tag instead of the select) causing the get of the value to return a value that I wasn't expecting. I've updated the main post to show the entire code.
Related
I make this component with custom validate and declare variable in top of component and assigning value to , but when i use this variable inside custom validation it return this error if change value in form "ERROR TypeError: Cannot read properties of undefined (reading 'file')"
in custom valide extention file I have to declare and assign value to variable inside '
so I want to know the reason of that
is custom validate fun not observe so it excute before other fun?,
but why variable value donot existing and assigning to undefiend,
and how i use file size in validate file size?
in all conditions this is code of component
import { Component, ElementRef, OnInit, ViewChild } from '#angular/core';
import { AbstractControl, FormArray, FormControl, FormGroup, ValidationErrors, Validators } from '#angular/forms';
import { PagedResult } from 'src/app/core/models/pagedResult.model';
import { AlertService } from 'src/app/core/services/alert.service';
import { FakeDataService } from 'src/app/core/services/fake-data.service';
import { Employee } from '../../models/employee.model';
import { PenaltyReason } from '../../models/penalty-reason.model';
import { PenaltyType } from '../../models/penalty-type.model';
import { Penalty } from '../../models/penalty.model';
#Component({
selector: 'app-penalties',
templateUrl: './penalties.component.html',
styleUrls: ['./penalties.component.css']
})
export class PenaltiesComponent implements OnInit {
penalties: PagedResult<Penalty>;
page = 1;
formTitle = "اضافة عقوبة";
penaltyTypes: PenaltyType[];
penaltyReasons: PenaltyReason[];
employees: Employee[];
selectedPenaltyIndex: number;
penaltyForm: FormGroup;
file: FormControl;
constructor(private fakeData: FakeDataService, private alert: AlertService) { }
loadData() {
this.fakeData.getDataFor('Penalties', this.page).then(result => {
this.penalties = result as PagedResult<Penalty>;
});
}
loadRequiredData() {
//load penalty Types
this.fakeData.getAllDataFor('PenaltyTypes').then(result => {
this.penaltyTypes = result as PenaltyType[];
});
// Loading penalty Reasons
this.fakeData.getAllDataFor('penaltyReasons').then(result => {
this.penaltyReasons = result as PenaltyReason[];
});
// Loading Employees list
this.fakeData.getAllDataFor('Employees').then(result => {
this.employees = result as Employee[];
});
}
ngOnInit(): void {
this.resetForm();
this.loadData();
this.loadRequiredData();
}
paginateData(page: number) {
this.page = page;
this.loadData();
}
showEditForm(id: number, index: number) {
var penalty2Edit = this.penalties?.data?.find(p => p.Id == id) as Penalty;
this.penaltyForm?.setValue({ ...penalty2Edit });
this.selectedPenaltyIndex = index;
}
delete(id: number) {
var itemIndex = this.penalties.data.findIndex(c => c.Id == id);
this.penalties.data.splice(itemIndex, 1);
this.alert.showSuccess('تم مسح العنصر بنجاح');
}
resetForm() {
this.file = new FormControl(null);
this.penaltyForm = new FormGroup({
Id: new FormControl(0),
FingerPrintId: new FormControl(0),
EmpId: new FormControl(0),
EmpName: new FormControl('', Validators.required),
Date: new FormControl('', Validators.required),
PenalityTypeId: new FormControl(0),
PenalityTypeText: new FormControl('', Validators.required),
DeductionDaysCount: new FormControl(0, Validators.required),
ReasonTypeId: new FormControl(0, Validators.required),
DisciplineNotes: new FormControl('', Validators.required),
ImageURL: new FormControl('', [this.ValidateExtentionFile, this.validateFileSize])
});
this.loadRequiredData();
}
addPenalty() {
this.penaltyForm.get('Id')?.setValue(Math.floor(Math.random() * 1000));
this.penalties.data.unshift(this.penaltyForm.value);
this.resetForm();
this.alert.showSuccess("تم إضافةالعقوبة بنجاح");
}
updatePenalty() {
this.penalties.data.splice(this.selectedPenaltyIndex, 1, this.penaltyForm.value);
this.alert.showSuccess('تم تعديل العنصر بنجاح');
}
// deal with file
onFileSelected(event: any) {
this.file.setValue(event.target.files[0]);
console.log(this.file);
}
ValidateExtentionFile(control: AbstractControl): ValidationErrors | null {
if (!control.value)
return null;
let allowedEXT = ['jpg', 'png', 'pdf', 'jpeg'];
var fileExt = control.value?.split('.').pop().toLowerCase();
if (allowedEXT.includes(fileExt))
return null;
return { valideFileEXT: true };
}
validateFileSize(control: AbstractControl): ValidationErrors | null {
let maxSize = 1;
if (!control.value || !this.file)
return null;
const fileSize = (this.file.value.size / 1024 / 1024); // convert to MB
if (fileSize > maxSize) {
return { 'validateFileSize': true };
}
return null;
}
}
you can use like this
in the component file:
handleFileInput(files: any) {
this.fileNameDetail = undefined;
const image = files.files.item(0);
const reader = new FileReader();
reader.addEventListener("load", () => {
this.imageUrl = reader.result;
}, false);
reader.readAsDataURL(image);
}
in the template file:
<button (click)="filePicker.click()" class="file-picker-opener">انتخاب فایل</button>
<input type="file"
class="form-control"
#filePicker
accept="image/jpeg,image/jpg"
id="file"
(change)="handleFileInput($event.target);"
/>
I'm trying to write cross-field -validation using Abstract Control.
control.value[start] is showing undefined. kindly help me on solving.
working code is here.
app.component.html
export class AppComponent {
form: FormGroup;
rangeStart = new FormControl('', {
validators: [this.MyAwesomeRangeValidator('rangeStart')],
});
rangeEnd = new FormControl('', {
validators: this.MyAwesomeRangeValidator('rangeEnd'),
});
constructor(private fb: FormBuilder) {
this.form = this.fb.group({
rangeStart: this.rangeStart,
rangeEnd: this.rangeEnd,
});
}
MyAwesomeRangeValidator(rangeStart: string): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const start = control.value[rangeStart];
const end = control.value;
console.log('start', start, 'end', end);
let re = /^[1-9]\d*$/;
if (re.test(start) && re.test(end)) {
return null;
} else if (re.test(start) && !re.test(end)) {
return null;
} else if (!re.test(start) && re.test(end)) {
return null;
} else {
return {
range: true
};
}
};
}
}
<form [formGroup]="form">
<input formControlName="rangeStart" placeholder="Range start" type="number">
<input formControlName="rangeEnd" placeholder="Range end" type="number">
</form>
<div> Valid: {{ form.valid ? '👍' : '👎' }} </div>
working code
You are trying to get property 'rangeStart' from string.
const start = control.value[rangeStart];, where control.value is a string. If you want to validate few fields at once then you should put validator to the FormGroup.
export class AppComponent {
form: FormGroup;
rangeStart = new FormControl('', {
validators: [this.MyAwesomeRangeValidator('rangeStart', 'rangeEnd')],
});
rangeEnd = new FormControl('', {
validators: this.MyAwesomeRangeValidator('someField_1', 'someField_2'),
});
constructor(private fb: FormBuilder) {
this.form = this.fb.group({
rangeStart: this.rangeStart,
rangeEnd: this.rangeEnd,
});
}
MyAwesomeRangeValidator(
startControlName: string,
endControlName: string
): ValidatorFn {
return (formGroup: FormGroup): ValidationErrors | null => {
const start = formGroup.get(startControlName).value;
const end = formGroup.get(endControlName).value;
console.log(formGroup, 'start', start, 'end', end);
let re = /^[1-9]\d*$/;
if (!re.test(start) && !re.test(end)) {
return { range: true };
}
return null;
};
}
}
UPD: added dynamic form controls names
Hi I am trying to set/ take away custom validators for different elements in a form array that can change around, so far what I have tried to do is create a switch statement and loop through all of the input types that are set so I could set the validation rule as well as send a message to the user if the rule isn't met. The problem I am having is the form is initialized before the form data is set.
So my question is how can I loop through the array and set the validation rules. If someone could let me know if im along the right tracks with using a switch statement but have code in the wrong place or if there is a different and better approach it would be most helpful thank you
export class ReactiveComponent implements OnInit {
public form: FormGroup;
public fieldList: any;
types: Array<any>;
formData: any;
Param: string;
setData: any;
formLabelNames: any;
get contactFormGroup() {
return this.form.get('inputs') as FormArray;
}
constructor(
private route: ActivatedRoute,
private fb: FormBuilder,
private api: FormService,
private notifiy: NotificationService,
private auth: AuthService,
private router: Router) { }
ngOnInit() {
this.form = this.fb.group({
name: ['', Validators.compose([Validators.required])],
organization: ['', Validators.compose([Validators.required])],
inputs: this.fb.array([this.createForm()])
});
this.route.paramMap.subscribe(params => {
this.Param = params.get('id');
this.getForm(this.Param);
});
// set fieldslist to this field
this.fieldList = this.form.get('inputs') as FormArray;
}
// formgroup
createForm(): FormGroup {
return this.fb.group({
type: ['', Validators.compose([Validators.required])],
name: ['', Validators.compose([Validators.required])],
value: ['', this.validators()]
});
}
getForm(id) {
this.api.getForm(id).subscribe(
(data: any) => this.setForm(data)
);
}
getFieldsFormGroup(index): FormGroup {
const formGroup = this.fieldList.controls[index] as FormGroup;
return formGroup;
}
getContactsFormGroup(index): FormGroup {
const formGroup = this.fieldList.controls[index] as FormGroup;
return formGroup;
}
setForm(data) {
const d = data.results;
this.setData = d;
this.formLabelNames = d[0].fields;
this.form.patchValue({
name: [d[0].form_name],
organization: [d[0].org],
});
this.form.setControl('inputs', this.setExistingFields(d[0].fields));
}
setExistingFields(fields: any): FormArray {
const formArray = new FormArray([]);
this.fieldList = formArray;
fields.forEach(f => {
formArray.push(this.fb.group({
name: f.name,
type: f.type,
value: f.value
}));
});
return formArray;
}
/* This is where I have tried to create a switch statement but I get a undefined error because the setform function is being called after this one */
validators() {
this.formLabelNames.type.forEach((field: any) => {
switch (field.type) {
case 'email':
}
});
}
submit() {
if (this.form.valid) {
const formId = this.Param;
const local = this.auth.decodePayload();
const userId = local.sub;
this.router.navigateByUrl('/dashboard');
this.api.sendForm(this.form.value, formId, userId).subscribe();
this.form.reset();
} else {
this.notifiy.showFailure('Form is not valid', 'Error');
}
}
}
You have several problems, as far as I can judge:
You don't initialize neither the formControls nor the formArray correctly inside of you formGroup. It should rather look like this:
this.form = this.fb.group({
name: new FormControl( "", {validators: [Validators.required]}),
organization: new FormControl( "", {validators: [Validators.required]}),
inputs: this.fb.array([new FormControl(), new FormControl()]),
});
Besides: What is the point of using a formArray when it consists of only one formGroup?
Off course you can set the validators for any abstractControl be it a formControl or a formGroup. For the array it would look like something like this:
this.formArray.controls.map((ctrl) => {
ctrl.setValidators([Validators.required, Validators.email]);
});
It doesn't matter where in the component class you put your method. Though it surely matters when that method is invoked!
I have a huge amont of data to transform into new format.
Actually I'm using map method but as it's syncronous and it's affecting performances.
dataFormatted = cmtAllRawdataDB[0].rows.map(elm => new Message(elm, configResult));
For information Message class have globally this format:
export class Data {
public value: string;
public date: Date;
constructor(dbData) {
this.value = '123';
}
}
export class Measure {
public name: string;
public unit: string;
public data: Data[];
constructor(config, dbData) {
this.name = config.name;
this.unit = config.value;
...
this.data = [new Data(dbData)];
}
}
export class Sensor {
public id: string;
public label: string;
public measures: Measure[] = [];
constructor(dbData, config) {
this.id = '123';
this.label = 'SensorType';
config.unitConfig.map(elm => this.measures.push(new Measure(elm, dbData)));
}
}
export class Message {
public id: string;
...
public sensors: Sensor[];
constructor(dbData: any, config: any) {
this.id = dbData.value._id;
....
this.sensors = [new Sensor(dbData, config)];
console.log(this.id, this.arrivalTimestamp);
}
}
Is there a way to run asynchronously this code ?
Just put this operation inside function and put it inside settimeout method, for just 10 millisecond
var example = () => {
setTimeout(() => {
return (dataFormatted = cmtAllRawdataDB[0].rows.map(
elm => new Message(elm, configResult)
));
}, 10);
};
Use async and await keywords like this way
async getDataFormatted(){ return(cmtAllRawdataDB[0].rows.map(elm => new Message(elm, configResult)));
}
let dataFormatted= await getDataFormatted();
Imagine I have the following interfaces
interface IMarket {
ID: number,
Name: string,
MarketDescription: string
}
interface IDepartment {
ID: number,
Name: string,
DepartmentDescription: string
}
Is there a way to store the interfaces in an object like this?
var typeMap = { Markets: IMarket, Departments: IDepartment }
I'd like to do something like this. I'd like to dynamically set the generic type for "getQueryResults" based on a string value I pass into the constructor.
export class Service {
protected baseURL = "";
protected typeName = "";
private typeMap = { Markets: IMarket, Departments: IDepartment }
constructor(typeName) {
this.baseURL = 'http://localhost/API/odata/' + typeName;
this.currentType = typeMap[typeName];
}
getQueryResults(): Promise<this.currentType> {
return new Promise<this.currentType>((resolve, reject) => {
$.getJSON(this.baseURL, function (returnValue) {
resolve(returnValue.value);
});
})
}
}
var marketService = new Service("Markets");
var topMarket = marketService.getQueryResults();
//topMarket is an instance(?) of IMarket
var departmentService = new Service("Departments");
var topDepartment = departmentServicegetQueryResults();
//topDepartment is an instance(?) of IDepartment
That can be simply solved using generics, it's exactly what it's for:
export class Service<T> {
protected baseURL = "";
constructor() {
this.baseURL = 'http://localhost/API/odata/' + typeName;
}
getQueryResults(): Promise<T> {
return new Promise<T>((resolve, reject) => {
$.getJSON(this.baseURL, function (returnValue) {
resolve(returnValue.value);
});
})
}
}
var marketService = new Service<IMarket>();
var topMarket: Promise<IMarket> = marketService.getQueryResults();
var departmentService = new Service<IDepartment>();
var topDepartment: Promise<IDepartment> = departmentService.getQueryResults();
Edit
You can use 2 more classes to "get rid" of the need to have Service<TYPE> more than once (per TYPE):
export abstract class Service<T> {
protected baseURL = "";
constructor() {
this.baseURL = 'http://localhost/API/odata/' + this.getTypeName();
}
getQueryResults(): Promise<T> {
return new Promise<T>((resolve, reject) => {
$.getJSON(this.baseURL, function (returnValue) {
resolve(returnValue.value);
});
})
}
protected abstract getTypeName(): string;
}
export class MarketsService extends Service<IMarket> {
protected getTypeName(): string {
return "Markets";
}
}
export class DepartmentsService extends Service<IDepartment> {
protected getTypeName(): string {
return "Departments";
}
}
var marketService = new MarketsService();
var topMarket: Promise<IMarket> = marketService.getQueryResults();
var departmentService = new DepartmentsService();
var topDepartment: Promise<IDepartment> = departmentService.getQueryResults();
But unlike the need to specify the type every time you use Service, these extra classes will be part of the compiled js, so it's a question of what's more important to you.
Taking a note from the TypeScript docs:
http://www.typescriptlang.org/docs/handbook/namespaces.html#namespaced-validators
namespace Validation {
export interface StringValidator {
isAcceptable(s: string): boolean;
}
It appears you would want:
namespace YourNamespace {
export interface IMarket {
ID: number,
Name: string,
MarketDescription: string
}
export interface IDepartment {
ID: number,
Name: string,
DepartmentDescription: string
}
}