From 18b8496a9532ba7161d75ae61aa8c92a517c2562 Mon Sep 17 00:00:00 2001 From: Aaron Roth Date: Sat, 20 Apr 2024 14:09:06 -0400 Subject: [PATCH 1/3] Fixes Ajv breaking changes, adds additional editor enhancements --- projects/swimlane/ngx-ui/CHANGELOG.md | 1 + .../json-editor-flat.component.ts | 16 ++++++++ .../json-editor/json-editor-node.ts | 4 +- .../lib/components/json-editor/json-editor.ts | 35 +++++++++++++++- .../node-types/object-node.component.ts | 2 +- .../json-editor/schema-validator.service.ts | 40 ++++++++++++++++++- .../json-editor-page.component.html | 2 + .../json-editor-page.component.ts | 7 +++- 8 files changed, 99 insertions(+), 8 deletions(-) diff --git a/projects/swimlane/ngx-ui/CHANGELOG.md b/projects/swimlane/ngx-ui/CHANGELOG.md index 9e5069fe3..e05406236 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 ## 47.0.0 (2023-02-12) 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 */ diff --git a/src/app/components/json-editor-page/json-editor-page.component.html b/src/app/components/json-editor-page/json-editor-page.component.html index 7a2dcd067..de9631805 100644 --- a/src/app/components/json-editor-page/json-editor-page.component.html +++ b/src/app/components/json-editor-page/json-editor-page.component.html @@ -8,6 +8,8 @@

JSON Editor

[schema]="jsonEditorSchema" label="Model" [typeCheckOverrides]="typeOverrides" + [additionalFormats]="formats" + [additionalKeywords]="keywords" > diff --git a/src/app/components/json-editor-page/json-editor-page.component.ts b/src/app/components/json-editor-page/json-editor-page.component.ts index ff82b70f5..f2e6cbd0b 100644 --- a/src/app/components/json-editor-page/json-editor-page.component.ts +++ b/src/app/components/json-editor-page/json-editor-page.component.ts @@ -1,4 +1,5 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { Format, KeywordDefinition } from 'ajv'; import { JSONSchema7 } from 'json-schema'; @Component({ @@ -100,7 +101,11 @@ export class JsonEditorPageComponent { }, required: ['productId', 'productName', 'price', 'availability', 'onSale', 'dimensions', 'userApiKey', 'file'] }; - + formats: Map = new Map([ + ['test', 'test'], + ['test2', 'test2'] + ]); + keywords: Array = ['test3', 'test4', 'test5']; hideRoot = false; showKnownProperties = false; passwordToggleEnabled = false; From b8126f276a7ac4be8b17dab33228e264b6c794e3 Mon Sep 17 00:00:00 2001 From: Aaron Roth Date: Sat, 20 Apr 2024 14:28:41 -0400 Subject: [PATCH 2/3] pulling out demo tests --- .../json-editor-page/json-editor-page.component.html | 2 -- .../json-editor-page/json-editor-page.component.ts | 7 +------ 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/app/components/json-editor-page/json-editor-page.component.html b/src/app/components/json-editor-page/json-editor-page.component.html index de9631805..7a2dcd067 100644 --- a/src/app/components/json-editor-page/json-editor-page.component.html +++ b/src/app/components/json-editor-page/json-editor-page.component.html @@ -8,8 +8,6 @@

JSON Editor

[schema]="jsonEditorSchema" label="Model" [typeCheckOverrides]="typeOverrides" - [additionalFormats]="formats" - [additionalKeywords]="keywords" > diff --git a/src/app/components/json-editor-page/json-editor-page.component.ts b/src/app/components/json-editor-page/json-editor-page.component.ts index f2e6cbd0b..ff82b70f5 100644 --- a/src/app/components/json-editor-page/json-editor-page.component.ts +++ b/src/app/components/json-editor-page/json-editor-page.component.ts @@ -1,5 +1,4 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; -import { Format, KeywordDefinition } from 'ajv'; import { JSONSchema7 } from 'json-schema'; @Component({ @@ -101,11 +100,7 @@ export class JsonEditorPageComponent { }, required: ['productId', 'productName', 'price', 'availability', 'onSale', 'dimensions', 'userApiKey', 'file'] }; - formats: Map = new Map([ - ['test', 'test'], - ['test2', 'test2'] - ]); - keywords: Array = ['test3', 'test4', 'test5']; + hideRoot = false; showKnownProperties = false; passwordToggleEnabled = false; From eefd4a17e1944381bbed361dfe6971c1a7c4f496 Mon Sep 17 00:00:00 2001 From: Aaron Roth Date: Sat, 20 Apr 2024 14:32:05 -0400 Subject: [PATCH 3/3] reverted autoformat on import line