(component: T) {
const $component = window.angular.copy(InputService.$BaseComponent);
for (const key of Object.keys(component) as (keyof T)[]) {
($component as T)[key] = component[key];
}
const isRadioOrCheckbox = $component.labelClass === 'form-check-label';
const $definition = window.angular.copy(InputService.$baseDefinition);
// assign child objects
if ($definition.bindings != null && $component.bindings != null) {
for (const key of Object.keys($component.bindings)) {
$definition.bindings[key] = $component.bindings[key];
}
}
if (
$definition.transclude != null &&
typeof $component.transclude === 'object' &&
$component.transclude != null
) {
for (const key of Object.keys($component.transclude)) {
($definition.transclude as Indexed)[key] = ($component.transclude as Indexed)[key];
}
}
// assign controller
if ($component.controller === undefined) {
throw new Error(`Invalid component: ${JSON.stringify($component)}`);
}
$definition.controller = $component.controller;
// assign template
// tslint:disable-next-line:cyclomatic-complexity
$definition.template = ['$element', '$attrs', ($element: [HTMLElement], $attrs: NgAttributes) => {
let $template = as HTMLDivElement;
const $el = $element[0];
// allow consumer to access $template and $attrs attributes from `this`
const $input = $component.render.call({ $template, $attrs });
// all inputs must have labels
const $label =
as HTMLLabelElement;
const isRadio = ($input as HTMLInputElement).type === 'radio';
if ($attrs.hasOwnProperty('required') === true && isRadio === false) {
$label.appendChild( *);
}
if (isRadioOrCheckbox === false) {
$template.appendChild($label);
}
if ($component.canHaveIcon === true && $attrs.icon?.length > 0) {
$template.appendChild(
,
);
} else {
$template.appendChild($input);
}
if (closest($el, 'contain') != null) {
$input.style.marginTop = '8px';
$label.classList.add('sr-only');
}
const requiredTag = $label.firstElementChild;
if (requiredTag != null) {
$label.removeChild(requiredTag);
}
$component.renderLabel?.call({ $label, $attrs });
if (requiredTag != null) {
$label.appendChild(requiredTag);
}
// add a transclusion slot for e.g. nesting inputs
$template.appendChild();
if (isRadioOrCheckbox === true) {
$label.style.setProperty('cursor', 'pointer');
$template.appendChild($label);
}
$component.postRender?.call({ $template, $attrs });
// that's right, i named it after filterFilter. fight me.
const $inputInput = InputService.getInputInput($input);
const $inputValidationAttrs = InputService.$validationAttrs
.filter(x => $attrs.hasOwnProperty(x) === true);
for (const attr of $inputValidationAttrs) {
$inputInput.setAttribute(
attr.replace(/[A-Z]/, s => `-${s.toLowerCase()}`),
/^ng/.test(attr) ? `$ctrl.${attr}` : 'true',
);
}
if ($inputInput.getAttribute('ng-attr-id') == null) {
$inputInput.setAttribute('ng-attr-id', `{{id}}_{{$ctrl.uniqueId}}${isRadio ? '_{{$index}}' : ''}`);
}
$inputInput.setAttribute('ng-attr-name', '{{id}}_{{$ctrl.uniqueId}}');
if (($inputInput.getAttribute('ng-attr-id')?.indexOf('{{id}}') ?? -1) === 0) {
$inputInput.setAttribute('ng-model', '$ctrl.ngModel');
}
if ($inputInput.tagName !== 'SELECT') {
const ngClass = `{ 'is-invalid': ${InputService.$ValidationExpressions.$IsInvalid} }`;
$inputInput.setAttribute('ng-class', ngClass);
$inputInput.setAttribute('ng-blur', '$ctrl.ngModelCtrl.$setTouched()');
$inputInput.setAttribute('ng-model-options', '$ctrl.ngModelOptions');
}
const $validationBlock =
;
const { validators = {} } = $component;
const attrs = Object.keys($component.attrs as Indexed);
for (const key of Object.keys(validators)) {
InputService.$validationMessages[key] = validators[key];
attrs.push(key);
}
const $inputValidationMessages = InputService.$validationAttrs
.concat(...attrs, 'email')
.filter(x => /^ng/.test(x) === false)
.filter(x => InputService.$validationMessages.hasOwnProperty(x) === true)
.filter(x => x !== 'email' || $inputInput.type === x);
for (const msg of $inputValidationMessages) {
const $message = ;
$message.innerText = InputService.$validationMessages[msg as 'email'];
$validationBlock.appendChild($message);
}
if (isRadio === true) {
$template = {$template}{$validationBlock}
as HTMLDivElement;
} else {
$template.appendChild($validationBlock);
}
let $html = $template.outerHTML.replace(/{{id}}/g, InputService.modelIdentifier($attrs));
for (const prop of attrs) {
$html = $html.replace(
new RegExp(`{{${prop}}}`, 'g'),
$attrs[prop] || ($component.attrs as Indexed)[prop],
);
}
return $html;
}];
return $definition as T;
}
}