From f1d7c0fde564268714f2137f3f62b121d893bfad Mon Sep 17 00:00:00 2001 From: pedroestabruxelas Date: Thu, 28 May 2026 17:34:57 +0200 Subject: [PATCH 1/5] angular: add option to toggle off errors without user interaction --- .../example/app/app.component.ts | 17 ++- .../angular/src/library/abstract-control.ts | 23 ++-- packages/angular/src/library/index.ts | 20 +-- .../src/library/jsonforms-root.component.ts | 29 +++- .../angular/src/library/jsonforms.config.ts | 62 +++++++++ .../src/examples/show-errors-immediately.ts | 125 ++++++++++++++++++ packages/examples/src/index.ts | 2 + 7 files changed, 254 insertions(+), 24 deletions(-) create mode 100644 packages/angular/src/library/jsonforms.config.ts create mode 100644 packages/examples/src/examples/show-errors-immediately.ts diff --git a/packages/angular-material/example/app/app.component.ts b/packages/angular-material/example/app/app.component.ts index 24b3aaea58..78e90d2b4e 100644 --- a/packages/angular-material/example/app/app.component.ts +++ b/packages/angular-material/example/app/app.component.ts @@ -22,15 +22,15 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -import { Component } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { Component } from '@angular/core'; import { JsonFormsModule } from '@jsonforms/angular'; -import { ExampleDescription, getExamples } from '@jsonforms/examples'; import { JsonFormsI18nState, UISchemaElement, UISchemaTester, } from '@jsonforms/core'; +import { ExampleDescription, getExamples } from '@jsonforms/examples'; import { angularMaterialRenderers } from '../../src/library'; const uiSchema = { @@ -79,6 +79,9 @@ const itemTester: UISchemaTester = (_schema, schemaPath, _path) => { + { [renderers]="renderers" [i18n]="i18n" [readonly]="readonly" + [config]="config" > `, imports: [CommonModule, JsonFormsModule], @@ -97,6 +101,7 @@ export class AppComponent { selectedExample: ExampleDescription | undefined; i18n: JsonFormsI18nState; readonly = false; + config = { showErrorsImmediately: false }; data: any; uischemas: { tester: UISchemaTester; uischema: UISchemaElement }[] = [ { tester: itemTester, uischema: uiSchema }, @@ -111,6 +116,7 @@ export class AppComponent { this.selectedExample = this.examples.find( (e) => e.name === ev.target.value ); + this.i18n = this.selectedExample?.i18n ?? defaultI18n; } @@ -121,4 +127,11 @@ export class AppComponent { toggleReadonly() { this.readonly = !this.readonly; } + + toggleShowErrorsImmediately() { + this.config = { + ...this.config, + showErrorsImmediately: !this.config.showErrorsImmediately, + }; + } } diff --git a/packages/angular/src/library/abstract-control.ts b/packages/angular/src/library/abstract-control.ts index 08f2b806c5..420ff7051b 100644 --- a/packages/angular/src/library/abstract-control.ts +++ b/packages/angular/src/library/abstract-control.ts @@ -22,6 +22,13 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +import { Component, inject, Input, OnDestroy, OnInit } from '@angular/core'; +import { + AbstractControl, + FormControl, + ValidationErrors, + ValidatorFn, +} from '@angular/forms'; import { Actions, computeLabel, @@ -32,17 +39,11 @@ import { removeId, StatePropsOfControl, } from '@jsonforms/core'; -import { Component, inject, Input, OnDestroy, OnInit } from '@angular/core'; -import { - AbstractControl, - FormControl, - ValidationErrors, - ValidatorFn, -} from '@angular/forms'; - import { JsonFormsBaseRenderer } from './base.renderer'; import { JsonFormsAngularService } from './jsonforms.service'; + import merge from 'lodash/merge'; + @Component({ template: '', }) @@ -176,7 +177,11 @@ export abstract class JsonFormsAbstractControl< protected triggerValidation() { // these cause the correct update of the error underline, seems to be // related to ionic-team/ionic#11640 - this.form.markAsTouched(); + const config = this.jsonFormsService.getConfig(); + const appliedUiSchemaOptions = merge({}, config, this.uischema?.options); + if (appliedUiSchemaOptions.showErrorsImmediately) { + this.form.markAsTouched(); + } this.form.updateValueAndValidity(); } } diff --git a/packages/angular/src/library/index.ts b/packages/angular/src/library/index.ts index c68de6b6b5..8341620952 100644 --- a/packages/angular/src/library/index.ts +++ b/packages/angular/src/library/index.ts @@ -1,19 +1,19 @@ /* The MIT License - + Copyright (c) 2017-2020 EclipseSource Munich https://github.com/eclipsesource/jsonforms - + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -22,12 +22,16 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +export * from './abstract-control'; +export * from './array-control'; export * from './base.renderer'; export * from './control'; -export * from './array-control'; +export * from './jsonforms-root.component'; export * from './jsonforms.component'; +export { + JsonFormsAngularConfig, + provideJsonFormsConfig, +} from './jsonforms.config'; export * from './jsonforms.module'; -export * from './unknown.component'; export * from './jsonforms.service'; -export * from './jsonforms-root.component'; -export * from './abstract-control'; +export * from './unknown.component'; diff --git a/packages/angular/src/library/jsonforms-root.component.ts b/packages/angular/src/library/jsonforms-root.component.ts index 62a4ce55d7..bb4108054f 100644 --- a/packages/angular/src/library/jsonforms-root.component.ts +++ b/packages/angular/src/library/jsonforms-root.component.ts @@ -36,6 +36,7 @@ import { } from '@angular/core'; import { Actions, + defaultMiddleware, JsonFormsI18nState, JsonFormsRendererRegistryEntry, JsonSchema, @@ -43,13 +44,18 @@ import { UISchemaElement, UISchemaTester, ValidationMode, - defaultMiddleware, } from '@jsonforms/core'; import type Ajv from 'ajv'; import type { ErrorObject } from 'ajv'; -import { JsonFormsAngularService, USE_STATE_VALUE } from './jsonforms.service'; +import merge from 'lodash/merge'; import { Subscription } from 'rxjs'; import { JsonFormsOutlet } from './jsonforms.component'; +import { + angularConfigDefault, + JSONFORMS_CONFIG, + JsonFormsAngularConfig, +} from './jsonforms.config'; +import { JsonFormsAngularService, USE_STATE_VALUE } from './jsonforms.service'; // TODO Can this be rewritten to not use DoCheck and OnChanges? /* eslint-disable @angular-eslint/no-conflicting-lifecycle */ @@ -69,7 +75,7 @@ export class JsonForms implements DoCheck, OnChanges, OnInit, OnDestroy { @Input() readonly: boolean; @Input() validationMode: ValidationMode; @Input() ajv: Ajv; - @Input() config: any; + @Input() config: JsonFormsAngularConfig; @Input() i18n: JsonFormsI18nState; @Input() additionalErrors: ErrorObject[]; @Input() middleware: Middleware = defaultMiddleware; @@ -83,6 +89,7 @@ export class JsonForms implements DoCheck, OnChanges, OnInit, OnDestroy { oldI18N: JsonFormsI18nState; private jsonformsService = inject(JsonFormsAngularService); + private providedConfig = inject(JSONFORMS_CONFIG, { optional: true }); ngOnInit(): void { this.jsonformsService.init( @@ -98,7 +105,12 @@ export class JsonForms implements DoCheck, OnChanges, OnInit, OnDestroy { uischemas: this.uischemas, i18n: this.i18n, renderers: this.renderers, - config: this.config, + config: merge( + {}, + angularConfigDefault, + this.providedConfig, + this.config + ), readonly: this.readonly, }, this.middleware @@ -202,7 +214,14 @@ export class JsonForms implements DoCheck, OnChanges, OnInit, OnDestroy { if (newConfig && !newConfig.isFirstChange()) { this.jsonformsService.updateConfig( - Actions.setConfig(newConfig.currentValue) + Actions.setConfig( + merge( + {}, + angularConfigDefault, + this.providedConfig, + newConfig.currentValue + ) + ) ); } } diff --git a/packages/angular/src/library/jsonforms.config.ts b/packages/angular/src/library/jsonforms.config.ts new file mode 100644 index 0000000000..dc2c428e39 --- /dev/null +++ b/packages/angular/src/library/jsonforms.config.ts @@ -0,0 +1,62 @@ +/* + The MIT License + + Copyright (c) 2017-2020 EclipseSource Munich + https://github.com/eclipsesource/jsonforms + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ +import { InjectionToken, Provider } from '@angular/core'; + +export interface JsonFormsAngularConfig { + /** + * When true (default), validation error indicators are shown immediately without + * requiring the user to focus or interact with the field first + */ + showErrorsImmediately?: boolean; + [key: string]: unknown; +} + +export const angularConfigDefault: JsonFormsAngularConfig = { + showErrorsImmediately: true, +}; + +/** + * Injection token for providing a global JSONForms config via Angular's DI system. + * Config provided via this token acts as the base; the `config` @Input on the + * `` component takes precedence when both are supplied. + */ +export const JSONFORMS_CONFIG = new InjectionToken( + 'JsonFormsConfig' +); + +/** + * Returns an Angular provider that sets the global JSONForms config. + * + * @example + * // Standalone bootstrap (app.config.ts) + * export const appConfig: ApplicationConfig = { + * providers: [provideJsonFormsConfig({ showErrorsImmediately: false })], + * }; + */ +export function provideJsonFormsConfig( + config: JsonFormsAngularConfig +): Provider { + return { provide: JSONFORMS_CONFIG, useValue: config }; +} diff --git a/packages/examples/src/examples/show-errors-immediately.ts b/packages/examples/src/examples/show-errors-immediately.ts new file mode 100644 index 0000000000..5722845036 --- /dev/null +++ b/packages/examples/src/examples/show-errors-immediately.ts @@ -0,0 +1,125 @@ +/* + The MIT License + + Copyright (c) 2017-2026 EclipseSource Munich + https://github.com/eclipsesource/jsonforms + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ +import { registerExamples } from '../register'; + +export const schema = { + type: 'object', + properties: { + name: { + type: 'string', + minLength: 1, + }, + email: { + type: 'string', + format: 'email', + }, + password: { + type: 'string', + format: 'password', + minLength: 8, + }, + age: { + type: 'integer', + minimum: 0, + }, + rating: { + type: 'number', + minimum: 0, + maximum: 5, + }, + birthDate: { + type: 'string', + format: 'date', + }, + role: { + type: 'string', + enum: ['admin', 'editor', 'viewer'], + }, + notes: { + type: 'string', + maxLength: 500, + }, + }, + required: [ + 'name', + 'email', + 'password', + 'age', + 'rating', + 'birthDate', + 'role', + 'notes', + ], +}; + +export const uischema = { + type: 'VerticalLayout', + elements: [ + { + type: 'HorizontalLayout', + elements: [ + { type: 'Control', scope: '#/properties/name' }, + { type: 'Control', scope: '#/properties/email' }, + ], + }, + { + type: 'HorizontalLayout', + elements: [ + { type: 'Control', scope: '#/properties/password' }, + { type: 'Control', scope: '#/properties/birthDate' }, + ], + }, + { + type: 'HorizontalLayout', + elements: [ + { type: 'Control', scope: '#/properties/age' }, + { type: 'Control', scope: '#/properties/rating' }, + ], + }, + { + type: 'HorizontalLayout', + elements: [ + { type: 'Control', scope: '#/properties/role' }, + { + type: 'Control', + scope: '#/properties/notes', + options: { multi: true }, + }, + ], + }, + ], +}; + +export const data = {}; + +registerExamples([ + { + name: 'show-errors-immediately', + label: 'Show Errors Immediately', + data, + schema, + uischema, + }, +]); diff --git a/packages/examples/src/index.ts b/packages/examples/src/index.ts index f8fdc044df..f31827837a 100644 --- a/packages/examples/src/index.ts +++ b/packages/examples/src/index.ts @@ -76,6 +76,7 @@ import * as additionalErrors from './examples/additional-errors'; import * as multiEnumWithLabelAndDesc from './examples/enum-multi-with-label-and-desc'; import * as additionalProperties from './examples/additional-properties'; import * as login from './examples/login'; +import * as showErrorsImmediately from './examples/show-errors-immediately'; import * as mixed from './examples/mixed'; import * as mixedObject from './examples/mixed-object'; import * as string from './examples/string'; @@ -144,6 +145,7 @@ export { additionalErrors, additionalProperties, login, + showErrorsImmediately, mixed, mixedObject, issue_1884, From f193a813975122f9b5efb1aeccad88411a86f419 Mon Sep 17 00:00:00 2001 From: pedroestabruxelas Date: Fri, 29 May 2026 11:21:54 +0200 Subject: [PATCH 2/5] rename config parameter to showErrorsOnTouch --- .../example/app/app.component.ts | 6 +- .../angular/src/library/abstract-control.ts | 3 +- .../angular/src/library/jsonforms.config.ts | 8 +- ...immediately.ts => show-errors-on-touch.ts} | 4 +- packages/examples/src/index.ts | 156 +++++++++--------- 5 files changed, 89 insertions(+), 88 deletions(-) rename packages/examples/src/examples/{show-errors-immediately.ts => show-errors-on-touch.ts} (97%) diff --git a/packages/angular-material/example/app/app.component.ts b/packages/angular-material/example/app/app.component.ts index 78e90d2b4e..daa46102a6 100644 --- a/packages/angular-material/example/app/app.component.ts +++ b/packages/angular-material/example/app/app.component.ts @@ -80,7 +80,7 @@ const itemTester: UISchemaTester = (_schema, schemaPath, _path) => { {{ readonly ? 'Unset' : 'Set' }} Readonly ( * @example * // Standalone bootstrap (app.config.ts) * export const appConfig: ApplicationConfig = { - * providers: [provideJsonFormsConfig({ showErrorsImmediately: false })], + * providers: [provideJsonFormsConfig({ showErrorsOnTouch: false })], * }; */ export function provideJsonFormsConfig( diff --git a/packages/examples/src/examples/show-errors-immediately.ts b/packages/examples/src/examples/show-errors-on-touch.ts similarity index 97% rename from packages/examples/src/examples/show-errors-immediately.ts rename to packages/examples/src/examples/show-errors-on-touch.ts index 5722845036..3bd57ad7d7 100644 --- a/packages/examples/src/examples/show-errors-immediately.ts +++ b/packages/examples/src/examples/show-errors-on-touch.ts @@ -116,8 +116,8 @@ export const data = {}; registerExamples([ { - name: 'show-errors-immediately', - label: 'Show Errors Immediately', + name: 'show-errors-on-touch', + label: 'Show Errors On Touch', data, schema, uischema, diff --git a/packages/examples/src/index.ts b/packages/examples/src/index.ts index f31827837a..49eef6d5c3 100644 --- a/packages/examples/src/index.ts +++ b/packages/examples/src/index.ts @@ -22,135 +22,135 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +import * as issue_1884 from './examples/1884'; +import * as issue_1948 from './examples/1948'; +import * as additionalErrors from './examples/additional-errors'; +import * as additionalProperties from './examples/additional-properties'; import * as allOf from './examples/allOf'; import * as anyOf from './examples/anyOf'; -import * as oneOf from './examples/oneOf'; -import * as oneOfArray from './examples/oneOfArray'; import * as anyOfOneOfAllOfResolve from './examples/anyOf-oneOf-allOf-resolve'; import * as array from './examples/arrays'; -import * as arrayI18n from './examples/arraysI18n'; -import * as nestedArray from './examples/nestedArrays'; -import * as nestedCategorization from './examples/nestedCategorization'; +import * as arrayWithCustomChildLabel from './examples/arrays-with-custom-element-label'; +import * as arrayWithDefaults from './examples/arrays-with-defaults'; import * as arrayWithDetail from './examples/arrays-with-detail'; import * as arrayWithDetailAndRule from './examples/arrays-with-detail-and-rule'; -import * as arrayWithCustomChildLabel from './examples/arrays-with-custom-element-label'; import * as arrayWithSorting from './examples/arrays-with-sorting'; import * as arrayWithTranslatedCustomChildLabel from './examples/arrays-with-translated-custom-element-label'; -import * as arrayWithDefaults from './examples/arrays-with-defaults'; -import * as stringArray from './examples/stringArray'; +import * as arrayI18n from './examples/arraysI18n'; import * as categorization from './examples/categorization'; import * as stepper from './examples/categorization-stepper'; import * as steppershownav from './examples/categorization-stepper-nav-buttons'; +import * as conditionalSchemaComposition from './examples/conditional-schema-compositions'; +import * as config from './examples/config'; import * as controlOptions from './examples/control-options'; import * as dates from './examples/dates'; -import * as generateDynamic from './examples/generate-dynamic'; -import * as generateSchema from './examples/generate'; -import * as generateUISchema from './examples/generateUI'; -import * as layout from './examples/layout'; -import * as person from './examples/person'; -import * as issue_1884 from './examples/1884'; -import * as rule from './examples/rule'; -import * as ruleInheritance from './examples/ruleInheritance'; -import * as config from './examples/config'; -import * as text from './examples/text'; -import * as numbers from './examples/numbers'; -import * as scope from './examples/scope'; -import * as listWithDetail from './examples/list-with-detail'; -import * as listWithDetailRegistered from './examples/list-with-detail-registered'; -import * as object from './examples/object'; -import * as i18n from './examples/i18n'; -import * as issue_1948 from './examples/1948'; -import * as oneOfRecursive from './examples/oneOf-recursive'; -import * as huge from './examples/huge'; import * as defaultExample from './examples/default'; -import * as onChange from './examples/onChange'; import * as enumExample from './examples/enum'; -import * as radioGroupExample from './examples/radioGroup'; import * as multiEnum from './examples/enum-multi'; +import * as multiEnumWithLabelAndDesc from './examples/enum-multi-with-label-and-desc'; import * as enumI18n from './examples/enumI18n'; import * as enumInArray from './examples/enumInArray'; -import * as readonly from './examples/readonly'; +import * as generateSchema from './examples/generate'; +import * as generateDynamic from './examples/generate-dynamic'; +import * as generateUISchema from './examples/generateUI'; +import * as huge from './examples/huge'; +import * as i18n from './examples/i18n'; +import * as layout from './examples/layout'; +import * as listWithDetail from './examples/list-with-detail'; import * as listWithDetailPrimitives from './examples/list-with-detail-primitives'; -import * as conditionalSchemaComposition from './examples/conditional-schema-compositions'; -import * as additionalErrors from './examples/additional-errors'; -import * as multiEnumWithLabelAndDesc from './examples/enum-multi-with-label-and-desc'; -import * as additionalProperties from './examples/additional-properties'; +import * as listWithDetailRegistered from './examples/list-with-detail-registered'; import * as login from './examples/login'; -import * as showErrorsImmediately from './examples/show-errors-immediately'; import * as mixed from './examples/mixed'; import * as mixedObject from './examples/mixed-object'; -import * as string from './examples/string'; +import * as nestedArray from './examples/nestedArrays'; +import * as nestedCategorization from './examples/nestedCategorization'; +import * as numbers from './examples/numbers'; +import * as object from './examples/object'; +import * as onChange from './examples/onChange'; +import * as oneOf from './examples/oneOf'; +import * as oneOfRecursive from './examples/oneOf-recursive'; +import * as oneOfArray from './examples/oneOfArray'; +import * as person from './examples/person'; import * as prependAppendSlots from './examples/prepend-append-slots'; +import * as radioGroupExample from './examples/radioGroup'; +import * as readonly from './examples/readonly'; +import * as rule from './examples/rule'; +import * as ruleInheritance from './examples/ruleInheritance'; +import * as scope from './examples/scope'; +import * as showErrorsOnTouch from './examples/show-errors-on-touch'; +import * as string from './examples/string'; +import * as stringArray from './examples/stringArray'; +import * as text from './examples/text'; import * as validationNestedSameName from './examples/validation-nested-same-name'; -export * from './register'; export * from './example'; +export * from './register'; import * as ifThenElse from './examples/if_then_else'; -import * as jsonschema from './examples/jsonschema'; import * as jsoneditor from './examples/json-editor'; +import * as jsonschema from './examples/jsonschema'; export { - issue_1948, - defaultExample, + additionalErrors, + additionalProperties, allOf, anyOf, - oneOf, - oneOfArray, anyOfOneOfAllOfResolve, - stringArray, array, arrayI18n, - nestedArray, - nestedCategorization, + arrayWithCustomChildLabel, + arrayWithDefaults, arrayWithDetail, arrayWithDetailAndRule, - arrayWithCustomChildLabel, arrayWithSorting, arrayWithTranslatedCustomChildLabel, categorization, - stepper, - steppershownav, + conditionalSchemaComposition, + config, controlOptions, - generateSchema, - generateUISchema, - layout, - person, - rule, - ruleInheritance, dates, + defaultExample, + enumExample, + enumI18n, + enumInArray, generateDynamic, - config, - text, - numbers, - scope, - listWithDetail, - listWithDetailRegistered, - object, - i18n, - oneOfRecursive, + generateSchema, + generateUISchema, huge, + i18n, ifThenElse, + issue_1884, + issue_1948, jsoneditor, jsonschema, - onChange, - enumExample, - radioGroupExample, - multiEnum, - multiEnumWithLabelAndDesc, - enumI18n, - enumInArray, - readonly, + layout, + listWithDetail, listWithDetailPrimitives, - conditionalSchemaComposition, - additionalErrors, - additionalProperties, + listWithDetailRegistered, login, - showErrorsImmediately, mixed, mixedObject, - issue_1884, - arrayWithDefaults, - string, + multiEnum, + multiEnumWithLabelAndDesc, + nestedArray, + nestedCategorization, + numbers, + object, + onChange, + oneOf, + oneOfArray, + oneOfRecursive, + person, prependAppendSlots, + radioGroupExample, + readonly, + rule, + ruleInheritance, + scope, + showErrorsOnTouch, + stepper, + steppershownav, + string, + stringArray, + text, validationNestedSameName, }; From 8ee6d2df7560152b4b762cda042eb73747016a94 Mon Sep 17 00:00:00 2001 From: pedroestabruxelas Date: Fri, 29 May 2026 12:13:10 +0200 Subject: [PATCH 3/5] export all --- packages/angular/src/library/index.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/angular/src/library/index.ts b/packages/angular/src/library/index.ts index 8341620952..e5213023f2 100644 --- a/packages/angular/src/library/index.ts +++ b/packages/angular/src/library/index.ts @@ -28,10 +28,7 @@ export * from './base.renderer'; export * from './control'; export * from './jsonforms-root.component'; export * from './jsonforms.component'; -export { - JsonFormsAngularConfig, - provideJsonFormsConfig, -} from './jsonforms.config'; +export * from './jsonforms.config'; export * from './jsonforms.module'; export * from './jsonforms.service'; export * from './unknown.component'; From 010dd8d294cf2934c6ea3e64987621fca569f3fc Mon Sep 17 00:00:00 2001 From: pedroestabruxelas Date: Fri, 29 May 2026 15:12:05 +0200 Subject: [PATCH 4/5] fix prop description, remove form example --- .../example/app/app.component.ts | 4 +- .../src/library/jsonforms-root.component.ts | 20 +-- .../angular/src/library/jsonforms.config.ts | 7 +- .../src/examples/show-errors-on-touch.ts | 125 ------------------ packages/examples/src/index.ts | 2 - 5 files changed, 6 insertions(+), 152 deletions(-) delete mode 100644 packages/examples/src/examples/show-errors-on-touch.ts diff --git a/packages/angular-material/example/app/app.component.ts b/packages/angular-material/example/app/app.component.ts index daa46102a6..af34983e00 100644 --- a/packages/angular-material/example/app/app.component.ts +++ b/packages/angular-material/example/app/app.component.ts @@ -79,7 +79,7 @@ const itemTester: UISchemaTester = (_schema, schemaPath, _path) => { - @@ -128,7 +128,7 @@ export class AppComponent { this.readonly = !this.readonly; } - toggleShowErrorsImmediately() { + toggleShowErrorsOnTouch() { this.config = { ...this.config, showErrorsOnTouch: !this.config.showErrorsOnTouch, diff --git a/packages/angular/src/library/jsonforms-root.component.ts b/packages/angular/src/library/jsonforms-root.component.ts index bb4108054f..c93cd05e4b 100644 --- a/packages/angular/src/library/jsonforms-root.component.ts +++ b/packages/angular/src/library/jsonforms-root.component.ts @@ -50,11 +50,7 @@ import type { ErrorObject } from 'ajv'; import merge from 'lodash/merge'; import { Subscription } from 'rxjs'; import { JsonFormsOutlet } from './jsonforms.component'; -import { - angularConfigDefault, - JSONFORMS_CONFIG, - JsonFormsAngularConfig, -} from './jsonforms.config'; +import { JSONFORMS_CONFIG, JsonFormsAngularConfig } from './jsonforms.config'; import { JsonFormsAngularService, USE_STATE_VALUE } from './jsonforms.service'; // TODO Can this be rewritten to not use DoCheck and OnChanges? @@ -105,12 +101,7 @@ export class JsonForms implements DoCheck, OnChanges, OnInit, OnDestroy { uischemas: this.uischemas, i18n: this.i18n, renderers: this.renderers, - config: merge( - {}, - angularConfigDefault, - this.providedConfig, - this.config - ), + config: merge({}, this.providedConfig, this.config), readonly: this.readonly, }, this.middleware @@ -215,12 +206,7 @@ export class JsonForms implements DoCheck, OnChanges, OnInit, OnDestroy { if (newConfig && !newConfig.isFirstChange()) { this.jsonformsService.updateConfig( Actions.setConfig( - merge( - {}, - angularConfigDefault, - this.providedConfig, - newConfig.currentValue - ) + merge({}, this.providedConfig, newConfig.currentValue) ) ); } diff --git a/packages/angular/src/library/jsonforms.config.ts b/packages/angular/src/library/jsonforms.config.ts index 30ef13c526..c627e554cf 100644 --- a/packages/angular/src/library/jsonforms.config.ts +++ b/packages/angular/src/library/jsonforms.config.ts @@ -26,17 +26,12 @@ import { InjectionToken, Provider } from '@angular/core'; export interface JsonFormsAngularConfig { /** - * When true, validation error indicators are shown immediately without - * requiring the user to focus or interact with the field first + * When true, markAsTouched() is skipped, so errors only appear after the user actually interacts with the field. */ showErrorsOnTouch?: boolean; [key: string]: unknown; } -export const angularConfigDefault: JsonFormsAngularConfig = { - showErrorsOnTouch: false, -}; - /** * Injection token for providing a global JSONForms config via Angular's DI system. * Config provided via this token acts as the base; the `config` @Input on the diff --git a/packages/examples/src/examples/show-errors-on-touch.ts b/packages/examples/src/examples/show-errors-on-touch.ts deleted file mode 100644 index 3bd57ad7d7..0000000000 --- a/packages/examples/src/examples/show-errors-on-touch.ts +++ /dev/null @@ -1,125 +0,0 @@ -/* - The MIT License - - Copyright (c) 2017-2026 EclipseSource Munich - https://github.com/eclipsesource/jsonforms - - Permission is hereby granted, free of charge, to any person obtaining a copy - of this software and associated documentation files (the "Software"), to deal - in the Software without restriction, including without limitation the rights - to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - copies of the Software, and to permit persons to whom the Software is - furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - THE SOFTWARE. -*/ -import { registerExamples } from '../register'; - -export const schema = { - type: 'object', - properties: { - name: { - type: 'string', - minLength: 1, - }, - email: { - type: 'string', - format: 'email', - }, - password: { - type: 'string', - format: 'password', - minLength: 8, - }, - age: { - type: 'integer', - minimum: 0, - }, - rating: { - type: 'number', - minimum: 0, - maximum: 5, - }, - birthDate: { - type: 'string', - format: 'date', - }, - role: { - type: 'string', - enum: ['admin', 'editor', 'viewer'], - }, - notes: { - type: 'string', - maxLength: 500, - }, - }, - required: [ - 'name', - 'email', - 'password', - 'age', - 'rating', - 'birthDate', - 'role', - 'notes', - ], -}; - -export const uischema = { - type: 'VerticalLayout', - elements: [ - { - type: 'HorizontalLayout', - elements: [ - { type: 'Control', scope: '#/properties/name' }, - { type: 'Control', scope: '#/properties/email' }, - ], - }, - { - type: 'HorizontalLayout', - elements: [ - { type: 'Control', scope: '#/properties/password' }, - { type: 'Control', scope: '#/properties/birthDate' }, - ], - }, - { - type: 'HorizontalLayout', - elements: [ - { type: 'Control', scope: '#/properties/age' }, - { type: 'Control', scope: '#/properties/rating' }, - ], - }, - { - type: 'HorizontalLayout', - elements: [ - { type: 'Control', scope: '#/properties/role' }, - { - type: 'Control', - scope: '#/properties/notes', - options: { multi: true }, - }, - ], - }, - ], -}; - -export const data = {}; - -registerExamples([ - { - name: 'show-errors-on-touch', - label: 'Show Errors On Touch', - data, - schema, - uischema, - }, -]); diff --git a/packages/examples/src/index.ts b/packages/examples/src/index.ts index 49eef6d5c3..9bd232955f 100644 --- a/packages/examples/src/index.ts +++ b/packages/examples/src/index.ts @@ -77,7 +77,6 @@ import * as readonly from './examples/readonly'; import * as rule from './examples/rule'; import * as ruleInheritance from './examples/ruleInheritance'; import * as scope from './examples/scope'; -import * as showErrorsOnTouch from './examples/show-errors-on-touch'; import * as string from './examples/string'; import * as stringArray from './examples/stringArray'; import * as text from './examples/text'; @@ -146,7 +145,6 @@ export { rule, ruleInheritance, scope, - showErrorsOnTouch, stepper, steppershownav, string, From 1b6081394db1cb7cf0b07c389dfb90f338d9bf16 Mon Sep 17 00:00:00 2001 From: Stefan Dirix Date: Fri, 29 May 2026 15:58:02 +0200 Subject: [PATCH 5/5] Update packages/angular/src/library/jsonforms.config.ts --- packages/angular/src/library/jsonforms.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/angular/src/library/jsonforms.config.ts b/packages/angular/src/library/jsonforms.config.ts index c627e554cf..57db0e3af3 100644 --- a/packages/angular/src/library/jsonforms.config.ts +++ b/packages/angular/src/library/jsonforms.config.ts @@ -26,7 +26,7 @@ import { InjectionToken, Provider } from '@angular/core'; export interface JsonFormsAngularConfig { /** - * When true, markAsTouched() is skipped, so errors only appear after the user actually interacts with the field. + * When true, validation errors only appear after the user has interacted with the field. */ showErrorsOnTouch?: boolean; [key: string]: unknown;