Skip to content

Commit

Permalink
feat: add more validators
Browse files Browse the repository at this point in the history
  • Loading branch information
timdeschryver committed Feb 16, 2024
1 parent 9c90a18 commit 6dc1a3f
Show file tree
Hide file tree
Showing 15 changed files with 196 additions and 83 deletions.
10 changes: 4 additions & 6 deletions apps/example/src/app/basic-form/basic-form.component.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Component, effect, inject } from '@angular/core';
import { Component, effect } from '@angular/core';
import {
SignalFormBuilder,
createFormGroup,
SignalInputDebounceDirective,
SignalInputDirective,
SignalInputErrorDirective,
withErrorComponent,
withErrorComponent
} from '@ng-signal-forms';
import { JsonPipe, NgFor, NgIf } from '@angular/common';
import { FormsModule } from '@angular/forms';
Expand Down Expand Up @@ -63,9 +63,7 @@ import { CustomErrorComponent } from '../custom-input-error.component';
providers: [withErrorComponent(CustomErrorComponent)],
})
export default class BasicFormComponent {
private sfb = inject(SignalFormBuilder);

form = this.sfb.createFormGroup<{ name: string; age: number | null }>({
form = createFormGroup<{ name: string; age: number | null }>({
name: 'Alice',
age: null,
});
Expand Down
20 changes: 7 additions & 13 deletions apps/example/src/app/multi-page-form/multi-page.form.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,19 @@
import {
V,
createFormField,
createFormGroup,
createInjectableSignalForm,
} from '@ng-signal-forms';
import { signal } from '@angular/core';
import { createFormField, createFormGroup, createInjectableSignalForm, equalsTo, required } from '@ng-signal-forms';

export const { injectSignalForm, provideSignalForm } =
createInjectableSignalForm(() =>
createFormGroup(() => ({
step1: createFormGroup(() => ({
firstName: createFormField('', { validators: [V.required()] }),
lastName: createFormField('', { validators: [V.required()] }),
firstName: createFormField('', { validators: [required()] }),
lastName: createFormField('', { validators: [required()] }),
})),
step2: createFormGroup(() => ({
street: createFormField('', { validators: [V.required()] }),
street: createFormField('', { validators: [required()] }),
zip: createFormField<number | undefined>(undefined, {
validators: [V.required(), V.equalsTo(signal(1234))],
validators: [required(), equalsTo(1234)],
}),
city: createFormField('', { validators: [V.required()] }),
country: createFormField('', { validators: [V.required()] }),
city: createFormField('', { validators: [required()] }),
country: createFormField('', { validators: [required()] }),
})),
}))
);
22 changes: 11 additions & 11 deletions apps/example/src/app/simple-form/simple-form.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
SignalInputDebounceDirective,
SignalInputDirective,
SignalInputErrorDirective,
V,
Validators,
Validator,
withErrorComponent,
} from '@ng-signal-forms';
Expand Down Expand Up @@ -45,7 +45,7 @@ import { CustomErrorComponent } from '../custom-input-error.component';
ngModel
[formField]="form.controls.passwords.controls.password"
/>
@if(form.controls.passwords.hasError('minLength')) {
<small>
{{ form.controls.passwords.errorMessage('minLength') }}
Expand Down Expand Up @@ -135,17 +135,17 @@ export default class SimpleFormComponent {
private sfb = inject(SignalFormBuilder);
form = this.sfb.createFormGroup(() => {
const username = this.sfb.createFormField('', {
validators: [V.required(), uniqueUsername()],
validators: [Validators.required(), uniqueUsername()],
});

return {
username,
passwords: this.sfb.createFormGroup(() => {
const password = this.sfb.createFormField('', (pw) => ({
validators: [
V.required(),
Validators.required(),
{
validator: V.minLength(5),
validator: Validators.minLength(5),
disable: () => pw().toLocaleLowerCase().startsWith('rob'),
message: ({
currentLength,
Expand All @@ -168,7 +168,7 @@ export default class SimpleFormComponent {
passwordConfirmation: this.sfb.createFormField<string | undefined>(
undefined,
{
validators: [V.required(), V.equalsTo(password.value)],
validators: [Validators.required(), Validators.equalsTo(password.value)],
hidden: () => {
return password.value() === '';
},
Expand All @@ -181,26 +181,26 @@ export default class SimpleFormComponent {
return [];
},
{
validators: [V.minLength(1)],
validators: [Validators.minLength(1)],
}
),

team: this.sfb.createFormGroup([
this.sfb.createFormField(''),
this.sfb.createFormField(''),
this.sfb.createFormField(''),
]),
};

});

createTodo = () => {
return this.sfb.createFormGroup<Todo>(() => {
return {
description: this.sfb.createFormField('', {
validators: [
V.required(),
V.minLength(5),
Validators.required(),
Validators.minLength(5),
todoUniqueInList(
this.form.controls.todos.value as unknown as Signal<Todo[]>
),
Expand Down
3 changes: 2 additions & 1 deletion packages/platform/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as V from './lib/validators';

export * from './lib/validation';
export * from './lib/validators';
export * from './lib/form-field';
export * from './lib/form-group';
export * from './lib/form-builder';
Expand All @@ -11,4 +12,4 @@ export * from './lib/signal-input-debounce.directive';
export * from './lib/signal-input-modifier.token';
export * from './lib/injectable-signal-form.token';

export { V };
export { V, V as Validators };
52 changes: 0 additions & 52 deletions packages/platform/src/lib/validators.ts

This file was deleted.

22 changes: 22 additions & 0 deletions packages/platform/src/lib/validators/equals-to.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { isSignal, Signal } from '@angular/core';
import { SetValidationState, ValidatorFn } from '../validation';

export function equalsTo<Value>(otherValue: Value | Signal<Value>): ValidatorFn<Value> {
return (value: Value, setState: SetValidationState) => {
const other = isSignal(otherValue) ? otherValue() : otherValue;
const valid = value == null || value === undefined || value === other;

if (valid) {
setState('VALID');
} else {
setState('INVALID', {
equalsTo: {
details: {
otherValue: other,
currentValue: value
}
}
});
}
};
}
9 changes: 9 additions & 0 deletions packages/platform/src/lib/validators/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export * from './equals-to';
export * from './length';
export * from './max';
export * from './max-length';
export * from './min';
export * from './min-length';
export * from './pattern';
export * from './required';
export * from './required-true';
21 changes: 21 additions & 0 deletions packages/platform/src/lib/validators/length.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { SetValidationState, ValidatorFn } from '../validation';

export function length(length: number): ValidatorFn<string | Array<unknown>> {
return (value: string | Array<unknown>, setState: SetValidationState) => {
const valid =
value === null || value === undefined || value.length === length;

if (valid) {
setState('VALID');
} else {
setState('INVALID', {
length: {
details: {
currentLength: value.length,
length: length
}
}
});
}
};
}
21 changes: 21 additions & 0 deletions packages/platform/src/lib/validators/max-length.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { SetValidationState, ValidatorFn } from '../validation';

export function maxLength(length: number): ValidatorFn<string | Array<unknown>> {
return (value: string | Array<unknown>, setState: SetValidationState) => {
const valid =
value === null || value === undefined || value.length <= length;

if (valid) {
setState('VALID');
} else {
setState('INVALID', {
minLength: {
details: {
currentLength: value.length,
maxLength: length
}
}
});
}
};
}
21 changes: 21 additions & 0 deletions packages/platform/src/lib/validators/max.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { SetValidationState, ValidatorFn } from '../validation';

export function max(maxValue: number): ValidatorFn<string | Array<unknown>> {
return (value: number | unknown, setState: SetValidationState) => {
const valid =
value === null || value === undefined || typeof value !== 'number' || value <= maxValue;

if (valid) {
setState('VALID');
} else {
setState('INVALID', {
max: {
details: {
current: value,
max: maxValue
}
}
});
}
};
}
21 changes: 21 additions & 0 deletions packages/platform/src/lib/validators/min-length.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { SetValidationState, ValidatorFn } from '../validation';

export function minLength(length: number): ValidatorFn<string | Array<unknown>> {
return (value: string | Array<unknown>, setState: SetValidationState) => {
const valid =
value === null || value === undefined || value.length >= length;

if (valid) {
setState('VALID');
} else {
setState('INVALID', {
minLength: {
details: {
currentLength: value.length,
minLength: length
}
}
});
}
};
}
21 changes: 21 additions & 0 deletions packages/platform/src/lib/validators/min.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { SetValidationState, ValidatorFn } from '../validation';

export function min(minValue: number): ValidatorFn<string | Array<unknown>> {
return (value: number | unknown, setState: SetValidationState) => {
const valid =
value === null || value === undefined || typeof value !== 'number' || value >= minValue;

if (valid) {
setState('VALID');
} else {
setState('INVALID', {
min: {
details: {
current: value,
min: minValue
}
}
});
}
};
}
12 changes: 12 additions & 0 deletions packages/platform/src/lib/validators/pattern.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { SetValidationState, ValidatorFn } from '../validation';

export function pattern(regexp: RegExp): ValidatorFn {
return (value: unknown, setState: SetValidationState) => {
const valid = value === null || value === undefined || typeof value !== 'string' || regexp.test(value);
if (valid) {
setState('VALID');
} else {
setState('INVALID', { pattern: { details: { pattern: regexp, currentValue: value } } });
}
};
}
12 changes: 12 additions & 0 deletions packages/platform/src/lib/validators/required-true.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { SetValidationState, ValidatorFn } from '../validation';

export function requiredTrue(): ValidatorFn {
return (value: unknown, setState: SetValidationState) => {
const valid = value === true;
if (valid) {
setState('VALID');
} else {
setState('INVALID', { requiredTrue: { details: true } });
}
};
}
12 changes: 12 additions & 0 deletions packages/platform/src/lib/validators/required.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { SetValidationState, ValidatorFn } from '../validation';

export function required(): ValidatorFn {
return (value: unknown, setState: SetValidationState) => {
const valid = value !== null && value !== undefined && value !== '';
if (valid) {
setState('VALID');
} else {
setState('INVALID', { required: { details: true } });
}
};
}

0 comments on commit 6dc1a3f

Please sign in to comment.