Feature/add form validation against DTO for activity and account (#3230)
* Add form validation against DTO for activity and account * Update changelog
This commit is contained in:
parent
5d4e2fba8c
commit
9241c04d5a
@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Added a form validation against the DTO in the create or update account dialog
|
||||||
|
- Added a form validation against the DTO in the create or update activity dialog
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Moved the dividend calculations into the portfolio calculator
|
- Moved the dividend calculations into the portfolio calculator
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { CreateAccountDto } from '@ghostfolio/api/app/account/create-account.dto';
|
import { CreateAccountDto } from '@ghostfolio/api/app/account/create-account.dto';
|
||||||
import { UpdateAccountDto } from '@ghostfolio/api/app/account/update-account.dto';
|
import { UpdateAccountDto } from '@ghostfolio/api/app/account/update-account.dto';
|
||||||
import { DataService } from '@ghostfolio/client/services/data.service';
|
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||||
|
import { validateObjectForForm } from '@ghostfolio/client/util/form.util';
|
||||||
import { Currency } from '@ghostfolio/common/interfaces';
|
import { Currency } from '@ghostfolio/common/interfaces';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -102,7 +103,7 @@ export class CreateOrUpdateAccountDialog implements OnDestroy {
|
|||||||
this.dialogRef.close();
|
this.dialogRef.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
public onSubmit() {
|
public async onSubmit() {
|
||||||
const account: CreateAccountDto | UpdateAccountDto = {
|
const account: CreateAccountDto | UpdateAccountDto = {
|
||||||
balance: this.accountForm.controls['balance'].value,
|
balance: this.accountForm.controls['balance'].value,
|
||||||
comment: this.accountForm.controls['comment'].value,
|
comment: this.accountForm.controls['comment'].value,
|
||||||
@ -113,13 +114,29 @@ export class CreateOrUpdateAccountDialog implements OnDestroy {
|
|||||||
platformId: this.accountForm.controls['platformId'].value?.id ?? null
|
platformId: this.accountForm.controls['platformId'].value?.id ?? null
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.data.account.id) {
|
try {
|
||||||
(account as UpdateAccountDto).id = this.data.account.id;
|
if (this.data.account.id) {
|
||||||
} else {
|
(account as UpdateAccountDto).id = this.data.account.id;
|
||||||
delete (account as CreateAccountDto).id;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.dialogRef.close({ account });
|
await validateObjectForForm({
|
||||||
|
classDto: UpdateAccountDto,
|
||||||
|
form: this.accountForm,
|
||||||
|
object: account
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
delete (account as CreateAccountDto).id;
|
||||||
|
|
||||||
|
await validateObjectForForm({
|
||||||
|
classDto: CreateAccountDto,
|
||||||
|
form: this.accountForm,
|
||||||
|
object: account
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dialogRef.close({ account });
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ngOnDestroy() {
|
public ngOnDestroy() {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
|
import { CreateOrderDto } from '@ghostfolio/api/app/order/create-order.dto';
|
||||||
import { UpdateOrderDto } from '@ghostfolio/api/app/order/update-order.dto';
|
import { UpdateOrderDto } from '@ghostfolio/api/app/order/update-order.dto';
|
||||||
import { DataService } from '@ghostfolio/client/services/data.service';
|
import { DataService } from '@ghostfolio/client/services/data.service';
|
||||||
|
import { validateObjectForForm } from '@ghostfolio/client/util/form.util';
|
||||||
import { getDateFormatString } from '@ghostfolio/common/helper';
|
import { getDateFormatString } from '@ghostfolio/common/helper';
|
||||||
import { translate } from '@ghostfolio/ui/i18n';
|
import { translate } from '@ghostfolio/ui/i18n';
|
||||||
|
|
||||||
@ -451,7 +452,7 @@ export class CreateOrUpdateActivityDialog implements OnDestroy {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public onSubmit() {
|
public async onSubmit() {
|
||||||
const activity: CreateOrderDto | UpdateOrderDto = {
|
const activity: CreateOrderDto | UpdateOrderDto = {
|
||||||
accountId: this.activityForm.controls['accountId'].value,
|
accountId: this.activityForm.controls['accountId'].value,
|
||||||
assetClass: this.activityForm.controls['assetClass'].value,
|
assetClass: this.activityForm.controls['assetClass'].value,
|
||||||
@ -474,14 +475,32 @@ export class CreateOrUpdateActivityDialog implements OnDestroy {
|
|||||||
unitPrice: this.activityForm.controls['unitPrice'].value
|
unitPrice: this.activityForm.controls['unitPrice'].value
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.data.activity.id) {
|
try {
|
||||||
(activity as UpdateOrderDto).id = this.data.activity.id;
|
if (this.data.activity.id) {
|
||||||
} else {
|
(activity as UpdateOrderDto).id = this.data.activity.id;
|
||||||
(activity as CreateOrderDto).updateAccountBalance =
|
|
||||||
this.activityForm.controls['updateAccountBalance'].value;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.dialogRef.close({ activity });
|
await validateObjectForForm({
|
||||||
|
classDto: UpdateOrderDto,
|
||||||
|
form: this.activityForm,
|
||||||
|
ignoreFields: ['dataSource', 'date'],
|
||||||
|
object: activity as UpdateOrderDto
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
(activity as CreateOrderDto).updateAccountBalance =
|
||||||
|
this.activityForm.controls['updateAccountBalance'].value;
|
||||||
|
|
||||||
|
await validateObjectForForm({
|
||||||
|
classDto: CreateOrderDto,
|
||||||
|
form: this.activityForm,
|
||||||
|
ignoreFields: ['dataSource', 'date'],
|
||||||
|
object: activity
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.dialogRef.close({ activity });
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ngOnDestroy() {
|
public ngOnDestroy() {
|
||||||
|
38
apps/client/src/app/util/form.util.ts
Normal file
38
apps/client/src/app/util/form.util.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { FormGroup } from '@angular/forms';
|
||||||
|
import { plainToInstance } from 'class-transformer';
|
||||||
|
import { validate } from 'class-validator';
|
||||||
|
|
||||||
|
export async function validateObjectForForm<T>({
|
||||||
|
classDto,
|
||||||
|
form,
|
||||||
|
ignoreFields = [],
|
||||||
|
object
|
||||||
|
}: {
|
||||||
|
classDto: { new (): T };
|
||||||
|
form: FormGroup;
|
||||||
|
ignoreFields?: string[];
|
||||||
|
object: T;
|
||||||
|
}): Promise<void> {
|
||||||
|
const objectInstance = plainToInstance(classDto, object);
|
||||||
|
const errors = await validate(objectInstance as object);
|
||||||
|
|
||||||
|
const nonIgnoredErrors = errors.filter(({ property }) => {
|
||||||
|
return !ignoreFields.includes(property);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (nonIgnoredErrors.length === 0) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const { constraints, property } of nonIgnoredErrors) {
|
||||||
|
const formControl = form.get(property);
|
||||||
|
|
||||||
|
if (formControl) {
|
||||||
|
formControl.setErrors({
|
||||||
|
validationError: Object.values(constraints)[0]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(nonIgnoredErrors);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user