function formlyEval(scope, expression, $modelValue, $viewValue, extraLocals) { if (angular.isFunction(expression)) { return expression($viewValue, $modelValue, scope, extraLocals); } else { return scope.$eval(expression, angular.extend({$viewValue, $modelValue}, extraLocals)); } }
angular.forEach(checkerObjects, (shape, name) => { const checker = instance.shape(shape) const checkOptions = angular.extend({ prefix: `formly-field type ${options.type} for property ${name}`, url: formlyApiCheck.config.output.docsBaseUrl + 'formly-field-type-apicheck-failed', }, apiCheckOptions) instance[fn](checker, options[name], checkOptions) })
function extendOptionsWithDefaults(options, index) { const key = options.key || index || 0; angular.extend(options, { // attach the key in case the formly-field directive is used directly key, value: valueGetterSetter, runExpressions, resetModel, updateInitialValue }); }
function setupOptions() { formlyApiCheck.throw(optionsApi, [$scope.options], {prefix: 'formly-form options check'}); $scope.options = $scope.options || {}; $scope.options.formState = $scope.options.formState || {}; angular.extend($scope.options, { updateInitialValue, resetModel }); }
// @ngInject function formlyUsability(formlyApiCheck, formlyErrorAndWarningsUrlPrefix) { angular.extend(this, { getFormlyError, getFieldError, checkWrapper, checkWrapperTemplate, getErrorMessage, $get: () => this, }) function getFieldError(errorInfoSlug, message, field) { if (arguments.length < 3) { field = message message = errorInfoSlug errorInfoSlug = null } return new Error(getErrorMessage(errorInfoSlug, message) + ` Field definition: ${angular.toJson(field)}`) } function getFormlyError(errorInfoSlug, message) { if (!message) { message = errorInfoSlug errorInfoSlug = null } return new Error(getErrorMessage(errorInfoSlug, message)) } function getErrorMessage(errorInfoSlug, message) { let url = '' if (errorInfoSlug !== null) { url = `${formlyErrorAndWarningsUrlPrefix}${errorInfoSlug}` } return `Formly Error: ${message}. ${url}` } function checkWrapper(wrapper) { formlyApiCheck.throw(formlyApiCheck.formlyWrapperType, wrapper, { prefix: 'formlyConfig.setWrapper', urlSuffix: 'setwrapper-validation-failed', }) } function checkWrapperTemplate(template, additionalInfo) { const formlyTransclude = '<formly-transclude></formly-transclude>' if (template.indexOf(formlyTransclude) === -1) { throw getFormlyError( `Template wrapper templates must use "${formlyTransclude}" somewhere in them. ` + `This one does not have "<formly-transclude></formly-transclude>" in it: ${template}` + '\n' + `Additional information: ${JSON.stringify(additionalInfo)}` ) } } }
function setupOptions() { formlyApiCheck.throw( [formlyApiCheck.formOptionsApi.optional], [$scope.options], {prefix: 'formly-form options check'} ) $scope.options = $scope.options || {} $scope.options.formState = $scope.options.formState || {} angular.extend($scope.options, { updateInitialValue, resetModel, }) }
function addTemplateOptionsAttrs() { if (!options.templateOptions && !options.expressionProperties) { // no need to run these if there are no templateOptions or expressionProperties return; } const to = options.templateOptions || {}; const ep = options.expressionProperties || {}; let ngModelAttributes = getBuiltInAttributes(); // extend with the user's specifications winning angular.extend(ngModelAttributes, options.ngModelAttrs); // Feel free to make this more simple :-) angular.forEach(ngModelAttributes, (val, name) => { /* eslint complexity:[2, 14] */ let attrVal; let attrName; const ref = `options.templateOptions['${name}']`; const toVal = to[name]; const epVal = getEpValue(ep, name); const inTo = angular.isDefined(toVal); const inEp = angular.isDefined(epVal); if (val.value) { // I realize this looks backwards, but it's right, trust me... attrName = val.value; attrVal = name; } else if (val.expression && inTo) { attrName = val.expression; if (angular.isString(to[name])) { attrVal = `$eval(${ref})`; } else if (angular.isFunction(to[name])) { attrVal = `${ref}(model[options.key], options, this, $event)`; } else { throw new Error( `options.templateOptions.${name} must be a string or function: ${JSON.stringify(options)}` ); } } else if (val.bound && inEp) { attrName = val.bound; attrVal = ref; } else if ((val.attribute || val.boolean) && inEp) { attrName = val.attribute || val.boolean; attrVal = `${$interpolate.startSymbol()}${ref}${$interpolate.endSymbol()}`; } else if (val.attribute && inTo) { attrName = val.attribute; attrVal = toVal; } else if (val.boolean) { if (inTo && !inEp && toVal) { attrName = val.boolean; attrVal = true; } else { /* eslint no-empty:0 */ // empty to illustrate that a boolean will not be added via val.bound // if you want it added via val.bound, then put it in expressionProperties } } else if (val.bound && inTo) { attrName = val.bound; attrVal = ref; } if (angular.isDefined(attrName) && angular.isDefined(attrVal)) { addIfNotPresent(modelNodes, attrName, attrVal); } }); }
const typeOptionsDefaultOptions = angular.copy(fieldOptionsApiShape) typeOptionsDefaultOptions.key = apiCheck.string.optional const formlyTypeOptions = apiCheck.shape({ name: apiCheck.string, template: apiCheck.shape.ifNot('templateUrl', apiCheck.oneOfType([apiCheck.string, apiCheck.func])).optional, templateUrl: apiCheck.shape.ifNot('template', apiCheck.oneOfType([apiCheck.string, apiCheck.func])).optional, controller: apiCheck.oneOfType([ apiCheck.func, apiCheck.string, apiCheck.array, ]).optional, link: apiCheck.func.optional, defaultOptions: apiCheck.oneOfType([ apiCheck.func, apiCheck.shape(typeOptionsDefaultOptions), ]).optional, extends: apiCheck.string.optional, wrapper: specifyWrapperType.optional, data: apiCheck.object.optional, apiCheck: apiCheckProperty.optional, apiCheckInstance: apiCheckInstanceProperty.optional, apiCheckFunction: apiCheckFunctionProperty.optional, apiCheckOptions: apiCheck.object.optional, overwriteOk: apiCheck.bool.optional, }).strict angular.extend(apiCheck, { formlyTypeOptions, formlyFieldOptions, formlyExpression, formlyWrapperType, fieldGroup, formOptionsApi, }) export default apiCheck
// @ngInject function formlyConfig(formlyUsabilityProvider, formlyApiCheck) { var typeMap = {}; var templateWrappersMap = {}; var defaultWrapperName = 'default'; var _this = this; var getError = formlyUsabilityProvider.getFormlyError; angular.extend(this, { setType, getType, setWrapper, getWrapper, getWrapperByType, removeWrapperByName, removeWrappersForType, disableWarnings: false, extras: { disableNgModelAttrsManipulator: false, ngModelAttrsManipulatorPreferUnbound: false, removeChromeAutoComplete: false, defaultHideDirective: 'ng-if', getFieldId: null }, templateManipulators: { preWrapper: [], postWrapper: [] }, $get: () => this }); function setType(options) { if (angular.isArray(options)) { angular.forEach(options, setType); } else if (angular.isObject(options)) { checkType(options); if (options.extends) { extendTypeOptions(options); } typeMap[options.name] = options; } else { throw getError(`You must provide an object or array for setType. You provided: ${JSON.stringify(arguments)}`); } } function checkType(options) { formlyApiCheck.throw(formlyApiCheck.formlyTypeOptions, options, { prefix: 'formlyConfig.setType', url: 'settype-validation-failed' }); if (!options.overwriteOk) { checkOverwrite(options.name, typeMap, options, 'types'); } else { options.overwriteOk = undefined; } } function extendTypeOptions(options) { const extendsType = getType(options.extends, true, options); extendTypeControllerFunction(options, extendsType); extendTypeLinkFunction(options, extendsType); extendTypeValidateOptionsFunction(options, extendsType); extendTypeDefaultOptions(options, extendsType); utils.reverseDeepMerge(options, extendsType); extendTemplate(options, extendsType); } function extendTemplate(options, extendsType){ if(options.template && extendsType.templateUrl){ delete options.templateUrl; } else if(options.templateUrl && extendsType.template){ delete options.template; } } function extendTypeControllerFunction(options, extendsType) { const extendsCtrl = extendsType.controller; if (!angular.isDefined(extendsCtrl)) { return; } const optionsCtrl = options.controller; if (angular.isDefined(optionsCtrl)) { options.controller = function ($scope, $controller) { $controller(extendsCtrl, {$scope}); $controller(optionsCtrl, {$scope}); }; options.controller.$inject = ['$scope', '$controller']; } else { options.controller = extendsCtrl; } } function extendTypeLinkFunction(options, extendsType) { const extendsFn = extendsType.link; if (!angular.isDefined(extendsFn)) { return; } const optionsFn = options.link; if (angular.isDefined(optionsFn)) { options.link = function () { extendsFn(...arguments); optionsFn(...arguments); }; } else { options.link = extendsFn; } } function extendTypeValidateOptionsFunction(options, extendsType) { const extendsFn = extendsType.validateOptions; if (!angular.isDefined(extendsFn)) { return; } const optionsFn = options.validateOptions; const originalDefaultOptions = options.defaultOptions; if (angular.isDefined(optionsFn)) { options.validateOptions = function (opts) { optionsFn(opts); let mergedOptions = angular.copy(opts); let defaultOptions = originalDefaultOptions; if (defaultOptions) { if (angular.isFunction(defaultOptions)) { defaultOptions = defaultOptions(mergedOptions); } utils.reverseDeepMerge(mergedOptions, defaultOptions); } extendsFn(mergedOptions); }; } else { options.validateOptions = extendsFn; } } function extendTypeDefaultOptions(options, extendsType) { const extendsDO = extendsType.defaultOptions; if (!angular.isDefined(extendsDO)) { return; } const optionsDO = options.defaultOptions; const optionsDOIsFn = angular.isFunction(optionsDO); const extendsDOIsFn = angular.isFunction(extendsDO); if (extendsDOIsFn) { options.defaultOptions = function defaultOptions(opts) { const extendsDefaultOptions = extendsDO(opts); const mergedDefaultOptions = {}; utils.reverseDeepMerge(mergedDefaultOptions, opts, extendsDefaultOptions); let extenderOptionsDefaultOptions = optionsDO; if (optionsDOIsFn) { extenderOptionsDefaultOptions = extenderOptionsDefaultOptions(mergedDefaultOptions); } utils.reverseDeepMerge(extendsDefaultOptions, extenderOptionsDefaultOptions); return extendsDefaultOptions; }; } else if (optionsDOIsFn) { options.defaultOptions = function defaultOptions(opts) { let newDefaultOptions = {}; utils.reverseDeepMerge(newDefaultOptions, opts, extendsDO); return optionsDO(newDefaultOptions); }; } } function getType(name, throwError, errorContext) { if (!name) { return undefined; } var type = typeMap[name]; if (!type && throwError === true) { throw getError( `There is no type by the name of "${name}": ${JSON.stringify(errorContext)}` ); } else { return type; } } function setWrapper(options, name) { if (angular.isArray(options)) { return options.map(wrapperOptions => setWrapper(wrapperOptions)); } else if (angular.isObject(options)) { options.types = getOptionsTypes(options); options.name = getOptionsName(options, name); checkWrapperAPI(options); templateWrappersMap[options.name] = options; return options; } else if (angular.isString(options)) { return setWrapper({ template: options, name }); } } function getOptionsTypes(options) { if (angular.isString(options.types)) { return [options.types]; } if (!angular.isDefined(options.types)) { return []; } else { return options.types; } } function getOptionsName(options, name) { return options.name || name || options.types.join(' ') || defaultWrapperName; } function checkWrapperAPI(options) { formlyUsabilityProvider.checkWrapper(options); if (options.template) { formlyUsabilityProvider.checkWrapperTemplate(options.template, options); } if (!options.overwriteOk) { checkOverwrite(options.name, templateWrappersMap, options, 'templateWrappers'); } else { delete options.overwriteOk; } checkWrapperTypes(options); } function checkWrapperTypes(options) { let shouldThrow = !angular.isArray(options.types) || !options.types.every(angular.isString); if (shouldThrow) { throw getError(`Attempted to create a template wrapper with types that is not a string or an array of strings`); } } function checkOverwrite(property, object, newValue, objectName) { if (object.hasOwnProperty(property)) { warn([ `Attempting to overwrite ${property} on ${objectName} which is currently`, `${JSON.stringify(object[property])} with ${JSON.stringify(newValue)}`, `To supress this warning, specify the property "overwriteOk: true"` ].join(' ')); } } function getWrapper(name) { return templateWrappersMap[name || defaultWrapperName]; } function getWrapperByType(type) { /* jshint maxcomplexity:6 */ var wrappers = []; for (var name in templateWrappersMap) { if (templateWrappersMap.hasOwnProperty(name)) { if (templateWrappersMap[name].types && templateWrappersMap[name].types.indexOf(type) !== -1) { wrappers.push(templateWrappersMap[name]); } } } return wrappers; } function removeWrapperByName(name) { var wrapper = templateWrappersMap[name]; delete templateWrappersMap[name]; return wrapper; } function removeWrappersForType(type) { var wrappers = getWrapperByType(type); if (!wrappers) { return undefined; } if (!angular.isArray(wrappers)) { return removeWrapperByName(wrappers.name); } else { wrappers.forEach((wrapper) => removeWrapperByName(wrapper.name)); return wrappers; } } function warn() { if (!_this.disableWarnings) { /* eslint no-console:0 */ console.warn(...arguments); } } }
// @ngInject function formlyConfig(formlyUsabilityProvider, formlyErrorAndWarningsUrlPrefix, formlyApiCheck) { const typeMap = {} const templateWrappersMap = {} const defaultWrapperName = 'default' const _this = this const getError = formlyUsabilityProvider.getFormlyError angular.extend(this, { setType, getType, getTypeHeritage, setWrapper, getWrapper, getWrapperByType, removeWrapperByName, removeWrappersForType, disableWarnings: false, extras: { disableNgModelAttrsManipulator: false, fieldTransform: [], ngModelAttrsManipulatorPreferUnbound: false, removeChromeAutoComplete: false, defaultHideDirective: 'ng-if', getFieldId: null, }, templateManipulators: { preWrapper: [], postWrapper: [], }, $get: () => this, }) function setType(options) { if (angular.isArray(options)) { const allTypes = [] angular.forEach(options, item => { allTypes.push(setType(item)) }) return allTypes } else if (angular.isObject(options)) { checkType(options) if (options.extends) { extendTypeOptions(options) } typeMap[options.name] = options return typeMap[options.name] } else { throw getError(`You must provide an object or array for setType. You provided: ${JSON.stringify(arguments)}`) } } function checkType(options) { formlyApiCheck.throw(formlyApiCheck.formlyTypeOptions, options, { prefix: 'formlyConfig.setType', url: 'settype-validation-failed', }) if (!options.overwriteOk) { checkOverwrite(options.name, typeMap, options, 'types') } else { options.overwriteOk = undefined } } function extendTypeOptions(options) { const extendsType = getType(options.extends, true, options) extendTypeControllerFunction(options, extendsType) extendTypeLinkFunction(options, extendsType) extendTypeDefaultOptions(options, extendsType) utils.reverseDeepMerge(options, extendsType) extendTemplate(options, extendsType) } function extendTemplate(options, extendsType) { if (options.template && extendsType.templateUrl) { delete options.templateUrl } else if (options.templateUrl && extendsType.template) { delete options.template } } function extendTypeControllerFunction(options, extendsType) { const extendsCtrl = extendsType.controller if (!angular.isDefined(extendsCtrl)) { return } const optionsCtrl = options.controller if (angular.isDefined(optionsCtrl)) { options.controller = function($scope, $controller) { $controller(extendsCtrl, {$scope}) $controller(optionsCtrl, {$scope}) } options.controller.$inject = ['$scope', '$controller'] } else { options.controller = extendsCtrl } } function extendTypeLinkFunction(options, extendsType) { const extendsFn = extendsType.link if (!angular.isDefined(extendsFn)) { return } const optionsFn = options.link if (angular.isDefined(optionsFn)) { options.link = function() { extendsFn(...arguments) optionsFn(...arguments) } } else { options.link = extendsFn } } function extendTypeDefaultOptions(options, extendsType) { const extendsDO = extendsType.defaultOptions if (!angular.isDefined(extendsDO)) { return } const optionsDO = options.defaultOptions const optionsDOIsFn = angular.isFunction(optionsDO) const extendsDOIsFn = angular.isFunction(extendsDO) if (extendsDOIsFn) { options.defaultOptions = function defaultOptions(opts, scope) { const extendsDefaultOptions = extendsDO(opts, scope) const mergedDefaultOptions = {} utils.reverseDeepMerge(mergedDefaultOptions, opts, extendsDefaultOptions) let extenderOptionsDefaultOptions = optionsDO if (optionsDOIsFn) { extenderOptionsDefaultOptions = extenderOptionsDefaultOptions(mergedDefaultOptions, scope) } utils.reverseDeepMerge(extendsDefaultOptions, extenderOptionsDefaultOptions) return extendsDefaultOptions } } else if (optionsDOIsFn) { options.defaultOptions = function defaultOptions(opts, scope) { const newDefaultOptions = {} utils.reverseDeepMerge(newDefaultOptions, opts, extendsDO) return optionsDO(newDefaultOptions, scope) } } } function getType(name, throwError, errorContext) { if (!name) { return undefined } const type = typeMap[name] if (!type && throwError === true) { throw getError( `There is no type by the name of "${name}": ${JSON.stringify(errorContext)}` ) } else { return type } } function getTypeHeritage(parent) { const heritage = [] let type = parent if (angular.isString(type)) { type = getType(parent) } parent = type.extends while (parent) { type = getType(parent) heritage.push(type) parent = type.extends } return heritage } function setWrapper(options, name) { if (angular.isArray(options)) { return options.map(wrapperOptions => setWrapper(wrapperOptions)) } else if (angular.isObject(options)) { options.types = getOptionsTypes(options) options.name = getOptionsName(options, name) checkWrapperAPI(options) templateWrappersMap[options.name] = options return options } else if (angular.isString(options)) { return setWrapper({ template: options, name, }) } } function getOptionsTypes(options) { if (angular.isString(options.types)) { return [options.types] } if (!angular.isDefined(options.types)) { return [] } else { return options.types } } function getOptionsName(options, name) { return options.name || name || options.types.join(' ') || defaultWrapperName } function checkWrapperAPI(options) { formlyUsabilityProvider.checkWrapper(options) if (options.template) { formlyUsabilityProvider.checkWrapperTemplate(options.template, options) } if (!options.overwriteOk) { checkOverwrite(options.name, templateWrappersMap, options, 'templateWrappers') } else { delete options.overwriteOk } checkWrapperTypes(options) } function checkWrapperTypes(options) { const shouldThrow = !angular.isArray(options.types) || !options.types.every(angular.isString) if (shouldThrow) { throw getError(`Attempted to create a template wrapper with types that is not a string or an array of strings`) } } function checkOverwrite(property, object, newValue, objectName) { if (object.hasOwnProperty(property)) { warn('overwriting-types-or-wrappers', [ `Attempting to overwrite ${property} on ${objectName} which is currently`, `${JSON.stringify(object[property])} with ${JSON.stringify(newValue)}`, `To supress this warning, specify the property "overwriteOk: true"`, ].join(' ')) } } function getWrapper(name) { return templateWrappersMap[name || defaultWrapperName] } function getWrapperByType(type) { /* eslint prefer-const:0 */ const wrappers = [] for (let name in templateWrappersMap) { if (templateWrappersMap.hasOwnProperty(name)) { if (templateWrappersMap[name].types && templateWrappersMap[name].types.indexOf(type) !== -1) { wrappers.push(templateWrappersMap[name]) } } } return wrappers } function removeWrapperByName(name) { const wrapper = templateWrappersMap[name] delete templateWrappersMap[name] return wrapper } function removeWrappersForType(type) { const wrappers = getWrapperByType(type) if (!wrappers) { return undefined } if (!angular.isArray(wrappers)) { return removeWrapperByName(wrappers.name) } else { wrappers.forEach((wrapper) => removeWrapperByName(wrapper.name)) return wrappers } } function warn() { if (!_this.disableWarnings && console.warn) { /* eslint no-console:0 */ const args = Array.prototype.slice.call(arguments) const warnInfoSlug = args.shift() args.unshift('Formly Warning:') args.push(`${formlyErrorAndWarningsUrlPrefix}${warnInfoSlug}`) console.warn(...args) } } }