Using Custom validations in forms.
anirudhr95 opened this issue · 3 comments
Hello, I came across a couple of Github issues which go over this topic - but I still face an issue in what I'm trying to do. I want a few fields in my form to be required depending on the value of other fields. (Conditionally validate i.e)
I am using:
"ngrx-forms": "4.1.0",
"@ngrx/effects": "^7.0.0",
"@ngrx/store": "^7.0.0",
"@ngrx/store-devtools": "^9.2.0",
I have a
FrameSpecForm.ts
import {createFormGroupState, FormGroupState, updateGroup, validate} from 'ngrx-forms';
import {orientation} from "./Orientation";
import {required} from "ngrx-forms/validation";
import {ProhibitionType} from "./ProhibitionType";
export interface FrameSpecForm {
id: string,
type: string,
format: string,
digitalMotionType:string,
minAdDuration:number,
maxAdDuration:number,
shareOfTime:number,
transitMedia:string,
lattitude: number,
longitude: number,
address: string,
zipCode: string,
city: string,
state: string,
region:string
notes: string
}
export const FrameSpecFormUniqueId = '3124';
export const frameSpecFormInitialState: FormGroupState<FrameSpecForm> = createFormGroupState(FrameSpecFormUniqueId, {
id: '',
type: '',
format: '',
digitalMotionType:'',
minAdDuration:undefined,
maxAdDuration:undefined,
shareOfTime:undefined,
transitMedia:'No',
lattitude: undefined,
longitude: undefined,
address: '',
zipCode: '',
city: '',
state: '',
notes: '',
region:'',
});
In my reducer, I am trying to validate this -
import {
FormGroupState,
formGroupReducer,
Actions,
updateGroup,
validate,
StateUpdateFns
} from 'ngrx-forms';
import { frameSpecFormInitialState, FrameSpecForm } from 'src/app/models/frame-spec-form';
import {filterValues} from "./FrameSpecFilterFetch.reducer";
import {required} from "ngrx-forms/validation";
interface AppState {
frameStateSpec: FormGroupState<FrameSpecForm>;
}
const initialState: AppState = {
frameStateSpec: frameSpecFormInitialState
};
const frameSpecFormValidation = updateGroup<FrameSpecForm>({
id: validate(required),
type: validate(required),
format:validate(required),
digitalMotionType: (digitalMotionType, formState) => {
const isValidationRequired = formState.value.type === 'DIGITAL';
const validationFunction = isValidationRequired ? required : () => ({});
return validate(validationFunction);
}
// This is the validation I want.
// If I comment this out, everything works fine.
}
export function FrameSpecificationReducer(
state: AppState = initialState,
action: Actions<any>
): AppState {
const frameStateSpec = frameSpecFormValidation(formGroupReducer(state.frameStateSpec, action));
if (frameStateSpec !== state.frameStateSpec) {
state.frameStateSpec = frameStateSpec;
}
switch (action.type) {
default: return { ...state };
}
}
This is the error I get
ERROR in src/app/create-frame-page/reducers/FrameSpecification.reducer.ts(33,3): error TS2345: Argument of type '{ id: (state: AbstractControlState<string>) => FormControlState<string>; type: (state: AbstractControlState<string>) => FormControlState<string>; format: (state: AbstractControlState<string>) => FormControlState<...>; digitalMotionType: (digitalMotionType: FormControlState<...>, formState: FormGroupState<...>) => (s...' is not assignable to parameter of type 'StateUpdateFns<FrameSpecForm>[]'.
Object literal may only specify known properties, and 'id' does not exist in type 'StateUpdateFns<FrameSpecForm>[]'.
Note: If I instead use this
digitalMotionType: (digitalMotionType, formState) => {
const isValidationRequired = formState.value.type === 'DIGITAL';
const validationFunction = isValidationRequired ? required : () => ({});
return validate(validationFunction, digitalMotionType);
}
I get this error:
ERROR in src/app/create-frame-page/reducers/FrameSpecification.reducer.ts(39,21): error TS2345: Argument of type '(<T>(value: T | Boxed<T>) => ValidationErrors) | (() => {})' is not assignable to parameter of type 'AbstractControlState<{}>'.
Type '<T>(value: T | Boxed<T>) => ValidationErrors' is missing the following properties from type 'AbstractControlState<{}>': id, value, isValid, isInvalid, and 12 more.
Edit #2: There is another clarification made by the author after I closed the post using this comment. You probably want to look at that.
Closing this issue.
The problem was this:
If you read this, please know it is out of date: #5
instead of returning an empty function, you should be returning the state. The latest documentation/clarification is given here by @MrWolfZ himself - #126
Therefore, the right way to code this is:
const frameSpecFormValidation = updateGroup<FrameSpecForm>({
id: validate(required),
type: validate(required),
format: validate(required),
digitalMotionType: (digitalMotionType, formState) => {
const isValidationRequired = formState.value.type === 'Digital';
return isValidationRequired ? validate(digitalMotionType, required) : digitalMotionType;
}
});
TADA!
@anirudhr95 your original code was almost correct, it just needed a minor adjustment to work. You flipped the order of parameters for validate
, i.e. this works:
digitalMotionType: (digitalMotionType, formState) => {
const isValidationRequired = formState.value.type === 'DIGITAL';
const validationFunction = isValidationRequired ? required : () => ({});
return validate(digitalMotionType, validationFunction);
},
That said, both approach work, but do slightly different things. The first approach (i.e. using a validation function that returns {}
) will clear all validation errors if the type
is not DIGITAL
, while the second version will leave any existing validation errors on the digitalMotionType
control. Usually you would probably want the former.
@MrWolfZ Thanks for the clarification. My requirement is the former case as you guessed. I would have never figured out the issue myself if I went ahead thinking my code is working the way I wanted it to.
Also, thanks for your reply in such a short time.