diff --git a/projects/swimlane/ngx-ui/CHANGELOG.md b/projects/swimlane/ngx-ui/CHANGELOG.md index 619fb3084..b0e64c899 100644 --- a/projects/swimlane/ngx-ui/CHANGELOG.md +++ b/projects/swimlane/ngx-ui/CHANGELOG.md @@ -3,6 +3,7 @@ ## HEAD (unreleased) - Enhancement(`ngx-calendar`): Supports selecting a range of dates with hours and minutes +- Enhancement(`ngx-json-editor`): Fixes Ajv breaking changes unaccounted for in 47.1.0. Adds enhancements to allow for overriding the default Ajv options, and adding extra formats and keywords ## 48.0.0 (2023-06-17) diff --git a/projects/swimlane/ngx-ui/src/lib/components/json-editor/json-editor-flat/json-editor-flat.component.ts b/projects/swimlane/ngx-ui/src/lib/components/json-editor/json-editor-flat/json-editor-flat.component.ts index b95c2f11a..ef6b15eaa 100644 --- a/projects/swimlane/ngx-ui/src/lib/components/json-editor/json-editor-flat/json-editor-flat.component.ts +++ b/projects/swimlane/ngx-ui/src/lib/components/json-editor/json-editor-flat/json-editor-flat.component.ts @@ -22,6 +22,7 @@ import { } from './json-editor-node-flat/node-types/property-config/property-config.component'; import type { QueryList } from '@angular/core'; +import { Format, InstanceOptions, KeywordDefinition } from 'ajv'; @Component({ selector: 'ngx-json-editor-flat', @@ -51,6 +52,12 @@ export class JsonEditorFlatComponent extends JsonEditor implements OnInit, OnCha @Input() inputControlTemplate: TemplateRef; + @Input() ajvOptions: InstanceOptions; + + @Input() additionalKeywords?: Array; + + @Input() additionalFormats?: Map; + @ContentChildren(JsonEditorNodeFlatComponent) nodeElms: QueryList; @ViewChild('propertyConfigTmpl') propertyConfigTmpl: TemplateRef; @@ -71,6 +78,15 @@ export class JsonEditorFlatComponent extends JsonEditor implements OnInit, OnCha if (this.formats.length && this.schemaBuilderMode) { this.buildCustomFormats(); } + if (!this.schemaValidator && this.ajvOptions) { + this.schemaValidatorService.setAjvOptions(this.ajvOptions); + } + if (this.additionalKeywords) { + this.schemaValidatorService.addAjvKeywords(this.additionalKeywords); + } + if (this.additionalFormats) { + this.schemaValidatorService.addAjvFormats(this.additionalFormats); + } } ngOnChanges(changes: SimpleChanges) { diff --git a/projects/swimlane/ngx-ui/src/lib/components/json-editor/json-editor-node.ts b/projects/swimlane/ngx-ui/src/lib/components/json-editor/json-editor-node.ts index 3707cce50..b4fb9adf4 100644 --- a/projects/swimlane/ngx-ui/src/lib/components/json-editor/json-editor-node.ts +++ b/projects/swimlane/ngx-ui/src/lib/components/json-editor/json-editor-node.ts @@ -159,11 +159,11 @@ export class JsonEditorNode implements OnInit, OnChanges { if (this.errors && this.errors.length) { this.ownErrors = this.errors.filter(e => { - return e.dataPath === this.path; + return e.instancePath === this.path; }); this.childrenErrors = this.errors.filter(e => { - return e.dataPath.startsWith(this.path); + return e.instancePath.startsWith(this.path); }); } this.childrenValid = this.childrenErrors.length === 0; diff --git a/projects/swimlane/ngx-ui/src/lib/components/json-editor/json-editor.ts b/projects/swimlane/ngx-ui/src/lib/components/json-editor/json-editor.ts index 2d7ee91a3..f62823bec 100644 --- a/projects/swimlane/ngx-ui/src/lib/components/json-editor/json-editor.ts +++ b/projects/swimlane/ngx-ui/src/lib/components/json-editor/json-editor.ts @@ -1,10 +1,20 @@ -import { Input, Output, EventEmitter, OnChanges, SimpleChanges, ChangeDetectorRef, Directive } from '@angular/core'; +import { + Input, + Output, + EventEmitter, + OnChanges, + SimpleChanges, + ChangeDetectorRef, + Directive, + OnInit +} from '@angular/core'; import { SchemaValidatorService } from './schema-validator.service'; import { JSONEditorSchema } from './json-editor.helper'; import { debounceable } from '../../decorators/debounceable/debounceable.decorator'; +import { Format, InstanceOptions, KeywordDefinition } from 'ajv'; @Directive() -export class JsonEditor implements OnChanges { +export class JsonEditor implements OnInit, OnChanges { @Input() model: any; @Input() schema: JSONEditorSchema; @@ -17,6 +27,12 @@ export class JsonEditor implements OnChanges { @Input() showKnownProperties = false; + @Input() ajvOptions?: InstanceOptions; + + @Input() additionalKeywords?: Array; + + @Input() additionalFormats?: Map; + @Output() modelChange: EventEmitter = new EventEmitter(); @Output() schemaUpdate: EventEmitter = new EventEmitter(); @@ -25,6 +41,21 @@ export class JsonEditor implements OnChanges { constructor(protected schemaValidatorService: SchemaValidatorService, protected cdr: ChangeDetectorRef) {} + /** + * On component initialization, set Ajv options if provided + */ + ngOnInit() { + if (!this.schemaValidator && this.ajvOptions) { + this.schemaValidatorService.setAjvOptions(this.ajvOptions); + } + if (this.additionalKeywords) { + this.schemaValidatorService.addAjvKeywords(this.additionalKeywords); + } + if (this.additionalFormats) { + this.schemaValidatorService.addAjvFormats(this.additionalFormats); + } + } + ngOnChanges(changes: SimpleChanges) { if (changes.schema) { this.schema = JSON.parse(JSON.stringify(this.schema)); diff --git a/projects/swimlane/ngx-ui/src/lib/components/json-editor/node-types/object-node.component.ts b/projects/swimlane/ngx-ui/src/lib/components/json-editor/node-types/object-node.component.ts index 5ce4de95f..4ee921526 100644 --- a/projects/swimlane/ngx-ui/src/lib/components/json-editor/node-types/object-node.component.ts +++ b/projects/swimlane/ngx-ui/src/lib/components/json-editor/node-types/object-node.component.ts @@ -264,7 +264,7 @@ export class ObjectNode implements OnInit, OnChanges { return `['${propName}']`; } - return `.${propName}`; + return `/${propName}`; } /** diff --git a/projects/swimlane/ngx-ui/src/lib/components/json-editor/schema-validator.service.ts b/projects/swimlane/ngx-ui/src/lib/components/json-editor/schema-validator.service.ts index cd73d74db..c677ce1c4 100644 --- a/projects/swimlane/ngx-ui/src/lib/components/json-editor/schema-validator.service.ts +++ b/projects/swimlane/ngx-ui/src/lib/components/json-editor/schema-validator.service.ts @@ -1,4 +1,5 @@ -import Ajv from 'ajv'; +import Ajv, { Format, InstanceOptions, KeywordDefinition } from 'ajv'; +import addFormats from 'ajv-formats'; import { Injectable } from '@angular/core'; @Injectable({ @@ -6,15 +7,50 @@ import { Injectable } from '@angular/core'; }) export class SchemaValidatorService { ajv = new Ajv({ - allErrors: true + allErrors: true, + strict: true }); constructor() { + addFormats(this.ajv); + this.ajv.addKeyword('$meta'); this.ajv.addFormat('password', '.*'); this.ajv.addFormat('code', '.*'); this.ajv.addFormat('binary', '.*'); } + /** + * Allows for overriding the default set Ajv Options + * @param opts {InstanceOptions} The Ajv options to override + */ + setAjvOptions(opts: InstanceOptions): void { + this.ajv.opts = opts; + } + + /** + * Allows for adding Ajv keywords + * @param keywords {Array} The Ajv keywords to add + */ + addAjvKeywords(keywords: Array): void { + if (Array.isArray(keywords)) { + keywords.forEach((keyword: string | KeywordDefinition) => { + this.ajv.addKeyword(keyword); + }); + } + } + + /** + * Allows for adding Ajv formats + * @param formats {Map} The Ajv formats to add + */ + addAjvFormats(formats: Map): void { + if (formats) { + formats.forEach((formatValue: Format, formatName: string) => { + this.ajv.addFormat(formatName, formatValue); + }); + } + } + /** * Validates schemas of a specified type */