Skip to content

Commit

Permalink
Merge pull request #1803 from riganti/fix-validation-in-dynamic
Browse files Browse the repository at this point in the history
JS: fix VM validation inside dynamic object
  • Loading branch information
tomasherceg committed Apr 27, 2024
2 parents afc34cd + 97c19a8 commit 29d9df2
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 2 deletions.
Expand Up @@ -115,6 +115,16 @@ initDotvvm({
"C": 4,
"D": 8,
}
},
tValidated: {
type: "object",
properties: {
RegexValidated: {
validationRules: [
{ ruleName: "regularExpression", errorMessage: "Must have even length", parameters: ["^(..)+$"] }
]
}
}
}
}
})
Expand Down
21 changes: 21 additions & 0 deletions src/Framework/Framework/Resources/Scripts/tests/validation.test.ts
Expand Up @@ -3,6 +3,12 @@ import { globalValidationObject as validation, ValidationErrorDescriptor } from
import { createComplexObservableSubViewmodel, createComplexObservableViewmodel, ObservableHierarchy, ObservableSubHierarchy } from "./observableHierarchies"
import { getErrors } from "../validation/error"
import { setLogger } from "../utils/logging";
import { runClientSideValidation } from '../validation/validation'
import dotvvm from '../dotvvm-root'
import { getStateManager } from "../dotvvm-base";
import { StateManager } from "../state-manager";

require("./stateManagement.data")


describe("DotVVM.Validation - public API", () => {
Expand Down Expand Up @@ -319,6 +325,21 @@ describe("DotVVM.Validation - public API", () => {
})
});


describe("DotVVM.Validation - view model validation", () => {
const s = getStateManager() as StateManager<any>
test("Validated object in dynamic", () => {
dotvvm.updateState(x => ({...x, Dynamic: { something: "abc", validatedObj: { $type: "tValidated", RegexValidated: "abcd" } } }))
s.doUpdateNow()
runClientSideValidation(s.stateObservable, {} as any)
expect(dotvvm.validation.errors).toHaveLength(0)
s.patchState({ Dynamic: { validatedObj: { RegexValidated: "abcde" }}})
s.doUpdateNow()
runClientSideValidation(s.stateObservable, {} as any)
expect(dotvvm.validation.errors).toHaveLength(1)
})
})

function SetupComplexObservableViewmodelWithErrorsOnProp1AndProp21() {
validation.removeErrors("/");
const vm = createComplexObservableViewmodel();
Expand Down
Expand Up @@ -66,7 +66,7 @@ const createValidationHandler = (pathFunction: (context: KnockoutBindingContext)
}
})

const runClientSideValidation = (validationTarget: any, options: PostbackOptions) => {
export const runClientSideValidation = (validationTarget: any, options: PostbackOptions) => {

watchAndTriggerValidationErrorChanged(options,
() => {
Expand Down Expand Up @@ -171,6 +171,9 @@ function validateViewModel(viewModel: any, path: string): void {
}

function validateRecursive(observable: KnockoutObservable<any>, propertyValue: any, type: TypeDefinition, propertyPath: string) {
if (compileConstants.debug && !ko.isObservable(observable)) {
throw Error(`Property ${propertyPath} isn't a knockout observable and cannot be validated.`)
}
const lastSetError: CoerceResult = (observable as any)[lastSetErrorSymbol];
if (lastSetError && lastSetError.isError) {
ValidationError.attach(lastSetError.message, propertyPath, observable);
Expand Down Expand Up @@ -203,7 +206,7 @@ function validateRecursive(observable: KnockoutObservable<any>, propertyValue: a
validateViewModel(propertyValue, propertyPath);
} else {
for (const k of keys(propertyValue)) {
validateRecursive(ko.unwrap(propertyValue[k]), propertyValue[k], { type: "dynamic" }, propertyPath + "/" + k);
validateRecursive(propertyValue[k], ko.unwrap(propertyValue[k]), { type: "dynamic" }, propertyPath + "/" + k);
}
}
}
Expand Down

0 comments on commit 29d9df2

Please sign in to comment.