Skip to content

Add generic typings #47

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@

* The type of validators is adjusted

### 8.0.0-beta

* [BREAKING CHANGE] `FormStore` uses a generic now
* [BREAKING CHANGE] `FormObject` uses a generic now

### 9.0.3-beta

* Temporary adjust save method types
Expand Down
10 changes: 5 additions & 5 deletions projects/ngx-form-object-sample-app/src/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import { UserFormObject } from './models/user.form-object';
import { User } from './models/user.model';

@Component({
selector: 'ngxfosa-root', // ngxfosa === ngx-form-object sample app
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
standalone: false
selector: 'ngxfosa-root', // ngxfosa === ngx-form-object sample app
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
standalone: false,
})
export class AppComponent {
public userFormStore: FormStore;
public userFormStore: FormStore<User>;

constructor(public injector: Injector) {
const user: User = new User();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@ import { FormObject, FormObjectOptions } from 'ngx-form-object';
import { Observable, of } from 'rxjs';
import { User } from './user.model';

export class UserFormObject extends FormObject {
export class UserFormObject extends FormObject<User> {
public validators: Record<string, ValidatorFn | Array<ValidatorFn>> = {
name: [Validators.required],
};

constructor(public model: any, protected options: FormObjectOptions, public injector: Injector) {
constructor(
public model: User,
protected options: FormObjectOptions,
public injector: Injector
) {
super(model, options);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { FormObject } from '../../form-object/form-object';
import { updateClassPropertyMetadata } from '../../helpers/update-class-property-metadata/update-class-property-metadata.helper';
import { MODEL_BUILD_CONTROL_METHODS } from '../../types/model-metadata.type';

export function BuildControl(propertyName: string): MethodDecorator {
return function (target: FormObject, _key: string | symbol, descriptor: PropertyDescriptor) {
export function BuildControl<T>(propertyName: string): MethodDecorator {
return function (target: FormObject<T>, _key: string | symbol, descriptor: PropertyDescriptor) {
updateClassPropertyMetadata(target, propertyName, MODEL_BUILD_CONTROL_METHODS, descriptor.value);
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { FormObject } from '../../form-object/form-object';
import { updateClassPropertyMetadata } from '../../helpers/update-class-property-metadata/update-class-property-metadata.helper';
import { CREATE_FORM_OBJECT_METHODS } from '../../types/model-metadata.type';

export function BuildRelationshipFormObject(propertyName: string): MethodDecorator {
return function (target: FormObject, _key: string | symbol, descriptor: PropertyDescriptor) {
export function BuildRelationshipFormObject<T>(propertyName: string): MethodDecorator {
return function (target: FormObject<T>, _key: string | symbol, descriptor: PropertyDescriptor) {
updateClassPropertyMetadata(target, propertyName, CREATE_FORM_OBJECT_METHODS, descriptor.value);
};
}
Original file line number Diff line number Diff line change
@@ -1,28 +1,25 @@
import { FormBuilder, ValidatorFn, AbstractControl } from '@angular/forms';
import { AbstractControl, FormBuilder, ValidatorFn } from '@angular/forms';
import { ExtendedFormArray } from '../extended-form-array/extended-form-array';
import { ExtendedFormControl } from '../extended-form-control/extended-form-control';
import { FormObject } from '../form-object/form-object';
import { FormStore } from '../form-store/form-store';
import { capitalize } from '../helpers/helpers';
import { PropertyOptions } from '../interfaces/property-options.interface';
import { MODEL_BUILD_CONTROL_METHODS } from '../types/model-metadata.type';
import { CREATE_FORM_OBJECT_METHODS } from '../types/model-metadata.type';
import { Injectable } from '@angular/core';
import { CREATE_FORM_OBJECT_METHODS, MODEL_BUILD_CONTROL_METHODS } from '../types/model-metadata.type';

@Injectable()
export class FormObjectBuilder {
public formBuilder: FormBuilder = new FormBuilder();

public create(formObject: FormObject): FormStore {
const formFields = {};
public create<T>(formObject: FormObject<T>): FormStore<T> {
const formFields: Record<string, AbstractControl> = {};

Object.assign(formFields, this.createAttributeFormFields(formObject));
Object.assign(formFields, this.createHasManyFormFields(formObject));
Object.assign(formFields, this.createBelongsToFormFields(formObject));

const formStoreClass: any = formObject.formStoreClass ? formObject.formStoreClass : FormStore;
const formStoreClass = formObject.formStoreClass || FormStore;

const formStore: FormStore = new formStoreClass(
const formStore: FormStore<T> = new formStoreClass(
formFields,
formObject.formGroupOptions.validator,
formObject.formGroupOptions.asyncValidator
Expand All @@ -32,10 +29,10 @@ export class FormObjectBuilder {
return formStore;
}

private createAttributeFormFields(formObject: FormObject): Record<string, unknown> {
const attributeFormFields = {};
private createAttributeFormFields<T>(formObject: FormObject<T>): Record<string, AbstractControl> {
const attributeFormFields: Record<string, AbstractControl> = {};

formObject.attributePropertiesKeys.forEach((attributeName: string | symbol) => {
formObject.attributePropertiesKeys.forEach((attributeName: string) => {
const buildFunction = this.getBuildFunction(formObject, attributeName);
const validators: ValidatorFn | Array<ValidatorFn> = formObject.getValidators(attributeName.toString());
const maskFunction: Function = formObject[`mask${capitalize(attributeName.toString())}`];
Expand All @@ -52,10 +49,10 @@ export class FormObjectBuilder {
return attributeFormFields;
}

private createHasManyFormFields(formObject: FormObject): Record<string, unknown> {
const hasManyFormFields = {};
private createHasManyFormFields<T>(formObject: FormObject<T>): Record<string, AbstractControl> {
const hasManyFormFields: Record<string, AbstractControl> = {};

formObject.hasManyPropertiesKeys.forEach((propertyName) => {
formObject.hasManyPropertiesKeys.forEach((propertyName: string) => {
const buildFunction = this.getBuildFunction(formObject, propertyName);
const validators: ValidatorFn | Array<ValidatorFn> = formObject.getValidators(propertyName.toString());
const hasManyModels = formObject.model[propertyName];
Expand All @@ -69,7 +66,7 @@ export class FormObjectBuilder {
return hasManyFormFields;
}

private createBelongsToFormFields(formObject: FormObject): Record<string, unknown> {
private createBelongsToFormFields<T>(formObject: FormObject<T>): object {
const belongsToFormFields = {};

formObject.belongsToPropertiesKeys.forEach((propertyName: string | symbol) => {
Expand Down Expand Up @@ -103,7 +100,7 @@ export class FormObjectBuilder {
return belongsToFormFields;
}

private getBuildFunction(formObject: FormObject, propertyName: string | symbol): () => AbstractControl {
private getBuildFunction<T>(formObject: FormObject<T>, propertyName: string | symbol): () => AbstractControl {
const propertyNameString = propertyName.toString();

// Deprecated in favour of build control decorator
Expand All @@ -116,8 +113,8 @@ export class FormObjectBuilder {
return buildFunction;
}

private buildRelationshipModels(
formObject: FormObject,
private buildRelationshipModels<T>(
formObject: FormObject<T>,
relationshipName: string | symbol,
relationshipModels: Array<any> = [],
propertyOptions: PropertyOptions
Expand All @@ -126,7 +123,11 @@ export class FormObjectBuilder {
const formGroups: Array<any> = [];

relationshipModels.forEach((relationshipModel) => {
const formStore: FormStore = this.createRelationshipFormObject(formObject, relationshipName, relationshipModel);
const formStore: FormStore<T> = this.createRelationshipFormObject(
formObject,
relationshipName,
relationshipModel
);
if (formStore) {
formGroups.push(formStore);
}
Expand All @@ -142,12 +143,12 @@ export class FormObjectBuilder {
return relationshipFormGroups;
}

private createRelationshipFormObject(
formObject: FormObject,
private createRelationshipFormObject<T>(
formObject: FormObject<T>,
relationshipName: string | symbol,
relationshipModel: any,
propertyOptions: PropertyOptions = {}
): FormStore {
): FormStore<T> {
const relationshipNameString: string = relationshipName.toString();

// Deprecated in favour of create form object decorators
Expand All @@ -158,13 +159,13 @@ export class FormObjectBuilder {
}

if (createFormObjectFunction) {
const modelFormObject: FormObject = createFormObjectFunction.call(
const modelFormObject: FormObject<T> = createFormObjectFunction.call(
formObject,
relationshipModel,
null,
propertyOptions
);
const formStore: FormStore = this.create(modelFormObject);
const formStore: FormStore<T> = this.create(modelFormObject);
return formStore;
} else {
// There is no function specified for creating form object for ${relationshipName.toString()}
Expand Down
26 changes: 21 additions & 5 deletions projects/ngx-form-object/src/lib/form-object/form-object.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,19 @@ class UserMock {

@Attribute()
public displayName: string;
public config: any = null;
public city: string;
}

const customValidatorFn: ValidatorFn = () => null;

class UserMockFormObject extends FormObject {
class UserMockFormObject extends FormObject<UserMock> {
public validators: Record<string, ValidatorFn | Array<ValidatorFn>> = {
name: [Validators.required],
name: [customValidatorFn, Validators.required],
city: customValidatorFn,
};

protected beforeSave(form: FormStore): Observable<FormStore> {
protected beforeSave(form: FormStore<UserMock>): Observable<FormStore<UserMock>> {
this.mockBeforeSave();
return of(form);
}
Expand All @@ -33,7 +35,7 @@ class UserMockFormObject extends FormObject {
return of(_model);
}

protected afterSave(model?: any, _form?: FormStore): Observable<any> {
protected afterSave(model?: any, _form?: FormStore<UserMock>): Observable<any> {
this.mockAfterSave();
return of(model);
}
Expand All @@ -53,7 +55,7 @@ class UserMockFormObject extends FormObject {

describe('Saving form', () => {
const name = 'John';
let form: FormStore;
let form: FormStore<UserMock>;

beforeEach(() => {
const formObjectBuilder = new FormObjectBuilder();
Expand Down Expand Up @@ -169,3 +171,17 @@ describe('Saving form', () => {
});
});
});

describe('Model Form Object', () => {
let userMockFormObject: UserMockFormObject;

beforeEach(() => {
const userMock = new UserMock();
userMockFormObject = new UserMockFormObject(userMock, null);
});

it('should return validators for single form field', () => {
expect(userMockFormObject.validators.name).toEqual([customValidatorFn, Validators.required]);
expect(userMockFormObject.validators.city).toEqual(customValidatorFn);
});
});
Loading
Loading