@ajsf/core
Version:
Angular JSON Schema Form builder core
730 lines • 106 kB
JavaScript
import cloneDeep from 'lodash/cloneDeep';
import isEqual from 'lodash/isEqual';
import { ChangeDetectionStrategy, Component, EventEmitter, forwardRef, Input, Output, } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { convertSchemaToDraft6 } from './shared/convert-schema-to-draft6.function';
import { forEach, hasOwn } from './shared/utility.functions';
import { hasValue, inArray, isArray, isEmpty, isObject } from './shared/validator.functions';
import { JsonPointer } from './shared/jsonpointer.functions';
import { JsonSchemaFormService } from './json-schema-form.service';
import { resolveSchemaReferences } from './shared/json-schema.functions';
import * as i0 from "@angular/core";
import * as i1 from "./framework-library/framework-library.service";
import * as i2 from "./widget-library/widget-library.service";
import * as i3 from "./json-schema-form.service";
import * as i4 from "@angular/common";
import * as i5 from "@angular/forms";
import * as i6 from "./widget-library/root.component";
export const JSON_SCHEMA_FORM_VALUE_ACCESSOR = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => JsonSchemaFormComponent),
multi: true,
};
/**
* @module 'JsonSchemaFormComponent' - Angular JSON Schema Form
*
* Root module of the Angular JSON Schema Form client-side library,
* an Angular library which generates an HTML form from a JSON schema
* structured data model and/or a JSON Schema Form layout description.
*
* This library also validates input data by the user, using both validators on
* individual controls to provide real-time feedback while the user is filling
* out the form, and then validating the entire input against the schema when
* the form is submitted to make sure the returned JSON data object is valid.
*
* This library is similar to, and mostly API compatible with:
*
* - JSON Schema Form's Angular Schema Form library for AngularJs
* http://schemaform.io
* http://schemaform.io/examples/bootstrap-example.html (examples)
*
* - Mozilla's react-jsonschema-form library for React
* https://github.com/mozilla-services/react-jsonschema-form
* https://mozilla-services.github.io/react-jsonschema-form (examples)
*
* - Joshfire's JSON Form library for jQuery
* https://github.com/joshfire/jsonform
* http://ulion.github.io/jsonform/playground (examples)
*
* This library depends on:
* - Angular (obviously) https://angular.io
* - lodash, JavaScript utility library https://github.com/lodash/lodash
* - ajv, Another JSON Schema validator https://github.com/epoberezkin/ajv
*
* In addition, the Example Playground also depends on:
* - brace, Browserified Ace editor http://thlorenz.github.io/brace
*/
export class JsonSchemaFormComponent {
constructor(changeDetector, frameworkLibrary, widgetLibrary, jsf) {
this.changeDetector = changeDetector;
this.frameworkLibrary = frameworkLibrary;
this.widgetLibrary = widgetLibrary;
this.jsf = jsf;
// TODO: quickfix to avoid subscribing twice to the same emitters
this.unsubscribeOnActivateForm$ = new Subject();
this.formValueSubscription = null;
this.formInitialized = false;
this.objectWrap = false; // Is non-object input schema wrapped in an object?
this.previousInputs = {
schema: null, layout: null, data: null, options: null, framework: null,
widgets: null, form: null, model: null, JSONSchema: null, UISchema: null,
formData: null, loadExternalAssets: null, debug: null,
};
// Outputs
this.onChanges = new EventEmitter(); // Live unvalidated internal form data
this.onSubmit = new EventEmitter(); // Complete validated form data
this.isValid = new EventEmitter(); // Is current data valid?
this.validationErrors = new EventEmitter(); // Validation errors (if any)
this.formSchema = new EventEmitter(); // Final schema used to create form
this.formLayout = new EventEmitter(); // Final layout used to create form
// Outputs for possible 2-way data binding
// Only the one input providing the initial form data will be bound.
// If there is no inital data, input '{}' to activate 2-way data binding.
// There is no 2-way binding if inital data is combined inside the 'form' input.
this.dataChange = new EventEmitter();
this.modelChange = new EventEmitter();
this.formDataChange = new EventEmitter();
this.ngModelChange = new EventEmitter();
}
get value() {
return this.objectWrap ? this.jsf.data['1'] : this.jsf.data;
}
set value(value) {
this.setFormValues(value, false);
}
resetScriptsAndStyleSheets() {
document.querySelectorAll('.ajsf').forEach(element => element.remove());
}
loadScripts() {
const scripts = this.frameworkLibrary.getFrameworkScripts();
scripts.map(script => {
const scriptTag = document.createElement('script');
scriptTag.src = script;
scriptTag.type = 'text/javascript';
scriptTag.async = true;
scriptTag.setAttribute('class', 'ajsf');
document.getElementsByTagName('head')[0].appendChild(scriptTag);
});
}
loadStyleSheets() {
const stylesheets = this.frameworkLibrary.getFrameworkStylesheets();
stylesheets.map(stylesheet => {
const linkTag = document.createElement('link');
linkTag.rel = 'stylesheet';
linkTag.href = stylesheet;
linkTag.setAttribute('class', 'ajsf');
document.getElementsByTagName('head')[0].appendChild(linkTag);
});
}
loadAssets() {
this.resetScriptsAndStyleSheets();
this.loadScripts();
this.loadStyleSheets();
}
ngOnInit() {
this.updateForm();
this.loadAssets();
}
ngOnChanges(changes) {
this.updateForm();
// Check if there's changes in Framework then load assets if that's the
if (changes.framework) {
if (!changes.framework.isFirstChange() &&
(changes.framework.previousValue !== changes.framework.currentValue)) {
this.loadAssets();
}
}
}
writeValue(value) {
this.setFormValues(value, false);
if (!this.formValuesInput) {
this.formValuesInput = 'ngModel';
}
}
registerOnChange(fn) {
this.onChange = fn;
}
registerOnTouched(fn) {
this.onTouched = fn;
}
setDisabledState(isDisabled) {
if (this.jsf.formOptions.formDisabled !== !!isDisabled) {
this.jsf.formOptions.formDisabled = !!isDisabled;
this.initializeForm();
}
}
updateForm() {
if (!this.formInitialized || !this.formValuesInput ||
(this.language && this.language !== this.jsf.language)) {
this.initializeForm();
}
else {
if (this.language && this.language !== this.jsf.language) {
this.jsf.setLanguage(this.language);
}
// Get names of changed inputs
let changedInput = Object.keys(this.previousInputs)
.filter(input => this.previousInputs[input] !== this[input]);
let resetFirst = true;
if (changedInput.length === 1 && changedInput[0] === 'form' &&
this.formValuesInput.startsWith('form.')) {
// If only 'form' input changed, get names of changed keys
changedInput = Object.keys(this.previousInputs.form || {})
.filter(key => !isEqual(this.previousInputs.form[key], this.form[key]))
.map(key => `form.${key}`);
resetFirst = false;
}
// If only input values have changed, update the form values
if (changedInput.length === 1 && changedInput[0] === this.formValuesInput) {
if (this.formValuesInput.indexOf('.') === -1) {
this.setFormValues(this[this.formValuesInput], resetFirst);
}
else {
const [input, key] = this.formValuesInput.split('.');
this.setFormValues(this[input][key], resetFirst);
}
// If anything else has changed, re-render the entire form
}
else if (changedInput.length) {
this.initializeForm();
if (this.onChange) {
this.onChange(this.jsf.formValues);
}
if (this.onTouched) {
this.onTouched(this.jsf.formValues);
}
}
// Update previous inputs
Object.keys(this.previousInputs)
.filter(input => this.previousInputs[input] !== this[input])
.forEach(input => this.previousInputs[input] = this[input]);
}
}
setFormValues(formValues, resetFirst = true) {
if (formValues) {
const newFormValues = this.objectWrap ? formValues['1'] : formValues;
if (!this.jsf.formGroup) {
this.jsf.formValues = formValues;
this.activateForm();
}
else if (resetFirst) {
this.jsf.formGroup.reset();
}
if (this.jsf.formGroup) {
this.jsf.formGroup.patchValue(newFormValues);
}
if (this.onChange) {
this.onChange(newFormValues);
}
if (this.onTouched) {
this.onTouched(newFormValues);
}
}
else {
this.jsf.formGroup.reset();
}
}
submitForm() {
const validData = this.jsf.validData;
this.onSubmit.emit(this.objectWrap ? validData['1'] : validData);
}
/**
* 'initializeForm' function
*
* - Update 'schema', 'layout', and 'formValues', from inputs.
*
* - Create 'schemaRefLibrary' and 'schemaRecursiveRefMap'
* to resolve schema $ref links, including recursive $ref links.
*
* - Create 'dataRecursiveRefMap' to resolve recursive links in data
* and corectly set output formats for recursively nested values.
*
* - Create 'layoutRefLibrary' and 'templateRefLibrary' to store
* new layout nodes and formGroup elements to use when dynamically
* adding form components to arrays and recursive $ref points.
*
* - Create 'dataMap' to map the data to the schema and template.
*
* - Create the master 'formGroupTemplate' then from it 'formGroup'
* the Angular formGroup used to control the reactive form.
*/
initializeForm() {
if (this.schema || this.layout || this.data || this.form || this.model ||
this.JSONSchema || this.UISchema || this.formData || this.ngModel ||
this.jsf.data) {
this.jsf.resetAllValues(); // Reset all form values to defaults
this.initializeOptions(); // Update options
this.initializeSchema(); // Update schema, schemaRefLibrary,
// schemaRecursiveRefMap, & dataRecursiveRefMap
this.initializeLayout(); // Update layout, layoutRefLibrary,
this.initializeData(); // Update formValues
this.activateForm(); // Update dataMap, templateRefLibrary,
// formGroupTemplate, formGroup
// Uncomment individual lines to output debugging information to console:
// (These always work.)
// console.log('loading form...');
// console.log('schema', this.jsf.schema);
// console.log('layout', this.jsf.layout);
// console.log('options', this.options);
// console.log('formValues', this.jsf.formValues);
// console.log('formGroupTemplate', this.jsf.formGroupTemplate);
// console.log('formGroup', this.jsf.formGroup);
// console.log('formGroup.value', this.jsf.formGroup.value);
// console.log('schemaRefLibrary', this.jsf.schemaRefLibrary);
// console.log('layoutRefLibrary', this.jsf.layoutRefLibrary);
// console.log('templateRefLibrary', this.jsf.templateRefLibrary);
// console.log('dataMap', this.jsf.dataMap);
// console.log('arrayMap', this.jsf.arrayMap);
// console.log('schemaRecursiveRefMap', this.jsf.schemaRecursiveRefMap);
// console.log('dataRecursiveRefMap', this.jsf.dataRecursiveRefMap);
// Uncomment individual lines to output debugging information to browser:
// (These only work if the 'debug' option has also been set to 'true'.)
if (this.debug || this.jsf.formOptions.debug) {
const vars = [];
// vars.push(this.jsf.schema);
// vars.push(this.jsf.layout);
// vars.push(this.options);
// vars.push(this.jsf.formValues);
// vars.push(this.jsf.formGroup.value);
// vars.push(this.jsf.formGroupTemplate);
// vars.push(this.jsf.formGroup);
// vars.push(this.jsf.schemaRefLibrary);
// vars.push(this.jsf.layoutRefLibrary);
// vars.push(this.jsf.templateRefLibrary);
// vars.push(this.jsf.dataMap);
// vars.push(this.jsf.arrayMap);
// vars.push(this.jsf.schemaRecursiveRefMap);
// vars.push(this.jsf.dataRecursiveRefMap);
this.debugOutput = vars.map(v => JSON.stringify(v, null, 2)).join('\n');
}
this.formInitialized = true;
}
}
/**
* 'initializeOptions' function
*
* Initialize 'options' (global form options) and set framework
* Combine available inputs:
* 1. options - recommended
* 2. form.options - Single input style
*/
initializeOptions() {
if (this.language && this.language !== this.jsf.language) {
this.jsf.setLanguage(this.language);
}
this.jsf.setOptions({ debug: !!this.debug });
let loadExternalAssets = this.loadExternalAssets || false;
let framework = this.framework || 'default';
if (isObject(this.options)) {
this.jsf.setOptions(this.options);
loadExternalAssets = this.options.loadExternalAssets || loadExternalAssets;
framework = this.options.framework || framework;
}
if (isObject(this.form) && isObject(this.form.options)) {
this.jsf.setOptions(this.form.options);
loadExternalAssets = this.form.options.loadExternalAssets || loadExternalAssets;
framework = this.form.options.framework || framework;
}
if (isObject(this.widgets)) {
this.jsf.setOptions({ widgets: this.widgets });
}
this.frameworkLibrary.setLoadExternalAssets(loadExternalAssets);
this.frameworkLibrary.setFramework(framework);
this.jsf.framework = this.frameworkLibrary.getFramework();
if (isObject(this.jsf.formOptions.widgets)) {
for (const widget of Object.keys(this.jsf.formOptions.widgets)) {
this.widgetLibrary.registerWidget(widget, this.jsf.formOptions.widgets[widget]);
}
}
if (isObject(this.form) && isObject(this.form.tpldata)) {
this.jsf.setTpldata(this.form.tpldata);
}
}
/**
* 'initializeSchema' function
*
* Initialize 'schema'
* Use first available input:
* 1. schema - recommended / Angular Schema Form style
* 2. form.schema - Single input / JSON Form style
* 3. JSONSchema - React JSON Schema Form style
* 4. form.JSONSchema - For testing single input React JSON Schema Forms
* 5. form - For testing single schema-only inputs
*
* ... if no schema input found, the 'activateForm' function, below,
* will make two additional attempts to build a schema
* 6. If layout input - build schema from layout
* 7. If data input - build schema from data
*/
initializeSchema() {
// TODO: update to allow non-object schemas
if (isObject(this.schema)) {
this.jsf.AngularSchemaFormCompatibility = true;
this.jsf.schema = cloneDeep(this.schema);
}
else if (hasOwn(this.form, 'schema') && isObject(this.form.schema)) {
this.jsf.schema = cloneDeep(this.form.schema);
}
else if (isObject(this.JSONSchema)) {
this.jsf.ReactJsonSchemaFormCompatibility = true;
this.jsf.schema = cloneDeep(this.JSONSchema);
}
else if (hasOwn(this.form, 'JSONSchema') && isObject(this.form.JSONSchema)) {
this.jsf.ReactJsonSchemaFormCompatibility = true;
this.jsf.schema = cloneDeep(this.form.JSONSchema);
}
else if (hasOwn(this.form, 'properties') && isObject(this.form.properties)) {
this.jsf.schema = cloneDeep(this.form);
}
else if (isObject(this.form)) {
// TODO: Handle other types of form input
}
if (!isEmpty(this.jsf.schema)) {
// If other types also allowed, render schema as an object
if (inArray('object', this.jsf.schema.type)) {
this.jsf.schema.type = 'object';
}
// Wrap non-object schemas in object.
if (hasOwn(this.jsf.schema, 'type') && this.jsf.schema.type !== 'object') {
this.jsf.schema = {
'type': 'object',
'properties': { 1: this.jsf.schema }
};
this.objectWrap = true;
}
else if (!hasOwn(this.jsf.schema, 'type')) {
// Add type = 'object' if missing
if (isObject(this.jsf.schema.properties) ||
isObject(this.jsf.schema.patternProperties) ||
isObject(this.jsf.schema.additionalProperties)) {
this.jsf.schema.type = 'object';
// Fix JSON schema shorthand (JSON Form style)
}
else {
this.jsf.JsonFormCompatibility = true;
this.jsf.schema = {
'type': 'object',
'properties': this.jsf.schema
};
}
}
// If needed, update JSON Schema to draft 6 format, including
// draft 3 (JSON Form style) and draft 4 (Angular Schema Form style)
this.jsf.schema = convertSchemaToDraft6(this.jsf.schema);
// Initialize ajv and compile schema
this.jsf.compileAjvSchema();
// Create schemaRefLibrary, schemaRecursiveRefMap, dataRecursiveRefMap, & arrayMap
this.jsf.schema = resolveSchemaReferences(this.jsf.schema, this.jsf.schemaRefLibrary, this.jsf.schemaRecursiveRefMap, this.jsf.dataRecursiveRefMap, this.jsf.arrayMap);
if (hasOwn(this.jsf.schemaRefLibrary, '')) {
this.jsf.hasRootReference = true;
}
// TODO: (?) Resolve external $ref links
// // Create schemaRefLibrary & schemaRecursiveRefMap
// this.parser.bundle(this.schema)
// .then(schema => this.schema = resolveSchemaReferences(
// schema, this.jsf.schemaRefLibrary,
// this.jsf.schemaRecursiveRefMap, this.jsf.dataRecursiveRefMap
// ));
}
}
/**
* 'initializeData' function
*
* Initialize 'formValues'
* defulat or previously submitted values used to populate form
* Use first available input:
* 1. data - recommended
* 2. model - Angular Schema Form style
* 3. form.value - JSON Form style
* 4. form.data - Single input style
* 5. formData - React JSON Schema Form style
* 6. form.formData - For easier testing of React JSON Schema Forms
* 7. (none) no data - initialize data from schema and layout defaults only
*/
initializeData() {
if (hasValue(this.data)) {
this.jsf.formValues = cloneDeep(this.data);
this.formValuesInput = 'data';
}
else if (hasValue(this.model)) {
this.jsf.AngularSchemaFormCompatibility = true;
this.jsf.formValues = cloneDeep(this.model);
this.formValuesInput = 'model';
}
else if (hasValue(this.ngModel)) {
this.jsf.AngularSchemaFormCompatibility = true;
this.jsf.formValues = cloneDeep(this.ngModel);
this.formValuesInput = 'ngModel';
}
else if (isObject(this.form) && hasValue(this.form.value)) {
this.jsf.JsonFormCompatibility = true;
this.jsf.formValues = cloneDeep(this.form.value);
this.formValuesInput = 'form.value';
}
else if (isObject(this.form) && hasValue(this.form.data)) {
this.jsf.formValues = cloneDeep(this.form.data);
this.formValuesInput = 'form.data';
}
else if (hasValue(this.formData)) {
this.jsf.ReactJsonSchemaFormCompatibility = true;
this.formValuesInput = 'formData';
}
else if (hasOwn(this.form, 'formData') && hasValue(this.form.formData)) {
this.jsf.ReactJsonSchemaFormCompatibility = true;
this.jsf.formValues = cloneDeep(this.form.formData);
this.formValuesInput = 'form.formData';
}
else {
this.formValuesInput = null;
}
}
/**
* 'initializeLayout' function
*
* Initialize 'layout'
* Use first available array input:
* 1. layout - recommended
* 2. form - Angular Schema Form style
* 3. form.form - JSON Form style
* 4. form.layout - Single input style
* 5. (none) no layout - set default layout instead
* (full layout will be built later from the schema)
*
* Also, if alternate layout formats are available,
* import from 'UISchema' or 'customFormItems'
* used for React JSON Schema Form and JSON Form API compatibility
* Use first available input:
* 1. UISchema - React JSON Schema Form style
* 2. form.UISchema - For testing single input React JSON Schema Forms
* 2. form.customFormItems - JSON Form style
* 3. (none) no input - don't import
*/
initializeLayout() {
// Rename JSON Form-style 'options' lists to
// Angular Schema Form-style 'titleMap' lists.
const fixJsonFormOptions = (layout) => {
if (isObject(layout) || isArray(layout)) {
forEach(layout, (value, key) => {
if (hasOwn(value, 'options') && isObject(value.options)) {
value.titleMap = value.options;
delete value.options;
}
}, 'top-down');
}
return layout;
};
// Check for layout inputs and, if found, initialize form layout
if (isArray(this.layout)) {
this.jsf.layout = cloneDeep(this.layout);
}
else if (isArray(this.form)) {
this.jsf.AngularSchemaFormCompatibility = true;
this.jsf.layout = cloneDeep(this.form);
}
else if (this.form && isArray(this.form.form)) {
this.jsf.JsonFormCompatibility = true;
this.jsf.layout = fixJsonFormOptions(cloneDeep(this.form.form));
}
else if (this.form && isArray(this.form.layout)) {
this.jsf.layout = cloneDeep(this.form.layout);
}
else {
this.jsf.layout = ['*'];
}
// Check for alternate layout inputs
let alternateLayout = null;
if (isObject(this.UISchema)) {
this.jsf.ReactJsonSchemaFormCompatibility = true;
alternateLayout = cloneDeep(this.UISchema);
}
else if (hasOwn(this.form, 'UISchema')) {
this.jsf.ReactJsonSchemaFormCompatibility = true;
alternateLayout = cloneDeep(this.form.UISchema);
}
else if (hasOwn(this.form, 'uiSchema')) {
this.jsf.ReactJsonSchemaFormCompatibility = true;
alternateLayout = cloneDeep(this.form.uiSchema);
}
else if (hasOwn(this.form, 'customFormItems')) {
this.jsf.JsonFormCompatibility = true;
alternateLayout = fixJsonFormOptions(cloneDeep(this.form.customFormItems));
}
// if alternate layout found, copy alternate layout options into schema
if (alternateLayout) {
JsonPointer.forEachDeep(alternateLayout, (value, pointer) => {
const schemaPointer = pointer
.replace(/\//g, '/properties/')
.replace(/\/properties\/items\/properties\//g, '/items/properties/')
.replace(/\/properties\/titleMap\/properties\//g, '/titleMap/properties/');
if (hasValue(value) && hasValue(pointer)) {
let key = JsonPointer.toKey(pointer);
const groupPointer = (JsonPointer.parse(schemaPointer) || []).slice(0, -2);
let itemPointer;
// If 'ui:order' object found, copy into object schema root
if (key.toLowerCase() === 'ui:order') {
itemPointer = [...groupPointer, 'ui:order'];
// Copy other alternate layout options to schema 'x-schema-form',
// (like Angular Schema Form options) and remove any 'ui:' prefixes
}
else {
if (key.slice(0, 3).toLowerCase() === 'ui:') {
key = key.slice(3);
}
itemPointer = [...groupPointer, 'x-schema-form', key];
}
if (JsonPointer.has(this.jsf.schema, groupPointer) &&
!JsonPointer.has(this.jsf.schema, itemPointer)) {
JsonPointer.set(this.jsf.schema, itemPointer, value);
}
}
});
}
}
/**
* 'activateForm' function
*
* ...continued from 'initializeSchema' function, above
* If 'schema' has not been initialized (i.e. no schema input found)
* 6. If layout input - build schema from layout input
* 7. If data input - build schema from data input
*
* Create final layout,
* build the FormGroup template and the Angular FormGroup,
* subscribe to changes,
* and activate the form.
*/
activateForm() {
this.unsubscribeOnActivateForm$.next();
// If 'schema' not initialized
if (isEmpty(this.jsf.schema)) {
// TODO: If full layout input (with no '*'), build schema from layout
// if (!this.jsf.layout.includes('*')) {
// this.jsf.buildSchemaFromLayout();
// } else
// If data input, build schema from data
if (!isEmpty(this.jsf.formValues)) {
this.jsf.buildSchemaFromData();
}
}
if (!isEmpty(this.jsf.schema)) {
// If not already initialized, initialize ajv and compile schema
this.jsf.compileAjvSchema();
// Update all layout elements, add values, widgets, and validators,
// replace any '*' with a layout built from all schema elements,
// and update the FormGroup template with any new validators
this.jsf.buildLayout(this.widgetLibrary);
// Build the Angular FormGroup template from the schema
this.jsf.buildFormGroupTemplate(this.jsf.formValues);
// Build the real Angular FormGroup from the FormGroup template
this.jsf.buildFormGroup();
}
if (this.jsf.formGroup) {
// Reset initial form values
if (!isEmpty(this.jsf.formValues) &&
this.jsf.formOptions.setSchemaDefaults !== true &&
this.jsf.formOptions.setLayoutDefaults !== true) {
this.setFormValues(this.jsf.formValues);
}
// TODO: Figure out how to display calculated values without changing object data
// See http://ulion.github.io/jsonform/playground/?example=templating-values
// Calculate references to other fields
// if (!isEmpty(this.jsf.formGroup.value)) {
// forEach(this.jsf.formGroup.value, (value, key, object, rootObject) => {
// if (typeof value === 'string') {
// object[key] = this.jsf.parseText(value, value, rootObject, key);
// }
// }, 'top-down');
// }
// Subscribe to form changes to output live data, validation, and errors
this.jsf.dataChanges.pipe(takeUntil(this.unsubscribeOnActivateForm$)).subscribe(data => {
this.onChanges.emit(this.objectWrap ? data['1'] : data);
if (this.formValuesInput && this.formValuesInput.indexOf('.') === -1) {
this[`${this.formValuesInput}Change`].emit(this.objectWrap ? data['1'] : data);
}
});
// Trigger change detection on statusChanges to show updated errors
this.jsf.formGroup.statusChanges.pipe(takeUntil(this.unsubscribeOnActivateForm$)).subscribe(() => this.changeDetector.markForCheck());
this.jsf.isValidChanges.pipe(takeUntil(this.unsubscribeOnActivateForm$)).subscribe(isValid => this.isValid.emit(isValid));
this.jsf.validationErrorChanges.pipe(takeUntil(this.unsubscribeOnActivateForm$)).subscribe(err => this.validationErrors.emit(err));
// Output final schema, final layout, and initial data
this.formSchema.emit(this.jsf.schema);
this.formLayout.emit(this.jsf.layout);
this.onChanges.emit(this.objectWrap ? this.jsf.data['1'] : this.jsf.data);
// If validateOnRender, output initial validation and any errors
const validateOnRender = JsonPointer.get(this.jsf, '/formOptions/validateOnRender');
if (validateOnRender) { // validateOnRender === 'auto' || true
const touchAll = (control) => {
if (validateOnRender === true || hasValue(control.value)) {
control.markAsTouched();
}
Object.keys(control.controls || {})
.forEach(key => touchAll(control.controls[key]));
};
touchAll(this.jsf.formGroup);
this.isValid.emit(this.jsf.isValid);
this.validationErrors.emit(this.jsf.ajvErrors);
}
}
}
}
JsonSchemaFormComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.0", ngImport: i0, type: JsonSchemaFormComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i1.FrameworkLibraryService }, { token: i2.WidgetLibraryService }, { token: i3.JsonSchemaFormService }], target: i0.ɵɵFactoryTarget.Component });
JsonSchemaFormComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.2.0", type: JsonSchemaFormComponent, selector: "json-schema-form", inputs: { schema: "schema", layout: "layout", data: "data", options: "options", framework: "framework", widgets: "widgets", form: "form", model: "model", JSONSchema: "JSONSchema", UISchema: "UISchema", formData: "formData", ngModel: "ngModel", language: "language", loadExternalAssets: "loadExternalAssets", debug: "debug", value: "value" }, outputs: { onChanges: "onChanges", onSubmit: "onSubmit", isValid: "isValid", validationErrors: "validationErrors", formSchema: "formSchema", formLayout: "formLayout", dataChange: "dataChange", modelChange: "modelChange", formDataChange: "formDataChange", ngModelChange: "ngModelChange" }, providers: [JsonSchemaFormService, JSON_SCHEMA_FORM_VALUE_ACCESSOR], usesOnChanges: true, ngImport: i0, template: "<form [autocomplete]=\"jsf?.formOptions?.autocomplete ? 'on' : 'off'\" class=\"json-schema-form\" (ngSubmit)=\"submitForm()\">\n <root-widget [layout]=\"jsf?.layout\"></root-widget>\n</form>\n<div *ngIf=\"debug || jsf?.formOptions?.debug\">\n Debug output:\n <pre>{{debugOutput}}</pre>\n</div>", dependencies: [{ kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i5.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i5.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i5.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: i6.RootComponent, selector: "root-widget", inputs: ["dataIndex", "layoutIndex", "layout", "isOrderable", "isFlexItem"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.0", ngImport: i0, type: JsonSchemaFormComponent, decorators: [{
type: Component,
args: [{ selector: 'json-schema-form', changeDetection: ChangeDetectionStrategy.OnPush, providers: [JsonSchemaFormService, JSON_SCHEMA_FORM_VALUE_ACCESSOR], template: "<form [autocomplete]=\"jsf?.formOptions?.autocomplete ? 'on' : 'off'\" class=\"json-schema-form\" (ngSubmit)=\"submitForm()\">\n <root-widget [layout]=\"jsf?.layout\"></root-widget>\n</form>\n<div *ngIf=\"debug || jsf?.formOptions?.debug\">\n Debug output:\n <pre>{{debugOutput}}</pre>\n</div>" }]
}], ctorParameters: function () { return [{ type: i0.ChangeDetectorRef }, { type: i1.FrameworkLibraryService }, { type: i2.WidgetLibraryService }, { type: i3.JsonSchemaFormService }]; }, propDecorators: { schema: [{
type: Input
}], layout: [{
type: Input
}], data: [{
type: Input
}], options: [{
type: Input
}], framework: [{
type: Input
}], widgets: [{
type: Input
}], form: [{
type: Input
}], model: [{
type: Input
}], JSONSchema: [{
type: Input
}], UISchema: [{
type: Input
}], formData: [{
type: Input
}], ngModel: [{
type: Input
}], language: [{
type: Input
}], loadExternalAssets: [{
type: Input
}], debug: [{
type: Input
}], value: [{
type: Input
}], onChanges: [{
type: Output
}], onSubmit: [{
type: Output
}], isValid: [{
type: Output
}], validationErrors: [{
type: Output
}], formSchema: [{
type: Output
}], formLayout: [{
type: Output
}], dataChange: [{
type: Output
}], modelChange: [{
type: Output
}], formDataChange: [{
type: Output
}], ngModelChange: [{
type: Output
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoianNvbi1zY2hlbWEtZm9ybS5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9hanNmLWNvcmUvc3JjL2xpYi9qc29uLXNjaGVtYS1mb3JtLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL2Fqc2YtY29yZS9zcmMvbGliL2pzb24tc2NoZW1hLWZvcm0uY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxTQUFTLE1BQU0sa0JBQWtCLENBQUM7QUFDekMsT0FBTyxPQUFPLE1BQU0sZ0JBQWdCLENBQUM7QUFFckMsT0FBTyxFQUNMLHVCQUF1QixFQUV2QixTQUFTLEVBQ1QsWUFBWSxFQUNaLFVBQVUsRUFDVixLQUFLLEVBR0wsTUFBTSxHQUVQLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFBd0IsaUJBQWlCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUN6RSxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQy9CLE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUMzQyxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSw0Q0FBNEMsQ0FBQztBQUNuRixPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBRTdELE9BQU8sRUFDTCxRQUFRLEVBQ1IsT0FBTyxFQUNQLE9BQU8sRUFDUCxPQUFPLEVBQ1AsUUFBUSxFQUNULE1BQU0sOEJBQThCLENBQUM7QUFDdEMsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBQzdELE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQ25FLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxNQUFNLGdDQUFnQyxDQUFDOzs7Ozs7OztBQUd6RSxNQUFNLENBQUMsTUFBTSwrQkFBK0IsR0FBUTtJQUNs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