UNPKG

ngx-schema-forms

Version:

New features: - Ajv schema validator. - Angular forms compatible: Property tree is created using FormGroup, FormArray and FormControl classes. - Array now properly loads initial data from model. - WidgetTyep: WidgetRegistry now supports WidgetType, now wo

1,677 lines (1,657 loc) 320 kB
import { ComponentFactoryResolver, Injectable, EventEmitter, Component, Input, forwardRef, ChangeDetectorRef, ViewEncapsulation, TemplateRef, Directive, ViewContainerRef, Injector, NgModule, Output, ElementRef, ContentChildren, SimpleChange } from '@angular/core'; import { __extends, __spread, __decorate, __metadata, __values } from 'tslib'; import { BehaviorSubject, combineLatest, merge } from 'rxjs'; import { map, startWith, filter, distinctUntilChanged } from 'rxjs/operators'; import { FormControl, FormArray, FormGroup, NG_VALUE_ACCESSOR, FormsModule, ReactiveFormsModule } from '@angular/forms'; import * as ZSchema from 'z-schema'; import * as Ajv from 'ajv'; import { CommonModule } from '@angular/common'; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ var ActionRegistry = /** @class */ (function () { function ActionRegistry() { this.actions = {}; } /** * @return {?} */ ActionRegistry.prototype.clear = /** * @return {?} */ function () { this.actions = {}; }; /** * @param {?} actionId * @param {?} action * @return {?} */ ActionRegistry.prototype.register = /** * @param {?} actionId * @param {?} action * @return {?} */ function (actionId, action) { this.actions[actionId] = action; }; /** * @param {?} actionId * @return {?} */ ActionRegistry.prototype.get = /** * @param {?} actionId * @return {?} */ function (actionId) { return this.actions[actionId]; }; return ActionRegistry; }()); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ var ValidatorRegistry = /** @class */ (function () { function ValidatorRegistry() { this.validators = {}; } /** * @param {?} path * @param {?} validator * @return {?} */ ValidatorRegistry.prototype.register = /** * @param {?} path * @param {?} validator * @return {?} */ function (path, validator) { this.validators[path] = validator; }; /** * @param {?} path * @return {?} */ ValidatorRegistry.prototype.get = /** * @param {?} path * @return {?} */ function (path) { return this.validators[path]; }; /** * @return {?} */ ValidatorRegistry.prototype.clear = /** * @return {?} */ function () { this.validators = {}; }; return ValidatorRegistry; }()); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ /** @enum {string} */ var SchemaPropertyType = { String: 'string', Object: 'object', Array: 'array', Boolean: 'boolean', Integer: 'integer', Number: 'number', }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ /** * @abstract */ var /** * @abstract */ SchemaValidatorFactory = /** @class */ (function () { function SchemaValidatorFactory() { } return SchemaValidatorFactory; }()); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ /** @enum {string} */ var WidgetType = { Field: 'field', Fieldset: 'fieldset', Button: 'button', }; var WidgetRegistry = /** @class */ (function () { function WidgetRegistry() { this.widgets = {}; this.defaultWidget = {}; } /** * @param {?} widget * @param {?=} type * @return {?} */ WidgetRegistry.prototype.setDefaultWidget = /** * @param {?} widget * @param {?=} type * @return {?} */ function (widget, type) { if (type === void 0) { type = WidgetType.Field; } this.defaultWidget[type] = widget; }; /** * @param {?=} type * @return {?} */ WidgetRegistry.prototype.getDefaultWidget = /** * @param {?=} type * @return {?} */ function (type) { if (type === void 0) { type = WidgetType.Field; } return this.defaultWidget[type]; }; /** * @param {?} id * @param {?=} type * @return {?} */ WidgetRegistry.prototype.hasWidget = /** * @param {?} id * @param {?=} type * @return {?} */ function (id, type) { if (type === void 0) { type = WidgetType.Field; } if (!this.widgets.hasOwnProperty(type)) { return false; } return this.widgets[type].hasOwnProperty(id); }; /** * @param {?} id * @param {?} widget * @param {?=} type * @return {?} */ WidgetRegistry.prototype.register = /** * @param {?} id * @param {?} widget * @param {?=} type * @return {?} */ function (id, widget, type) { if (type === void 0) { type = WidgetType.Field; } if (!this.widgets.hasOwnProperty(type)) { this.widgets[type] = {}; } this.widgets[type][id] = widget; }; /** * @template T * @param {?} id * @param {?=} type * @return {?} */ WidgetRegistry.prototype.getWidgetType = /** * @template T * @param {?} id * @param {?=} type * @return {?} */ function (id, type) { if (type === void 0) { type = WidgetType.Field; } if (this.hasWidget(id, type)) { return this.widgets[type][id]; } return this.getDefaultWidget(type); }; return WidgetRegistry; }()); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ var WidgetFactory = /** @class */ (function () { function WidgetFactory(widgetRegistry, factoryResolver) { this.widgetRegistry = widgetRegistry; this.factoryResolver = factoryResolver; } /** * @template T * @param {?} container * @param {?} id * @param {?=} opts * @return {?} */ WidgetFactory.prototype.createWidget = /** * @template T * @param {?} container * @param {?} id * @param {?=} opts * @return {?} */ function (container, id, opts) { if (opts === void 0) { opts = { type: WidgetType.Field }; } /** @type {?} */ var componentClass = this.widgetRegistry.getWidgetType(id, opts.type); /** @type {?} */ var componentFactory = this.factoryResolver .resolveComponentFactory(componentClass); return container.createComponent(componentFactory, undefined, // index // index opts.injector); }; WidgetFactory.decorators = [ { type: Injectable } ]; /** @nocollapse */ WidgetFactory.ctorParameters = function () { return [ { type: WidgetRegistry }, { type: ComponentFactoryResolver } ]; }; return WidgetFactory; }()); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ var FormPropertyErrors = /** @class */ (function () { function FormPropertyErrors(errors) { this.errors = errors; } /** * @return {?} */ FormPropertyErrors.prototype.getMessages = /** * @return {?} */ function () { var _this = this; /** @type {?} */ var errorsPaths = Object.keys(this.errors); if (!errorsPaths.length) { return []; } return errorsPaths .reduce(function (messages, path) { /** @type {?} */ var message = _this.errors[path]["message"]; if (!message) { messages.push('Missing validation error "message" for property ' + path); return messages; } messages.push(message); return messages; }, []); }; return FormPropertyErrors; }()); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ /** * @template T * @param {?} Base * @return {?} */ function ControlProperty(Base) { /** * @abstract */ var /** * @abstract */ Property = /** @class */ (function (_super) { __extends(Property, _super); function Property() { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } var _this = _super.apply(this, __spread(args)) || this; _this.nonEmptyValueChanges = new EventEmitter(); _this.visibilityChanges = new BehaviorSubject(true); _this._visible = true; return _this; } Object.defineProperty(Property.prototype, "id", { get: /** * @return {?} */ function () { return this.path.toLowerCase().slice(1).replace(/\//g, '-'); }, enumerable: true, configurable: true }); Object.defineProperty(Property.prototype, "isRoot", { get: /** * @return {?} */ function () { return this === this.root; }, enumerable: true, configurable: true }); Object.defineProperty(Property.prototype, "name", { get: /** * @return {?} */ function () { return this.path.split('/').pop(); }, enumerable: true, configurable: true }); Object.defineProperty(Property.prototype, "visible", { get: /** * @return {?} */ function () { return this._visible; }, enumerable: true, configurable: true }); /** * @return {?} */ Property.prototype.getErrors = /** * @return {?} */ function () { /** @type {?} */ var errors = this.errors; if (!errors) { return null; } return new FormPropertyErrors((_a = {}, _a[this.path] = errors, _a)); var _a; }; /** * @param {?} visible * @param {?=} opts * @return {?} */ Property.prototype.setVisible = /** * @param {?} visible * @param {?=} opts * @return {?} */ function (visible, opts) { if (opts === void 0) { opts = { disable: false }; } this._visible = visible; if (opts.disable) { if (this.visible) { this.enable(); } else { this.disable(); } } this.visibilityChanges.next(this.visible); }; // visible if AT LEAST ONE of the properties it depends on is visible // AND has a value in the list /** * @return {?} */ Property.prototype.bindVisibility = /** * @return {?} */ function () { var _this = this; /** @type {?} */ var visibleIf = this.schema["visibleIf"]; if (visibleIf === undefined) { return; } /** @type {?} */ var paths = Object.keys(visibleIf); if (typeof visibleIf === 'object' && paths.length === 0) { this.setVisible(false); return; } /** @type {?} */ var observables = []; var _loop_1 = function (path) { if (!visibleIf.hasOwnProperty(path)) { return "continue"; } /** @type {?} */ var property = this_1.root.get(path); if (!property) { console.warn("Couldn't find property " + path + " for visibility check of " + this_1.path); return "continue"; } /** @type {?} */ var values = visibleIf[path]; /** @type {?} */ var observable = property.valueChanges.pipe(startWith(values.includes(property.value)), map(function (value) { return values.includes('$ANY$') || values.includes(value); })); observables.push(observable); }; var this_1 = this; try { for (var paths_1 = __values(paths), paths_1_1 = paths_1.next(); !paths_1_1.done; paths_1_1 = paths_1.next()) { var path = paths_1_1.value; _loop_1(path); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (paths_1_1 && !paths_1_1.done && (_a = paths_1.return)) _a.call(paths_1); } finally { if (e_1) throw e_1.error; } } // TODO unsubscribe combineLatest(observables) .subscribe(function (values) { _this.setVisible(values.includes(true)); }); var e_1, _a; }; /** * @param {?} path * @return {?} */ Property.prototype.get = /** * @param {?} path * @return {?} */ function (path) { if (typeof path === 'string' && path.includes('/')) { path = this.normalizePath(path); } return _super.prototype.get.call(this, path); }; /** * @param {?} path * @return {?} */ Property.prototype.normalizePath = /** * @param {?} path * @return {?} */ function (path) { if (path[0] === '/') { path = path.slice(1); } return path.split('/'); }; return Property; }(Base)); return Property; } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ var GenericProperty = /** @class */ (function (_super) { __extends(GenericProperty, _super); function GenericProperty(path, schema) { var _this = _super.call(this, schema["default"]) || this; _this.path = path; _this.schema = schema; return _this; } /** * @return {?} */ GenericProperty.prototype._updateValue = /** * @return {?} */ function () { if (this.value === null || this.value === '') { this.nonEmptyValue = undefined; return; } this.nonEmptyValue = this.value; }; return GenericProperty; }(ControlProperty(FormControl))); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ var NumberProperty = /** @class */ (function (_super) { __extends(NumberProperty, _super); function NumberProperty() { return _super !== null && _super.apply(this, arguments) || this; } /** * @param {?} value * @param {?=} options * @return {?} */ NumberProperty.prototype.setValue = /** * @param {?} value * @param {?=} options * @return {?} */ function (value, options) { if (options === void 0) { options = {}; } _super.prototype.setValue.call(this, +value, options); }; return NumberProperty; }(GenericProperty)); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ var BooleanProperty = /** @class */ (function (_super) { __extends(BooleanProperty, _super); function BooleanProperty() { return _super !== null && _super.apply(this, arguments) || this; } /** * @param {?} value * @param {?=} options * @return {?} */ BooleanProperty.prototype.setValue = /** * @param {?} value * @param {?=} options * @return {?} */ function (value, options) { if (options === void 0) { options = {}; } if (typeof value !== 'boolean') { value = Boolean(value); } _super.prototype.setValue.call(this, value, options); }; return BooleanProperty; }(GenericProperty)); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ var StringProperty = /** @class */ (function (_super) { __extends(StringProperty, _super); function StringProperty() { return _super !== null && _super.apply(this, arguments) || this; } /** * @param {?} value * @param {?=} options * @return {?} */ StringProperty.prototype.setValue = /** * @param {?} value * @param {?=} options * @return {?} */ function (value, options) { if (options === void 0) { options = {}; } if (typeof value !== 'string') { value = "" + value; } _super.prototype.setValue.call(this, value, options); }; return StringProperty; }(GenericProperty)); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ var ArrayProperty = /** @class */ (function (_super) { __extends(ArrayProperty, _super); function ArrayProperty(formPropertyFactory, path, schema) { var _this = _super.call(this, []) || this; _this.formPropertyFactory = formPropertyFactory; _this.path = path; _this.schema = schema; return _this; } /** * @return {?} */ ArrayProperty.prototype._updateValue = /** * @return {?} */ function () { var _this = this; // to avoid ts complaints _super.prototype['_updateValue'].call(this); this.nonEmptyValue = this.controls .filter(function (control) { /** @type {?} */ var enabled = control.enabled || _this.disabled; return control.nonEmptyValue !== undefined && enabled; }) .map(function (control) { return control.value; }); }; /** * @return {?} */ ArrayProperty.prototype.getErrors = /** * @return {?} */ function () { /** @type {?} */ var aggregatedErrors = this.controls .reduce(function (errors, property) { /** @type {?} */ var propertyErrors = property.getErrors(); if (!propertyErrors) { return errors; } return Object.assign(errors, propertyErrors.errors); }, {}); if (this.errors) { aggregatedErrors[this.path] = this.errors; } if (!Object.keys(aggregatedErrors).length) { return null; } return new FormPropertyErrors(aggregatedErrors); }; /** * @param {?} value * @param {?=} options * @return {?} */ ArrayProperty.prototype.patchValue = /** * @param {?} value * @param {?=} options * @return {?} */ function (value, options) { var _this = this; if (options === void 0) { options = {}; } value.forEach(function (newValue, index) { _this.addPropertyAt(index); if (_this.at(index)) { _this.at(index).patchValue(newValue, { onlySelf: true, emitEvent: options.emitEvent }); } }); this.updateValueAndValidity(options); }; /** * @return {?} */ ArrayProperty.prototype.addProperty = /** * @return {?} */ function () { /** @type {?} */ var property = this.getPropertyFromSchemaItems(); _super.prototype.push.call(this, property); property.bindVisibility(); }; /** * @param {?} index * @return {?} */ ArrayProperty.prototype.addPropertyAt = /** * @param {?} index * @return {?} */ function (index) { /** @type {?} */ var property = this.getPropertyFromSchemaItems(); this.insert(index, property); property.bindVisibility(); }; /** * @return {?} */ ArrayProperty.prototype.bindVisibility = /** * @return {?} */ function () { _super.prototype.bindVisibility.call(this); this.controls.forEach(function (control) { control.bindVisibility(); }); }; /** * @param {?} fn * @param {?=} opts * @return {?} */ ArrayProperty.prototype.forEach = /** * @param {?} fn * @param {?=} opts * @return {?} */ function (fn, opts) { if (opts === void 0) { opts = { includeSelf: true }; } if (opts.includeSelf) { fn(this); } try { for (var _a = __values(this.controls), _b = _a.next(); !_b.done; _b = _a.next()) { var control = _b.value; /** @type {?} */ var property = /** @type {?} */ (control); if (property.forEach instanceof Function) { property.forEach(fn, { includeSelf: true }); continue; } fn(property); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (_b && !_b.done && (_c = _a.return)) _c.call(_a); } finally { if (e_1) throw e_1.error; } } var e_1, _c; }; /** * @return {?} */ ArrayProperty.prototype.getPropertyFromSchemaItems = /** * @return {?} */ function () { return this.formPropertyFactory.createProperty(this.schema["items"], this); }; return ArrayProperty; }(ControlProperty(FormArray))); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ var ObjectProperty = /** @class */ (function (_super) { __extends(ObjectProperty, _super); function ObjectProperty(path, schema) { var _this = _super.call(this, {}) || this; _this.path = path; _this.schema = schema; return _this; } /** * @return {?} */ ObjectProperty.prototype._updateValue = /** * @return {?} */ function () { var _this = this; // to avoid ts complaints _super.prototype['_updateValue'].call(this); this.nonEmptyValue = this['_reduceChildren']({}, function (result, control, name) { if (control.nonEmptyValue === undefined) { return result; } if (control.enabled || _this.disabled) { result[name] = control.nonEmptyValue; } return result; }); }; /** * @return {?} */ ObjectProperty.prototype.getErrors = /** * @return {?} */ function () { var _this = this; /** @type {?} */ var aggregatedErrors = Object.keys(this.controls) .reduce(function (errors, key) { /** @type {?} */ var property = /** @type {?} */ (_this.controls[key]); /** @type {?} */ var propertyErrors = property.getErrors(); if (!propertyErrors) { return errors; } return Object.assign(errors, propertyErrors.errors); }, {}); if (this.errors) { aggregatedErrors[this.path] = this.errors; } if (!Object.keys(aggregatedErrors).length) { return null; } return new FormPropertyErrors(aggregatedErrors); }; /** * @return {?} */ ObjectProperty.prototype.bindVisibility = /** * @return {?} */ function () { _super.prototype.bindVisibility.call(this); for (var key in this.controls) { if (this.controls.hasOwnProperty(key)) { (/** @type {?} */ (this.controls[key])).bindVisibility(); } } }; /** * @param {?} fn * @param {?=} opts * @return {?} */ ObjectProperty.prototype.forEach = /** * @param {?} fn * @param {?=} opts * @return {?} */ function (fn, opts) { if (opts === void 0) { opts = { includeSelf: true }; } if (opts.includeSelf) { fn(this); } for (var key in this.controls) { if (this.controls.hasOwnProperty(key)) { /** @type {?} */ var property = (/** @type {?} */ (this.controls[key])); if (property.forEach instanceof Function) { property.forEach(fn, { includeSelf: true }); continue; } fn(property); } } }; return ObjectProperty; }(ControlProperty(FormGroup))); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ /** * @param {?} o * @return {?} */ function isBlank(o) { return o === null || o === undefined; } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ /** * @param {?} message * @param {?} path * @return {?} */ function formatMessage(message, path) { return "Parsing error on " + path + ": " + message; } /** * @param {?} message * @param {?} path * @return {?} */ function schemaError(message, path) { /** @type {?} */ var mesg = formatMessage(message, path); throw new Error(mesg); } /** * @param {?} message * @param {?} path * @return {?} */ function schemaWarning(message, path) { /** @type {?} */ var mesg = formatMessage(message, path); throw new Error(mesg); } var SchemaPreprocessor = /** @class */ (function () { function SchemaPreprocessor() { } /** * @param {?} jsonSchema * @param {?=} path * @return {?} */ SchemaPreprocessor.preprocess = /** * @param {?} jsonSchema * @param {?=} path * @return {?} */ function (jsonSchema, path) { if (path === void 0) { path = '/'; } jsonSchema = jsonSchema || {}; if (jsonSchema.type === 'object') { SchemaPreprocessor.checkProperties(jsonSchema, path); SchemaPreprocessor.checkAndCreateFieldsets(jsonSchema, path); } else if (jsonSchema.type === 'array') { SchemaPreprocessor.checkItems(jsonSchema, path); } SchemaPreprocessor.normalizeWidget(jsonSchema); SchemaPreprocessor.recursiveCheck(jsonSchema, path); }; /** * @param {?} jsonSchema * @param {?} path * @return {?} */ SchemaPreprocessor.checkProperties = /** * @param {?} jsonSchema * @param {?} path * @return {?} */ function (jsonSchema, path) { if (isBlank(jsonSchema.properties)) { jsonSchema.properties = {}; schemaWarning('Provided json schema does not contain a \'properties\' entry. Output schema will be empty', path); } }; /** * @param {?} jsonSchema * @param {?} path * @return {?} */ SchemaPreprocessor.checkAndCreateFieldsets = /** * @param {?} jsonSchema * @param {?} path * @return {?} */ function (jsonSchema, path) { if (jsonSchema.fieldsets === undefined) { if (jsonSchema.order !== undefined) { SchemaPreprocessor.replaceOrderByFieldsets(jsonSchema); } else { SchemaPreprocessor.createFieldsets(jsonSchema); } } SchemaPreprocessor.checkFieldsUsage(jsonSchema, path); }; /** * @param {?} jsonSchema * @param {?} path * @return {?} */ SchemaPreprocessor.checkFieldsUsage = /** * @param {?} jsonSchema * @param {?} path * @return {?} */ function (jsonSchema, path) { /** @type {?} */ var fieldsId = Object.keys(jsonSchema.properties); /** @type {?} */ var usedFields = {}; try { for (var _a = __values(jsonSchema.fieldsets), _b = _a.next(); !_b.done; _b = _a.next()) { var fieldset = _b.value; try { for (var _c = __values(fieldset.fields), _d = _c.next(); !_d.done; _d = _c.next()) { var fieldId = _d.value; if (usedFields[fieldId] === undefined) { usedFields[fieldId] = []; } usedFields[fieldId].push(fieldset.id); } } catch (e_1_1) { e_1 = { error: e_1_1 }; } finally { try { if (_d && !_d.done && (_e = _c.return)) _e.call(_c); } finally { if (e_1) throw e_1.error; } } } } catch (e_2_1) { e_2 = { error: e_2_1 }; } finally { try { if (_b && !_b.done && (_f = _a.return)) _f.call(_a); } finally { if (e_2) throw e_2.error; } } try { for (var fieldsId_1 = __values(fieldsId), fieldsId_1_1 = fieldsId_1.next(); !fieldsId_1_1.done; fieldsId_1_1 = fieldsId_1.next()) { var fieldId = fieldsId_1_1.value; if (usedFields.hasOwnProperty(fieldId)) { if (usedFields[fieldId].length > 1) { schemaError(fieldId + " is referenced by more than one fieldset: " + usedFields[fieldId], path); } delete usedFields[fieldId]; } else if (jsonSchema.required.indexOf(fieldId) > -1) { schemaError(fieldId + " is a required field but it is not referenced as part of a 'order' or a 'fieldset' property", path); } else { delete jsonSchema[fieldId]; schemaWarning("Removing unreferenced field " + fieldId, path); } } } catch (e_3_1) { e_3 = { error: e_3_1 }; } finally { try { if (fieldsId_1_1 && !fieldsId_1_1.done && (_g = fieldsId_1.return)) _g.call(fieldsId_1); } finally { if (e_3) throw e_3.error; } } for (var remainingfieldsId in usedFields) { if (usedFields.hasOwnProperty(remainingfieldsId)) { schemaWarning("Referencing non-existent field " + remainingfieldsId + " in one or more fieldsets", path); } } var e_2, _f, e_1, _e, e_3, _g; }; /** * @param {?} jsonSchema * @return {?} */ SchemaPreprocessor.createFieldsets = /** * @param {?} jsonSchema * @return {?} */ function (jsonSchema) { jsonSchema.order = Object.keys(jsonSchema.properties); SchemaPreprocessor.replaceOrderByFieldsets(jsonSchema); }; /** * @param {?} jsonSchema * @return {?} */ SchemaPreprocessor.replaceOrderByFieldsets = /** * @param {?} jsonSchema * @return {?} */ function (jsonSchema) { jsonSchema.fieldsets = [{ id: 'fieldset-default', title: jsonSchema.title || '', description: jsonSchema.description || '', name: jsonSchema.name || '', fields: jsonSchema.order }]; delete jsonSchema.order; }; /** * @param {?} fieldSchema * @return {?} */ SchemaPreprocessor.normalizeWidget = /** * @param {?} fieldSchema * @return {?} */ function (fieldSchema) { /** @type {?} */ var widget = fieldSchema.widget; if (widget === undefined) { widget = { 'id': fieldSchema.type }; } else if (typeof widget === 'string') { widget = { 'id': widget }; } fieldSchema.widget = widget; }; /** * @param {?} jsonSchema * @param {?} path * @return {?} */ SchemaPreprocessor.checkItems = /** * @param {?} jsonSchema * @param {?} path * @return {?} */ function (jsonSchema, path) { if (jsonSchema.items === undefined) { schemaError('No \'items\' property in array', path); } }; /** * @param {?} jsonSchema * @param {?} path * @return {?} */ SchemaPreprocessor.recursiveCheck = /** * @param {?} jsonSchema * @param {?} path * @return {?} */ function (jsonSchema, path) { if (jsonSchema.type === 'object') { /* for (const fieldId in jsonSchema.properties) { if (jsonSchema.properties.hasOwnProperty(fieldId)) { const fieldSchema = jsonSchema.properties[fieldId]; SchemaPreprocessor.preprocess(fieldSchema, path + fieldId + '/'); } } */ if (jsonSchema.hasOwnProperty('definitions')) { for (var fieldId in jsonSchema.definitions) { if (jsonSchema.definitions.hasOwnProperty(fieldId)) { /** @type {?} */ var fieldSchema = jsonSchema.definitions[fieldId]; SchemaPreprocessor.removeRecursiveRefProperties(fieldSchema, "#/definitions/" + fieldId); // formPropertyFactory recursive is used instead // SchemaPreprocessor.preprocess(fieldSchema, path + fieldId + '/'); } } } } // else if (jsonSchema.type === 'array') { // formPropertyFactory recursive is used instead // SchemaPreprocessor.preprocess(jsonSchema.items, path + '*/'); // } }; /** * @param {?} jsonSchema * @param {?} definitionPath * @return {?} */ SchemaPreprocessor.removeRecursiveRefProperties = /** * @param {?} jsonSchema * @param {?} definitionPath * @return {?} */ function (jsonSchema, definitionPath) { // to avoid infinite loop if (jsonSchema.type === 'object') { for (var fieldId in jsonSchema.properties) { if (jsonSchema.properties.hasOwnProperty(fieldId)) { if (jsonSchema.properties[fieldId].$ref && jsonSchema.properties[fieldId].$ref === definitionPath) { delete jsonSchema.properties[fieldId]; } else if (jsonSchema.properties[fieldId].type === 'object') { SchemaPreprocessor.removeRecursiveRefProperties(jsonSchema.properties[fieldId], definitionPath); } } } } }; return SchemaPreprocessor; }()); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ var FormPropertyFactory = /** @class */ (function () { function FormPropertyFactory(schemaValidatorFactory, validatorRegistry) { this.schemaValidatorFactory = schemaValidatorFactory; this.validatorRegistry = validatorRegistry; } /** * @param {?} schema * @param {?=} propertyParent * @param {?=} propertyKey * @return {?} */ FormPropertyFactory.prototype.createProperty = /** * @param {?} schema * @param {?=} propertyParent * @param {?=} propertyKey * @return {?} */ function (schema, propertyParent, propertyKey) { /** @type {?} */ var property; /** @type {?} */ var path = this.generatePath(propertyParent, propertyKey); SchemaPreprocessor.preprocess(schema, path); // TODO test for parsing for reference schema if (schema["$ref"]) { /** @type {?} */ var refSchema = this.schemaValidatorFactory.getSchema((/** @type {?} */ (propertyParent.root)).schema, schema["$ref"]); property = this.createProperty(refSchema, propertyParent, propertyKey || path); } else { switch (schema["type"]) { case SchemaPropertyType.Integer: case SchemaPropertyType.Number: property = new NumberProperty(path, schema); break; case SchemaPropertyType.String: property = new StringProperty(path, schema); break; case SchemaPropertyType.Boolean: property = new BooleanProperty(path, schema); break; case SchemaPropertyType.Object: property = new ObjectProperty(path, schema); break; case SchemaPropertyType.Array: if (schema["widget"].id === 'array') { property = new ArrayProperty(this, path, schema); } else { schema["default"] = []; property = new GenericProperty(path, schema); } break; default: throw new TypeError("Undefined type " + schema["type"]); } } this.initializeFormProperty(property, propertyParent); return property; }; /** * @param {?} property * @param {?=} propertyParent * @return {?} */ FormPropertyFactory.prototype.initializeFormProperty = /** * @param {?} property * @param {?=} propertyParent * @return {?} */ function (property, propertyParent) { if (propertyParent) { property.setParent(propertyParent); } this.bindCustomValidator(property); if (property instanceof ObjectProperty) { for (var key in property.schema["properties"]) { if (property.schema["properties"].hasOwnProperty(key)) { /** @type {?} */ var _schema = property.schema["properties"][key]; /** @type {?} */ var _property = this.createProperty(_schema, property, key); property.addControl(key, _property); } } } if (property.isRoot) { this.bindSchemaValidator(property); // needs to run after entire property tree is built property.bindVisibility(); } }; /** * @param {?} property * @return {?} */ FormPropertyFactory.prototype.bindSchemaValidator = /** * @param {?} property * @return {?} */ function (property) { /** @type {?} */ var validate = this.schemaValidatorFactory.createValidatorFn(property.schema); // TODO use pipe startWith to do initial run property.valueChanges .pipe(startWith(null)) .subscribe(function () { /** @type {?} */ var value = property.nonEmptyValue; property.nonEmptyValueChanges.emit(value); /** @type {?} */ var errors = validate(value); if (!errors) { return; } Object.keys(errors).forEach(function (path) { /** @type {?} */ var control = property.get(path); if (control) { // set error to specific control control.setErrors(errors[path], { emitEvent: true }); } }); }); }; /** * @param {?} property * @return {?} */ FormPropertyFactory.prototype.bindCustomValidator = /** * @param {?} property * @return {?} */ function (property) { /** @type {?} */ var validators = this.validatorRegistry.get(property.path); if (validators) { property.setValidators(validators); } }; /** * @param {?=} propertyParent * @param {?=} propertyKey * @return {?} */ FormPropertyFactory.prototype.generatePath = /** * @param {?=} propertyParent * @param {?=} propertyKey * @return {?} */ function (propertyParent, propertyKey) { if (!propertyParent) { return '/'; } /** @type {?} */ var path = ''; path += propertyParent.path; if (propertyParent.parent !== undefined) { path += '/'; } switch (propertyParent.schema["type"]) { case SchemaPropertyType.Object: path += propertyKey; break; case SchemaPropertyType.Array: path += (/** @type {?} */ (propertyParent)).controls.length; break; default: // TODO move to class throw new Error('Instantiation of a FormProperty with an unknown parent type: ' + propertyParent.schema["type"]); } return path; }; return FormPropertyFactory; }()); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ /** @enum {string} */ var TemplateElementType = { Field: 'field', Button: 'button', }; var TemplateSchemaElementRegistry = /** @class */ (function () { function TemplateSchemaElementRegistry() { this.elements = {}; } /** * @param {?} id * @param {?=} type * @return {?} */ TemplateSchemaElementRegistry.prototype.hasElement = /** * @param {?} id * @param {?=} type * @return {?} */ function (id, type) { if (type === void 0) { type = TemplateElementType.Field; } if (!this.elements.hasOwnProperty(type)) { return false; } return this.elements[type].hasOwnProperty(id); }; /** * @param {?} id * @param {?} element * @param {?=} type * @return {?} */ TemplateSchemaElementRegistry.prototype.register = /** * @param {?} id * @param {?} element * @param {?=} type * @return {?} */ function (id, element, type) { if (type === void 0) { type = TemplateElementType.Field; } if (!this.elements.hasOwnProperty(type)) { this.elements[type] = {}; } this.elements[type][id] = element; }; /** * @template T * @param {?} id * @param {?=} type * @return {?} */ TemplateSchemaElementRegistry.prototype.getElement = /** * @template T * @param {?} id * @param {?=} type * @return {?} */ function (id, type) { if (type === void 0) { type = TemplateElementType.Field; } if (this.hasElement(id, type)) { return this.elements[type][id]; } }; /** * @return {?} */ TemplateSchemaElementRegistry.prototype.clear = /** * @return {?} */ function () { this.elements = {}; }; return TemplateSchemaElementRegistry; }()); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ /** * @param {?} schemaValidatorFactory * @param {?} validatorRegistry * @return {?} */ function useFactory(schemaValidatorFactory, validatorRegistry) { return new FormPropertyFactory(schemaValidatorFactory, validatorRegistry); } var FormComponent = /** @class */ (function () { function FormComponent(changeDetectorRef, formPropertyFactory, actionRegistry, validatorRegistry) { this.changeDetectorRef = changeDetectorRef; this.formPropertyFactory = formPropertyFactory; this.actionRegistry = actionRegistry; this.validatorRegistry = validatorRegistry; this.schema = null; this.actions = {}; this.validators = {}; this.rootFormProperty = null; } /** * @param {?} value * @return {?} */ FormComponent.prototype.writeValue = /** * @param {?} value * @return {?} */ function (value) { // value should be object if (this.rootFormProperty && value) { this.rootFormProperty.patchValue(value); } }; /** * @param {?} fn * @return {?} */ FormComponent.prototype.registerOnChange = /** * @param {?} fn * @return {?} */ function (fn) { this.onChangeCallback = fn; if (this.rootFormProperty) { this.rootFormProperty.nonEmptyValueChanges.subscribe(fn); } }; // TODO implement /** * @param {?} fn * @return {?} */ FormComponent.prototype.registerOnTouched = /** * @param {?} fn * @return {?} */ function (fn) { }; /** * @param {?} isDisabled * @return {?} */ FormComponent.prototype.setDisabledState = /** * @param {?} isDisabled * @return {?} */ function (isDisabled) { if (!this.rootFormProperty) { return; } if (isDisabled) { this.rootFormProperty.disable(); } else { this.rootFormProperty.enable(); } }; /** * @param {?} changes * @return {?} */ FormComponent.prototype.ngOnChanges = /** * @param {?} changes * @return {?} */ function (changes) { if (changes["validators"]) { this.registerValidators(); } if (changes["actions"]) { this.registerActions(); } if (this.schema && !this.schema.type) { this.schema.type = SchemaPropertyType.Object; } if (this.schema && changes["schema"]) { /** @type {?} */ var value = void 0; if (this.rootFormProperty) { // TODO validate model against schema value = this.rootFormProperty.nonEmptyValue; } // force component destruction this.rootFormProperty = null; this.changeDetectorRef.detectChanges(); /** @type {?} */ var rootFormProperty = this.formPropertyFactory.createProperty(this.schema); // registerOnChange for changes after init if (this.onChangeCallback) { rootFormProperty.nonEmptyValueChanges.subscribe(this.onChangeCallback); if (value) { rootFormProperty.patchValue(value); } } this.rootFormProperty = rootFormProperty; } }; /** * @return {?} */ FormComponent.prototype.ngOnInit = /** * @return {?} */ function () { }; /** * @return {?} */ FormComponent.prototype.registerValidators = /** * @return {?} */ function () { this.validatorRegistry.clear(); if (!this.validators) { return; } for (var propertyPath in this.validators) { if (this.validators.hasOwnProperty(propertyPath)) { this.validatorRegistry.register(propertyPath, this.validators[propertyPath]); } } }; /** * @return {?} */ FormComponent.prototype.registerActions = /** * @return {?} */ function () { this.actionRegistry.clear(); if (!this.actions) { return; } for (var actionId in this.actions) { if (this.actions.hasOwnProperty(actionId)) { this.actionRegistry.register(actionId, this.actions[actionId]); } } }; FormComponent.decorators = [ { type: Component, args: [{ selector: 'sf-form', template: "\n <form #form=\"ngForm\">\n <sf-form-element *ngIf=\"rootFormProperty; else noSchema\" [formProperty]=\"rootFormProperty\">\n </sf-form-element>\n <ng-template #noSchema>\n You need to provide a json or a template schema!\n </ng-template>\n </form>\n ", providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(function () { return FormComponent; }), multi: true }, ActionRegistry, ValidatorRegistry, WidgetFactory, { provide: FormPropertyFactory, useFactory: useFactory, deps: [SchemaValidatorFactory, ValidatorRegistry] }, TemplateSchemaElementRegistry ], encapsulation: ViewEncapsulation.None }] } ]; /** @nocollapse */ FormComponent.ctorParameters = function () { return [ { type: ChangeDetectorRef }, { type: FormPropertyFactory }, { type: ActionRegistry }, { type: ValidatorRegistry } ]; }; FormComponent.propDecorators = { schema: [{ type: Input }], actions: [{ type: Input }], validators: [{ type: Input }] }; return FormComponent; }()); /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ /** * @abstract */ var /** * @abstract */ FormElementTemplateRef = /** @class */ (function (_super) { __extends(FormElementTemplateRef, _super); function FormElementTemplateRef() { return _super !== null && _super.apply(this, arguments) || this; } return FormElementTemplateRef; }(TemplateRef));