UNPKG

ngx-form-control

Version:
1,666 lines (1,631 loc) 143 kB
import { Input, Component, ViewChild, EventEmitter, Output, NgModule } from '@angular/core'; import { NG_VALIDATORS, NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms'; import { BrowserModule } from '@angular/platform-browser'; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ /** * @abstract */ class BaseControlComponent { constructor() { this.id = 'ngx-' + Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1); this.innerCustomErrorMessages = []; this.name = ''; this.label = ''; this.placeholder = ''; this.title = ''; this.required = false; this.disabled = false; this.validMessage = ''; this.requiredErrorMessage = 'This field is required.'; } /** * @param {?} messages * @return {?} */ set customErrorMessages(messages) { if (!messages) { this.innerCustomErrorMessages = []; } else if ('string' === typeof messages) { this.innerCustomErrorMessages = [messages]; } else if (messages[0]) { this.innerCustomErrorMessages = messages; } else { this.innerCustomErrorMessages = []; } } /** * @return {?} */ get hasCustomError() { return !!(this.innerCustomErrorMessages && this.innerCustomErrorMessages[0]); } /** * @return {?} */ get hasRequiredError() { return this.required && this.value !== false && this.value !== 0 && !this.value; } /** * @param {?} fn * @return {?} */ registerOnChange(fn) { this._onChangeCallback = event => { if (this.cleanCustomErrorMessageOnChanged) { this.innerCustomErrorMessages = []; } return fn(event); }; } /** * @param {?} fn * @return {?} */ registerOnTouched(fn) { this._onTouchedCallback = fn; } /** * @return {?} */ triggerChange() { if (this._onChangeCallback) { this._onChangeCallback(this.value); } } } BaseControlComponent.propDecorators = { name: [{ type: Input }], label: [{ type: Input }], placeholder: [{ type: Input }], title: [{ type: Input }], required: [{ type: Input }], disabled: [{ type: Input }], validMessage: [{ type: Input }], requiredErrorMessage: [{ type: Input }], cleanCustomErrorMessageOnChanged: [{ type: Input }], customErrorMessages: [{ type: Input }] }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ /** * @abstract */ class BaseListControlComponent extends BaseControlComponent { constructor() { super(...arguments); this._options = []; this._selectOptions = []; this._selectedIndexes = []; } /** * @param {?} value * @return {?} */ set textKey(value) { this._textKey = value; this.initOptions(); } /** * @param {?} value * @return {?} */ set valueKey(value) { this._valueKey = value; this.initOptions(); } /** * @param {?} value * @return {?} */ set comparedKey(value) { this._comparedKey = value; this.initOptions(); } /** * @param {?} options * @return {?} */ set options(options) { this._options = options; this.initOptions(); } /** * @return {?} */ get selectOptions() { return this._selectOptions; } /** * @return {?} */ get selectedIndexes() { return this._selectedIndexes; } /** * @param {?} indexes * @return {?} */ set selectedIndexes(indexes) { /** @type {?} */ const oldSelectedIndexes = JSON.stringify(this._selectedIndexes); this._selectedIndexes = indexes && indexes.length ? indexes.reduce((arr, value) => { value = +value; if (value > -1) { arr.push(value); } return arr; }, []) : []; /** @type {?} */ const newSelectedIndexes = JSON.stringify(this._selectedIndexes); if (newSelectedIndexes !== oldSelectedIndexes) { this.triggerChange(); } } /** * @param {?} value * @return {?} */ findIndex(value) { /** @type {?} */ const comparedValue = this.getComparedValue(value); return this._selectOptions.findIndex((option) => { return comparedValue === option.comparedValue; }); } /** * @param {?} arrValue * @return {?} */ findIndexes(arrValue) { if (!arrValue || !arrValue.length) { return []; } return arrValue.reduce((arr, value) => { /** @type {?} */ const index = this.findIndex(value); if (index > -1) { arr.push(index); } return arr; }, []); } /** * @return {?} */ initOptions() { this.beforeInitOptions(); /** @type {?} */ const oldValue = this.value; this._selectOptions = []; if (this._options && this._options.length) { this._options.map((option, index) => { /** @type {?} */ let text; /** @type {?} */ let value; if ('string' === typeof option || 'number' === typeof option) { text = option; value = option; } else { text = option[this._textKey || 'text']; value = this._valueKey ? option[this._valueKey] : option; } this._selectOptions.push({ id: index, text: text, value: value, comparedValue: this.getComparedValue(option), }); }); } this.afterInitOptions(); this.writeValue(oldValue); /** @type {?} */ const newValue = this.value; if (JSON.stringify(oldValue) !== JSON.stringify(newValue)) { this.triggerChange(); } } /** * @return {?} */ beforeInitOptions() { } /** * @return {?} */ afterInitOptions() { } /** * @param {?} option * @return {?} */ getComparedValue(option) { if (!option) { return ''; } if ('string' === typeof option || 'number' === typeof option) { return option; } /** @type {?} */ let value; if (this._comparedKey) { value = option[this._comparedKey]; } else { value = this._valueKey ? option[this._valueKey] : option; } if ('string' === typeof value || 'number' === typeof value) { return value; } return JSON.stringify(value); } } BaseListControlComponent.propDecorators = { textKey: [{ type: Input }], valueKey: [{ type: Input }], comparedKey: [{ type: Input }], options: [{ type: Input }] }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ class FormCheckboxComponent extends BaseListControlComponent { constructor() { super(...arguments); this._isTouched = false; } /** * @return {?} */ get isEmpty() { return !this._selectedIndexes || !this._selectedIndexes.length; } /** * @return {?} */ get value() { return this.isEmpty ? null : this._selectedIndexes.map(index => this._selectOptions[index].value); } /** * @return {?} */ get invalid() { if (this.hasCustomError) { return true; } if (!this._isTouched) { return false; } return this.hasRequiredError; } /** * @return {?} */ get valid() { if (this.hasCustomError) { return false; } if (!this._isTouched) { return false; } return !this.hasRequiredError; } /** * @return {?} */ get errorMessages() { if (this.hasCustomError) { return this.innerCustomErrorMessages; } if (this.hasRequiredError) { return [this.requiredErrorMessage]; } } /** * @param {?} value * @return {?} */ writeValue(value) { /** @type {?} */ const listElement = this.listRadioElement.nativeElement.querySelectorAll('.custom-control-input'); for (const element of listElement) { element.checked = false; } if (!value) { return; } this._selectedIndexes = this.findIndexes(value); if (!this.listRadioElement || !this.listRadioElement.nativeElement) { return; } if (this.value) { setTimeout(() => { this._selectedIndexes.map(index => { listElement[index].checked = true; }); }); } } /** * @return {?} */ validate() { /** @type {?} */ const result = {}; if (this.hasRequiredError) { result['required'] = true; } return result; } /** * @param {?} index * @param {?} event * @return {?} */ toggle(index, event) { this._isTouched = true; /** @type {?} */ const checked = event.target.checked; index = +index; if (checked) { this._selectedIndexes.push(index); } else { /** @type {?} */ const indexOfIndex = this._selectedIndexes.indexOf(index); if (indexOfIndex > -1) { this._selectedIndexes.splice(indexOfIndex, 1); } } this.triggerChange(); } /** * @return {?} */ reset() { this._isTouched = false; } } FormCheckboxComponent.decorators = [ { type: Component, args: [{ selector: 'ngx-form-checkbox', template: `<label *ngIf="label" [for]="id">{{label}}</label> <div #listRadioElement> <div class="custom-control custom-checkbox" *ngFor="let option of selectOptions; let index = index;"> <input class="custom-control-input" [ngClass]="{'is-invalid': invalid, 'is-valid': valid}" type="checkbox" [id]="id + '-' + index" [name]="id" (click)="toggle(index, $event)" title=""> <label class="custom-control-label" [for]="id + '-' + index">{{option.text}}</label> </div> </div> <ng-container *ngIf="valid && validMessage"> <div class="custom-control-input is-valid"></div> <div class="valid-feedback">{{validMessage}}</div> </ng-container> <ng-container *ngIf="invalid"> <div class="custom-control-input is-invalid"></div> <div class="invalid-feedback"> <span *ngFor="let message of errorMessages; let last = last;"> {{message}}<br *ngIf="!last"> </span> </div> </ng-container> `, styles: [`:host .form-check.is-valid~.invalid-feedback,:host .form-check.is-valid~.invalid-tooltip,:host .form-check.is-valid~.valid-feedback,:host .form-check.is-valid~.valid-tooltip,:host .was-validated~.invalid-feedback,:host .was-validated~.invalid-tooltip,:host .was-validated~.valid-feedback,:host .was-validated~.valid-tooltip{display:block}`], providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: FormCheckboxComponent, multi: true }, { provide: NG_VALIDATORS, useExisting: FormCheckboxComponent, multi: true } ] },] }, ]; FormCheckboxComponent.propDecorators = { listRadioElement: [{ type: ViewChild, args: ['listRadioElement',] }] }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ class FormInputComponent extends BaseControlComponent { constructor() { super(...arguments); this.type = 'text'; this.pattern = ''; this.readonly = false; this.autocomplete = true; this.trimResult = true; this.minLengthErrorMessage = 'Value is too short.'; this.patternErrorMessage = 'Value is not valid.'; this.matchErrorMessage = 'Value does not match.'; this.focus = new EventEmitter(); this.blur = new EventEmitter(); } /** * @param {?} value * @return {?} */ set match(value) { this._match = value || ''; this.triggerChange(); } /** * @return {?} */ get value() { if (!this._innerValue) { return ''; } if ('string' !== typeof this._innerValue) { return this._innerValue; } return this.trimResult ? this._innerValue.trim() : this._innerValue; } /** * @param {?} value * @return {?} */ set value(value) { if (value !== this._innerValue) { this._innerValue = value; this.triggerChange(); } } /** * @return {?} */ get hasMatchError() { return !!this._match && this._match !== this.value; } /** * @return {?} */ get hasPatternError() { return this.customInput.errors && this.customInput.errors['pattern']; } /** * @return {?} */ get hasMinLengthError() { return this.customInput.errors && this.customInput.errors['minlength']; } /** * @return {?} */ get invalid() { if (this.hasCustomError) { return true; } if (!this.customInput.touched) { return false; } return this.customInput.invalid || this.hasMatchError || this.hasRequiredError; } /** * @return {?} */ get valid() { if (this.hasCustomError) { return false; } if (!this.customInput.touched) { return false; } return !this.customInput.invalid && !this.hasMatchError && !this.hasRequiredError; } /** * @return {?} */ get errorMessages() { if (this.hasRequiredError) { return [this.requiredErrorMessage]; } if (this.hasMatchError) { return [this.matchErrorMessage]; } if (this.hasPatternError) { return [this.patternErrorMessage]; } if (this.hasMinLengthError) { return [this.minLengthErrorMessage]; } if (this.hasCustomError) { return this.innerCustomErrorMessages; } } /** * @param {?} value * @return {?} */ writeValue(value) { this._innerValue = value; } /** * @return {?} */ validate() { /** @type {?} */ const result = this.customInput.errors || {}; if (this.hasRequiredError) { result['required'] = true; } else { delete result['required']; } if (this.hasMatchError) { result['match'] = true; } return result; } /** * @return {?} */ reset() { this.customInput.reset(); } /** * @param {?} event * @return {?} */ onFocus(event) { this.focus.emit(event); } /** * @param {?} event * @return {?} */ onBlur(event) { this.blur.emit(event); } } FormInputComponent.decorators = [ { type: Component, args: [{ selector: 'ngx-form-input', template: `<label *ngIf="label" [for]="id">{{label}}</label> <!--suppress HtmlFormInputWithoutLabel --> <input class="form-control" [ngClass]="{'is-invalid': invalid, 'is-valid': valid}" [type]="type" [id]="id" [required]="required" [disabled]="disabled" [readonly]="readonly" [minlength]="minlength" [maxlength]="maxlength" [placeholder]="placeholder" [title]="title" [pattern]="pattern" [(ngModel)]="value" (input)="triggerChange()" [autocomplete]="autocomplete ? 'on' : 'off'" (focus)="onFocus($event)" (blur)="onBlur($event)" #customInput="ngModel"> <div class="valid-feedback" *ngIf="valid && validMessage">{{validMessage}}</div> <div class="invalid-feedback" *ngIf="invalid"> <span *ngFor="let message of errorMessages; let last = last;"> {{message}}<br *ngIf="!last"> </span> </div> `, styles: [``], providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: FormInputComponent, multi: true }, { provide: NG_VALIDATORS, useExisting: FormInputComponent, multi: true } ] },] }, ]; FormInputComponent.propDecorators = { type: [{ type: Input }], pattern: [{ type: Input }], readonly: [{ type: Input }], autocomplete: [{ type: Input }], minlength: [{ type: Input }], maxlength: [{ type: Input }], trimResult: [{ type: Input }], minLengthErrorMessage: [{ type: Input }], patternErrorMessage: [{ type: Input }], matchErrorMessage: [{ type: Input }], customInput: [{ type: ViewChild, args: ['customInput',] }], match: [{ type: Input }], focus: [{ type: Output }], blur: [{ type: Output }] }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ class FormRadioComponent extends BaseListControlComponent { constructor() { super(...arguments); this._isTouched = false; } /** * @return {?} */ get isEmpty() { return !this._selectedIndexes || !this._selectedIndexes.length; } /** * @return {?} */ get value() { return this.isEmpty ? null : this._selectOptions[this._selectedIndexes[0]].value; } /** * @return {?} */ get invalid() { if (this.hasCustomError) { return true; } if (!this._isTouched) { return false; } return this.hasRequiredError; } /** * @return {?} */ get valid() { if (this.hasCustomError) { return false; } if (!this._isTouched) { return false; } return !this.hasRequiredError; } /** * @return {?} */ get errorMessages() { if (this.hasRequiredError) { return [this.requiredErrorMessage]; } if (this.hasCustomError) { return this.innerCustomErrorMessages; } } /** * @param {?} value * @return {?} */ writeValue(value) { /** @type {?} */ const listElement = this.listRadioElement.nativeElement.querySelectorAll('.custom-control-input'); for (const element of listElement) { element.checked = false; } if (!value) { return; } this._selectedIndexes = this.findIndexes([value]); if (!this.listRadioElement || !this.listRadioElement.nativeElement) { return; } if (this.value) { setTimeout(() => { /** @type {?} */ const index = this._selectedIndexes[0]; listElement[index].checked = true; }); } } /** * @return {?} */ validate() { /** @type {?} */ const result = {}; if (this.hasRequiredError) { result['required'] = true; } return result; } /** * @param {?} index * @param {?} event * @return {?} */ toggle(index, event) { this._isTouched = true; index = +index; if (this.required || index !== this._selectedIndexes[0]) { this._selectedIndexes = [+index]; } else { this._selectedIndexes = []; event.target.checked = false; } this.triggerChange(); } /** * @return {?} */ reset() { this._isTouched = false; } } FormRadioComponent.decorators = [ { type: Component, args: [{ selector: 'ngx-form-radio', template: `<label *ngIf="label" [for]="id">{{label}}</label> <div #listRadioElement> <div class="custom-control custom-radio" *ngFor="let option of selectOptions; let index = index;"> <input class="custom-control-input" [ngClass]="{'is-invalid': invalid, 'is-valid': valid}" type="radio" [id]="id + '-' + index" [name]="id" (click)="toggle(index, $event)" title=""> <label class="custom-control-label" [for]="id + '-' + index">{{option.text}}</label> </div> </div> <ng-container *ngIf="valid && validMessage"> <div class="custom-control-input is-valid"></div> <div class="valid-feedback">{{validMessage}}</div> </ng-container> <ng-container *ngIf="invalid"> <div class="custom-control-input is-invalid"></div> <div class="invalid-feedback"> <span *ngFor="let message of errorMessages; let last = last;"> {{message}}<br *ngIf="!last"> </span> </div> </ng-container> `, styles: [`:host .form-check.is-valid~.invalid-feedback,:host .form-check.is-valid~.invalid-tooltip,:host .form-check.is-valid~.valid-feedback,:host .form-check.is-valid~.valid-tooltip,:host .was-validated~.invalid-feedback,:host .was-validated~.invalid-tooltip,:host .was-validated~.valid-feedback,:host .was-validated~.valid-tooltip{display:block}`], providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: FormRadioComponent, multi: true }, { provide: NG_VALIDATORS, useExisting: FormRadioComponent, multi: true } ] },] }, ]; FormRadioComponent.propDecorators = { listRadioElement: [{ type: ViewChild, args: ['listRadioElement',] }] }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ class FormSelectComponent extends BaseListControlComponent { /** * @return {?} */ get multiple() { return this._multiple; } /** * @param {?} value * @return {?} */ set multiple(value) { this._multiple = value; } /** * @return {?} */ get isEmpty() { return !this._selectedIndexes || !this._selectedIndexes.length || (1 === this._selectedIndexes.length && -1 === this._selectedIndexes[0]); } /** * @return {?} */ get value() { if (this.isEmpty) { return null; } if (this._multiple) { return this._selectedIndexes.map(index => this._selectOptions[index].value); } else { /** @type {?} */ const index = this._selectedIndexes[0]; return this.selectOptions[index].value; } } /** * @return {?} */ get invalid() { if (this.hasCustomError) { return true; } if (!this.customSelect.touched) { return false; } return this.customSelect.invalid || this.hasRequiredError; } /** * @return {?} */ get valid() { if (this.hasCustomError) { return false; } if (!this.customSelect.touched) { return false; } return !this.customSelect.invalid && !this.hasRequiredError; } /** * @return {?} */ get errorMessages() { if (this.hasRequiredError) { return [this.requiredErrorMessage]; } if (this.hasCustomError) { return this.innerCustomErrorMessages; } } /** * @param {?} value * @return {?} */ writeValue(value) { if (!this.multiple) { value = [value]; } this._selectedIndexes = this.findIndexes(value); } /** * @return {?} */ validate() { /** @type {?} */ const result = this.customSelect.errors || {}; if (this.hasRequiredError) { result['required'] = true; } else { delete result['required']; } return result; } /** * @return {?} */ reset() { this.customSelect.reset(); } } FormSelectComponent.decorators = [ { type: Component, args: [{ selector: 'ngx-form-select', template: `<label *ngIf="label" [for]="id">{{label}}</label> <!--suppress HtmlFormInputWithoutLabel --> <select class="form-control" [ngClass]="{'is-invalid': invalid, 'is-valid': valid}" [id]="id" [title]="title" [required]="required" [disabled]="disabled" [multiple]="multiple" [(ngModel)]="selectedIndexes" #customSelect="ngModel" #customSelectElement> <option value="-1" *ngIf="placeholder">{{placeholder}}</option> <option *ngFor="let option of selectOptions; let index = index;" [value]="index">{{option.text}}</option> </select> <div class="valid-feedback" *ngIf="valid && validMessage">{{validMessage}}</div> <div class="invalid-feedback" *ngIf="invalid"> <span *ngFor="let message of errorMessages; let last = last;"> {{message}}<br *ngIf="!last"> </span> </div> `, styles: [``], providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: FormSelectComponent, multi: true }, { provide: NG_VALIDATORS, useExisting: FormSelectComponent, multi: true } ] },] }, ]; FormSelectComponent.propDecorators = { customSelect: [{ type: ViewChild, args: ['customSelect',] }], customSelectElement: [{ type: ViewChild, args: ['customSelectElement',] }], multiple: [{ type: Input }] }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ class Common { /** * @return {?} */ static isClient() { return 'undefined' !== typeof window; } /** * @return {?} */ static isServer() { return 'undefined' === typeof window; } } /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ class FormSelect2Component extends BaseListControlComponent { constructor() { super(...arguments); this._isTouched = false; } /** * @param {?} value * @return {?} */ set placeholder(value) { this._placeholder = value; this.updateSelect2Options(); } /** * @param {?} value * @return {?} */ set required(value) { this._required = value; this.updateSelect2Options(); } /** * @param {?} value * @return {?} */ set disabled(value) { this._disabled = value; this.updateSelect2Options(); } /** * @param {?} value * @return {?} */ set multiple(value) { this._multiple = value; this.updateSelect2Options(); } /** * @param {?} value * @return {?} */ set tag(value) { this._tag = value; this.updateSelect2Options(); } /** * @param {?} value * @return {?} */ set tokenSeparators(value) { this._tag = value; this.updateSelect2Options(); } /** * @return {?} */ get value() { if (!this._selectedIndexes || !this._selectedIndexes.length) { return null; } /** @type {?} */ const result = this._selectedIndexes.reduce((currentResult, index) => { if (Number.isInteger(index) && this._selectOptions[index]) { currentResult.push(this._selectOptions[index].value); } else if (this._tag) { /** @type {?} */ const match = index['value'].match(/^number: {([\d]+)}$/); if (match) { currentResult.push(match[1]); } else { currentResult.push(index['value']); } } return currentResult; }, []); return this._multiple ? result : result[0]; } /** * @return {?} */ get invalid() { if (this.hasCustomError) { return true; } if (!this._isTouched) { return false; } return this.hasRequiredError; } /** * @return {?} */ get valid() { if (this.hasCustomError) { return false; } if (!this._isTouched) { return false; } return !this.hasRequiredError; } /** * @return {?} */ get errorMessages() { if (this.hasRequiredError) { return [this.requiredErrorMessage]; } if (this.hasCustomError) { return this.innerCustomErrorMessages; } } /** * @return {?} */ ngOnInit() { this.updateSelect2Options(); } /** * @param {?} value * @return {?} */ writeValue(value) { if (this._multiple && value && value.length) { this.selectValues(value); } else if (!this._multiple) { this.selectValue(value); } else { this.cleanValue(); } } /** * @return {?} */ validate() { /** @type {?} */ const result = {}; if (this.hasRequiredError) { result['required'] = true; } return result; } /** * @return {?} */ reset() { this._isTouched = false; } /** * @return {?} */ afterInitOptions() { this._selectOptions = [...this._selectOptions]; this.updateSelect2Options(); } /** * @param {?} values * @return {?} */ selectValues(values) { this._selectedIndexes = []; /** @type {?} */ const select2Data = []; if (values && values.length) { values.map((value) => { /** @type {?} */ const index = this.findIndex(value); if (index > -1) { this._selectedIndexes.push(index); select2Data.push(index); } else if (this._tag) { this._selectedIndexes.push({ value }); select2Data.push(value); } }); } if (Common.isClient()) { this._selectElement.val(select2Data); this._selectElement.trigger('change'); } } /** * @return {?} */ updateSelectedIndexes() { if (Common.isServer()) { return; } /** @type {?} */ const oldSelectedIndexes = JSON.stringify(this._selectedIndexes); /** @type {?} */ const value = this._selectElement.val(); if ('number' === typeof value || ('string' === typeof value && Number.isInteger(+value))) { this._selectedIndexes = [+value]; } else if ('string' === typeof value && this._tag) { this._selectedIndexes = [{ value }]; } else if (value && value.length) { this._selectedIndexes = value.map(item => { if (Number.isInteger(+item)) { return +item; } if (this._tag) { return { value: item }; } return null; }); } else { this._selectedIndexes = []; } /** @type {?} */ const newSelectedIndexes = JSON.stringify(this._selectedIndexes); if (newSelectedIndexes !== oldSelectedIndexes) { this.triggerChange(); } } /** * @param {?} value * @return {?} */ selectValue(value) { if (Common.isServer()) { return; } /** @type {?} */ const index = this.findIndex(value); if (index > -1) { this._selectedIndexes = [index]; this._selectElement.val(this._selectedIndexes); this._selectElement.trigger('change'); } else if (this._tag) { this._selectedIndexes = [{ value }]; this._selectElement.val(value); this._selectElement.trigger('change'); } else { this.cleanValue(); } } /** * @return {?} */ cleanValue() { if (Common.isServer()) { return; } this._selectedIndexes = []; this._selectElement.val(null); this._selectElement.trigger('change'); } /** * @return {?} */ updateSelect2Options() { if (Common.isServer() || !this.customSelectElement || !this.customSelectElement.nativeElement) { return; } this._selectElement = $(this.customSelectElement.nativeElement); if (this._selectElement.hasClass('select2-hidden-accessible')) { this._selectElement.select2().empty(); this._selectElement.select2('destroy'); } this._selectElement.select2({ tags: this._tag, tokenSeparators: this._tokenSeparators || [], placeholder: this._placeholder, allowClear: !this._required, multiple: this._multiple, data: this._selectOptions, disabled: this._disabled, createTag: function (params) { /** @type {?} */ const term = $.trim(params.term); if (term === '') { return null; } return { id: Number.isInteger(+term) ? `number: {${term}}` : term, text: term, newTag: true }; } }); this._selectElement.on('select2:select', () => { this.updateSelectedIndexes(); }); this._selectElement.on('select2:unselect', () => { this.updateSelectedIndexes(); }); this._selectElement.on('select2:close', () => { this._isTouched = true; }); } } FormSelect2Component.decorators = [ { type: Component, args: [{ selector: 'ngx-form-select2', template: `<label *ngIf="label" [for]="id">{{label}}</label> <div class="form-control is-invalid" [ngClass]="{'is-invalid': invalid, 'is-valid': valid}"> <!--suppress HtmlFormInputWithoutLabel --> <select [id]="id" #customSelectElement></select> </div> <div class="valid-feedback" *ngIf="valid && validMessage">{{validMessage}}</div> <div class="invalid-feedback" *ngIf="invalid"> <span *ngFor="let message of errorMessages; let last = last;"> {{message}}<br *ngIf="!last"> </span> </div> `, styles: [`:host .form-control{padding:0}:host .form-control /deep/ .select2-container{width:100%!important}:host .form-control /deep/ .select2-container .select2-selection--single{height:auto}:host .form-control /deep/ .select2-container--default .select2-selection--multiple,:host .form-control /deep/ .select2-container--default .select2-selection--single,:host .form-control /deep/ .select2-container--default.select2-container--focus .select2-selection--multiple{border:none;outline:0!important}:host .form-control /deep/ .select2-container--default .select2-selection--multiple:focus,:host .form-control /deep/ .select2-container--default .select2-selection--single:focus{border-color:#80bdff;box-shadow:0 0 0 .2rem rgba(0,123,255,.25)}:host .form-control.is-invalid /deep/ .select2-container--default .select2-selection--multiple:focus,:host .form-control.is-invalid /deep/ .select2-container--default .select2-selection--single:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}:host .form-control.is-valid /deep/ .select2-container--default .select2-selection--multiple:focus,:host .form-control.is-valid /deep/ .select2-container--default .select2-selection--single:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}:host .form-control /deep/ .select2-container--default .select2-selection--single .select2-selection__rendered{min-height:40px;line-height:25px;padding:.375rem 25px .375rem .75rem}:host .form-control /deep/ .select2-container .select2-selection--multiple{min-height:40px;padding:.375rem .75rem}:host .form-control /deep/ .select2-container--default .select2-selection--multiple .select2-selection__rendered{padding:0}:host .form-control /deep/ .select2-container--default .select2-selection--single .select2-selection__arrow{height:38px}`], providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: FormSelect2Component, multi: true }, { provide: NG_VALIDATORS, useExisting: FormSelect2Component, multi: true } ] },] }, ]; FormSelect2Component.propDecorators = { customSelectElement: [{ type: ViewChild, args: ['customSelectElement',] }], placeholder: [{ type: Input }], required: [{ type: Input }], disabled: [{ type: Input }], multiple: [{ type: Input }], tag: [{ type: Input }], tokenSeparators: [{ type: Input }] }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ class FormTextareaComponent extends BaseControlComponent { constructor() { super(...arguments); this.rows = 5; this.readonly = false; this.trimResult = true; this.minLengthErrorMessage = 'Value is too short.'; } /** * @return {?} */ get value() { if (!this._innerValue) { return ''; } if ('string' !== typeof this._innerValue) { return this._innerValue; } return this.trimResult ? this._innerValue.trim() : this._innerValue; } /** * @param {?} value * @return {?} */ set value(value) { if (value !== this._innerValue) { this._innerValue = value; this.triggerChange(); } } /** * @return {?} */ get hasMinLengthError() { return this.customTextarea.errors && this.customTextarea.errors['minlength']; } /** * @return {?} */ get invalid() { if (this.hasCustomError) { return true; } if (!this.customTextarea.touched) { return false; } return this.customTextarea.invalid || this.hasRequiredError; } /** * @return {?} */ get valid() { if (this.hasCustomError) { return false; } if (!this.customTextarea.touched) { return false; } return !this.customTextarea.invalid && !this.hasRequiredError; } /** * @return {?} */ get errorMessages() { if (this.hasRequiredError) { return [this.requiredErrorMessage]; } if (this.hasMinLengthError) { return [this.minLengthErrorMessage]; } if (this.hasCustomError) { return this.innerCustomErrorMessages; } } /** * @param {?} value * @return {?} */ writeValue(value) { this._innerValue = value; } /** * @return {?} */ validate() { /** @type {?} */ const result = this.customTextarea.errors || {}; if (this.hasRequiredError) { result['required'] = true; } else { delete result['required']; } return result; } /** * @return {?} */ reset() { this.customTextarea.reset(); } } FormTextareaComponent.decorators = [ { type: Component, args: [{ selector: 'ngx-form-textarea', template: `<label *ngIf="label" [for]="id">{{label}}</label> <!--suppress HtmlFormInputWithoutLabel --> <textarea class="form-control" [ngClass]="{'is-invalid': invalid, 'is-valid': valid}" [id]="id" [title]="title" [required]="required" [disabled]="disabled" [readonly]="readonly" [minlength]="minlength" [maxlength]="maxlength" [placeholder]="placeholder" [(ngModel)]="value" [rows]="rows" #customTextarea="ngModel"></textarea> <div class="valid-feedback" *ngIf="valid && validMessage">{{validMessage}}</div> <div class="invalid-feedback" *ngIf="invalid"> <span *ngFor="let message of errorMessages; let last = last;"> {{message}}<br *ngIf="!last"> </span> </div> `, styles: [``], providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: FormTextareaComponent, multi: true }, { provide: NG_VALIDATORS, useExisting: FormTextareaComponent, multi: true } ] },] }, ]; FormTextareaComponent.propDecorators = { rows: [{ type: Input }], readonly: [{ type: Input }], minlength: [{ type: Input }], maxlength: [{ type: Input }], trimResult: [{ type: Input }], minLengthErrorMessage: [{ type: Input }], customTextarea: [{ type: ViewChild, args: ['customTextarea',] }] }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ class FormToggleComponent extends BaseControlComponent { constructor() { super(...arguments); this.type = 'checkbox'; this._innerValue = false; this._isTouch = false; } /** * @return {?} */ get value() { return this._innerValue; } /** * @param {?} value * @return {?} */ set value(value) { this._isTouch = true; this._innerValue = value; this.triggerChange(); } /** * @return {?} */ get invalid() { if (this.hasCustomError) { return true; } if (!this._isTouch) { return false; } return this.hasRequiredError; } /** * @return {?} */ get valid() { if (this.hasCustomError) { return false; } if (!this._isTouch) { return false; } return !this.hasRequiredError; } /** * @return {?} */ get errorMessages() { if (this.hasRequiredError) { return [this.requiredErrorMessage]; } if (this.hasCustomError) { return this.innerCustomErrorMessages; } } /** * @return {?} */ get isCheckboxType() { return 'checkbox' === this.type; } /** * @return {?} */ get isToggleType() { return !this.isCheckboxType; } /** * @param {?} value * @return {?} */ writeValue(value) { this._innerValue = value; } /** * @return {?} */ validate() { /** @type {?} */ const result = {}; if (this.hasRequiredError) { result['required'] = true; } return result; } /** * @return {?} */ reset() { this._isTouch = false; } } FormToggleComponent.decorators = [ { type: Component, args: [{ selector: 'ngx-form-toggle', template: `<div class="custom-checkbox" [ngClass]="{'custom-control': isCheckboxType}"> <ng-container *ngIf="isCheckboxType"> <!--suppress HtmlFormInputWithoutLabel --> <input class="custom-control-input" [title]="title" [ngClass]="{'is-invalid': invalid, 'is-valid': valid}" type="checkbox" [id]="id" [required]="required" [(ngModel)]="value"> <label class="custom-control-label" [for]="id">{{label}}</label> </ng-container> <ng-container *ngIf="isToggleType"> <input class="custom-control-input tgl tgl-light" [title]="title" [ngClass]="{'is-invalid': invalid, 'is-valid': valid}" type="checkbox" [id]="id" [required]="required" [(ngModel)]="value"> <label class="tgl-btn" [for]="id"></label> <label class="tgl-label" [for]="id" *ngIf="label">{{label}}</label> </ng-container> <div class="valid-feedback" *ngIf="valid && validMessage">{{validMessage}}</div> <div class="invalid-feedback" *ngIf="errorMessages && errorMessages.length"> <span *ngFor="let message of errorMessages; let last = last;"> {{message}}<br *ngIf="!last"> </span> </div> </div> `, styles: [`.tgl{display:none}.tgl,.tgl *,.tgl :after,.tgl :before,.tgl+.tgl-btn,.tgl:after,.tgl:before{box-sizing:border-box}.tgl ::-moz-selection,.tgl :after::-moz-selection,.tgl :before::-moz-selection,.tgl+.tgl-btn::-moz-selection,.tgl::-moz-selection,.tgl:after::-moz-selection,.tgl:before::-moz-selection{background:0 0}.tgl ::selection,.tgl :after::selection,.tgl :before::selection,.tgl+.tgl-btn::selection,.tgl::selection,.tgl:after::selection,.tgl:before::selection{background:0 0}.tgl+.tgl-btn{outline:0;display:inline-block;vertical-align:middle;margin-right:15px;width:4em;height:2em;position:relative;cursor:pointer;margin-bottom:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.tgl+.tgl-btn:after,.tgl+.tgl-btn:before{position:relative;display:block;content:"";width:50%;height:100%}.tgl+.tgl-btn:after{left:0}.tgl+.tgl-btn:before{display:none}.tgl:checked+.tgl-btn:after{left:50%}.tgl-light+.tgl-btn{background:#f0f0f0;border-radius:2em;padding:2px;transition:all .4s ease}.tgl-light+.tgl-btn:after{border-radius:50%;background:#fff;transition:all .2s ease}.tgl-light:checked+.tgl-btn{background:#9fd6ae}.custom-control-input~.tgl-label{margin-bottom:0;vertical-align:middle}.custom-control-input.is-invalid~.tgl-label{color:#dc3545}.custom-control-input.is-valid~.tgl-label{color:#28a745}`], providers: [ { provide: NG_VALUE_ACCESSOR, useExisting: FormToggleComponent, multi: true }, { provide: NG_VALIDATORS, useExisting: FormToggleComponent, multi: true } ] },] }, ]; FormToggleComponent.propDecorators = { type: [{ type: Input }] }; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ class FormControlModule { } FormControlModule.decorators = [ { type: NgModule, args: [{ imports: [ BrowserModule, FormsModule, ], declarations: [ FormCheckboxComponent, FormInputComponent, FormRadioComponent, FormSelectComponent, FormSelect2Component, FormTextareaComponent, FormToggleComponent, ], exports: [ FormCheckboxComponent, FormInputComponent, FormRadioComponent, FormSelectComponent, FormSelect2Component, FormTextareaComponent, FormToggleComponent, ] },] }, ]; /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ /** * @fileoverview added by tsickle * @suppress {checkTypes,extraRequire,uselessCode} checked by tsc */ export { FormCheckboxComponent, FormInputComponent, FormRadioComponent, FormSelectComponent, FormSelect2Component, FormTextareaComponent, FormToggleComponent, FormControlModule, BaseControlComponent