UNPKG

forms-reactive

Version:

Reactive Form Web Component

167 lines (166 loc) 19.9 kB
import "@ionic/core"; // Do not remove me! needed to render ion-elements import { h } from "@stencil/core"; import { FormBuilder, Validators } from "../../utils/forms"; export class TestComponent { constructor() { this.printedForm = {}; this.isValidating = false; this.forceUpdate = 0; this.isLoading = false; this.debounceTime = 100; this.options = []; this.subscriptions = []; } componentWillLoad() { this.options = ['Option 1', 'Option 2', 'Option 3']; this.formGroup = new FormBuilder().group({ textInputEmpty: ['', [Validators.required, Validators.email]], textInput: ['default text at initializer', [Validators.required, Validators.minLength(30)]], textInputPatched: ['', [Validators.required, Validators.maxLength(5)]], numericInputEmpty: [null, [Validators.required, Validators.email]], numericInput: [10, [Validators.required, Validators.minLength(30)]], numericInputPatched: [null, [Validators.required, Validators.maxLength(5)]], selectPickerInputEmpty: ['', null, this.asyncValidator('Option 3')], selectPickerInput: ['Option 2', Validators.required], selectPickerInputPatched: ['', Validators.required], checkboxEmpty: ['', Validators.required], checkboxFalse: [false, Validators.requiredTrue], checkbox: [true], checkboxPatched: [''], toggleEmpty: [null, Validators.required], toggleFalse: [false, Validators.requiredTrue], toggle: [true], togglePatched: [null], radioEmpty: [''], radio: ['tesla'], radioPatched: [''], rangeEmpty: [], range: [10, Validators.min(20)], rangePatched: [{}, Validators.max(10)], rangeDualEmpty: ['', this.rangeMinDistance(10)], rangeDual: [{ lower: 3, upper: 9 }], rangeDualPatched: [], }); this.isLoading = true; setTimeout(() => { this.formGroup.patchValue({ textInputPatched: 'patched value', numericInputPatched: 20, selectPickerInputPatched: 'Option 3', checkboxPatched: true, togglePatched: true, radioPatched: 'tesla', rangePatched: 30, rangeDualPatched: { lower: 6, upper: 12 }, }); this.isLoading = false; }, 1000); this.printedForm = Object.assign({}, this.formGroup.value); // We can subscribe or we can use reactive-form events. They are not affected by debounceTime // this.subscriptions.push(this.formGroup.valueChanges.subscribe(value => this.handleValueChanges(value))); // this.subscriptions.push(this.formGroup.statusChanges.subscribe(state => this.handleStatusChanges(state))); } disconnectedCallback() { this.subscriptions.forEach(s => s.unsubscribe()); } rangeMinDistance(distance) { return (control) => { const { lower, upper } = (control.value || { lower: 0, upper: 0 }); const actual = upper - lower; return actual < distance ? { 'min distance': { distance, actual } } : null; }; } asyncValidator(option) { return (control) => new Promise((resolve) => { this.isValidating = true; setTimeout(() => { resolve(control.value !== option ? { 'wrong option': { option, actual: control.value } } : null); this.isValidating = false; }, 2000); }); } handleValueChanges(ev) { this.printedForm = Object.assign({}, ev.detail); } handleStatusChanges(_state) { // Async validators and touch events require to update the ui after validation occurs // otherwise the component won't be able to render f`ormGroup.controls['controlName'].errors`: // They are updated asyncronously but they only change `formGroup`, which is not a `@State()` // So it's needed to change a `@State()` variable to force ui to re-render this.forceUpdate += 1; } handlePatchRandom() { this.formGroup.patchValue({ textInput: Math.random().toString(36).replace(/[^a-z]+/g, ''), rangeEmpty: Math.floor(Math.random() * 101), }, { emitEvent: true }); } renderChips(control) { var _a; const el = typeof control === 'string' ? (_a = this.formGroup) === null || _a === void 0 ? void 0 : _a.controls[control] : control; return (h("ion-item", { lines: "none" }, h("ion-chip", { color: (el === null || el === void 0 ? void 0 : el.valid) ? 'success' : 'danger', mode: "ios", outline: el === null || el === void 0 ? void 0 : el.valid }, h("ion-label", null, "valid: ", (el === null || el === void 0 ? void 0 : el.valid) ? 'true' : 'false')), h("ion-chip", { color: (el === null || el === void 0 ? void 0 : el.invalid) ? 'danger' : 'success', mode: "ios", outline: el === null || el === void 0 ? void 0 : el.invalid }, h("ion-label", null, "invalid: ", (el === null || el === void 0 ? void 0 : el.invalid) ? 'true' : 'false')), h("ion-chip", { color: (el === null || el === void 0 ? void 0 : el.pristine) ? 'primary' : 'secondary', mode: "ios", outline: el === null || el === void 0 ? void 0 : el.pristine }, h("ion-label", null, "pristine: ", (el === null || el === void 0 ? void 0 : el.pristine) ? 'true' : 'false')), h("ion-chip", { color: (el === null || el === void 0 ? void 0 : el.touched) ? 'primary' : 'secondary', mode: "ios", outline: el === null || el === void 0 ? void 0 : el.touched }, h("ion-label", null, "touched: ", (el === null || el === void 0 ? void 0 : el.touched) ? 'true' : 'false')), h("ion-chip", { color: (el === null || el === void 0 ? void 0 : el.dirty) ? 'primary' : 'secondary', mode: "ios", outline: el === null || el === void 0 ? void 0 : el.dirty }, h("ion-label", null, "dirty: ", (el === null || el === void 0 ? void 0 : el.dirty) ? 'true' : 'false')))); } renderErrors(control, isAsyncValidation = false) { var _a; const el = typeof control === 'string' ? (_a = this.formGroup) === null || _a === void 0 ? void 0 : _a.controls[control] : control; const isWithErrors = Object.keys((el === null || el === void 0 ? void 0 : el.errors) || {}).length > 0; return (h("ion-item", { lines: "none" }, h("ion-label", { color: isWithErrors ? 'danger' : 'success' }, isWithErrors ? 'Errors: ' : 'No errors', Object.keys((el === null || el === void 0 ? void 0 : el.errors) || {}).map(k => `${k}: ${JSON.stringify(el === null || el === void 0 ? void 0 : el.getError(k))}`)), isAsyncValidation && this.isValidating ? h("ion-spinner", { name: "dots" }) : null)); } renderTextInput() { return (h("span", null, h("ion-card", null, h("ion-card-header", null, h("ion-card-title", null, "Email empty value")), h("ion-item", { lines: "full" }, h("ion-input", { type: "text", "rf-ctrl": "textInputEmpty" })), this.renderChips('textInputEmpty'), this.renderErrors('textInputEmpty')), h("ion-card", null, h("ion-card-header", null, h("ion-card-title", null, "Text input default value")), h("ion-item", { lines: "full" }, h("ion-input", { type: "text", "rf-ctrl": "textInput" })), this.renderChips('textInput'), this.renderErrors('textInput')), h("ion-card", null, h("ion-card-header", null, h("ion-card-title", null, "Text input patched value")), h("ion-item", { lines: "full" }, h("ion-input", { type: "text", "rf-ctrl": "textInputPatched" })), this.renderChips('textInputPatched'), this.renderErrors('textInputPatched')), h("ion-card", null, h("ion-card-header", null, h("ion-card-title", null, "Text input missing control in form group")), h("ion-item", { lines: "full" }, h("ion-input", { type: "text", "rf-ctrl": "textInputMissingControl" })), this.renderChips('textInputMissingControl'), this.renderErrors('textInputMissingControl')), h("ion-card", null, h("ion-card-header", null, h("ion-card-title", null, "Text input error: empty html attribute")), h("ion-item", { lines: "full" }, h("ion-input", { type: "text", "rf-ctrl": "" }))))); } renderNumericInput() { return (h("span", null, h("ion-card", null, h("ion-card-header", null, h("ion-card-title", null, "Numeric input default value")), h("ion-item", { lines: "full" }, h("ion-input", { type: "number", "rf-ctrl": "numericInput" })), this.renderChips('numericInput'), this.renderErrors('numericInput')), h("ion-card", null, h("ion-card-header", null, h("ion-card-title", null, "Numeric input patched value")), h("ion-item", { lines: "full" }, h("ion-input", { type: "number", "rf-ctrl": "numericInputPatched" })), this.renderChips('numericInputPatched'), this.renderErrors('numericInputPatched')), h("ion-card", null, h("ion-card-header", null, h("ion-card-title", null, "Numeric input missing control in form group")), h("ion-item", { lines: "full" }, h("ion-input", { type: "number", "rf-ctrl": "numericInputMissingControl" })), this.renderChips('numericInputMissingControl'), this.renderErrors('numericInputMissingControl')))); } renderSelect() { return (h("span", null, h("ion-card", null, h("ion-card-header", null, h("ion-card-title", null, "Select empty value, async validators")), h("ion-item", { lines: "full" }, h("ion-label", null, "Select:"), h("ion-select", { "rf-ctrl": "selectPickerInputEmpty", "rf-self-hosted": true, interface: "popover" }, this.options.map(opt => h("ion-select-option", { value: opt }, opt)))), this.renderChips('selectPickerInputEmpty'), this.renderErrors('selectPickerInputEmpty', true)), h("ion-card", null, h("ion-card-header", null, h("ion-card-title", null, "Select default value")), h("ion-item", { lines: "full" }, h("ion-label", null, "Select:"), h("ion-select", { "rf-ctrl": "selectPickerInput", interface: "popover" }, this.options.map(opt => h("ion-select-option", { value: opt }, opt)))), this.renderChips('selectPickerInput'), this.renderErrors('selectPickerInput')), h("ion-card", null, h("ion-card-header", null, h("ion-card-title", null, "Select patched value")), h("ion-item", { lines: "full" }, h("ion-label", null, "Select:"), h("ion-select", { "rf-ctrl": "selectPickerInputPatched", "rf-self-hosted": true, interface: "popover" }, this.options.map(opt => h("ion-select-option", { value: opt }, opt)))), this.renderChips('selectPickerInputPatched'), this.renderErrors('selectPickerInputPatched')))); } renderCheckbox() { return (h("span", null, h("ion-card", null, h("ion-card-header", null, h("ion-card-title", null, "Checkbox empty value, required to be changed")), h("ion-item", { lines: "full" }, h("ion-label", null, "Checkbox"), h("ion-checkbox", { "rf-ctrl": "checkboxEmpty", slot: "start" })), this.renderChips('checkboxEmpty'), this.renderErrors('checkboxEmpty')), h("ion-card", null, h("ion-card-header", null, h("ion-card-title", null, "Checkbox default value (False), required True")), h("ion-item", { lines: "full" }, h("ion-label", null, "Checkbox"), h("ion-checkbox", { "rf-ctrl": "checkboxFalse", slot: "start" })), this.renderChips('checkboxFalse'), this.renderErrors('checkboxFalse')), h("ion-card", null, h("ion-card-header", null, h("ion-card-title", null, "Checkbox default value (True)")), h("ion-item", { lines: "full" }, h("ion-label", null, "Checkbox"), h("ion-checkbox", { "rf-ctrl": "checkbox", slot: "start" })), this.renderChips('checkbox'), this.renderErrors('checkbox')), h("ion-card", null, h("ion-card-header", null, h("ion-card-title", null, "Checkbox patched value")), h("ion-item", { lines: "full" }, h("ion-label", null, "Checkbox"), h("ion-checkbox", { "rf-ctrl": "checkboxPatched", slot: "start" })), this.renderChips('checkboxPatched'), this.renderErrors('checkboxPatched')))); } renderToggle() { return (h("span", null, h("ion-card", null, h("ion-card-header", null, h("ion-card-title", null, "Toggle empty value, required to be changed")), h("ion-item", { lines: "full" }, h("ion-label", null, "Toggle"), h("ion-toggle", { "rf-ctrl": "toggleEmpty", slot: "start" })), this.renderChips('toggleEmpty'), this.renderErrors('toggleEmpty')), h("ion-card", null, h("ion-card-header", null, h("ion-card-title", null, "Toggle default value (False), required True")), h("ion-item", { lines: "full" }, h("ion-label", null, "Toggle"), h("ion-toggle", { "rf-ctrl": "toggleFalse", slot: "start" })), this.renderChips('toggleFalse'), this.renderErrors('toggleFalse')), h("ion-card", null, h("ion-card-header", null, h("ion-card-title", null, "Toggle default value (True)")), h("ion-item", { lines: "full" }, h("ion-label", null, "Toggle"), h("ion-toggle", { "rf-ctrl": "toggle", slot: "start" })), this.renderChips('toggle'), this.renderErrors('toggle')), h("ion-card", null, h("ion-card-header", null, h("ion-card-title", null, "Toggle patched value")), h("ion-item", { lines: "full" }, h("ion-label", null, "Toggle"), h("ion-toggle", { "rf-ctrl": "togglePatched", slot: "start" })), this.renderChips('togglePatched'), this.renderErrors('togglePatched')))); } renderRadio() { return (h("span", null, h("ion-card", null, h("ion-card-header", null, h("ion-card-title", null, "Radio empty value")), h("ion-list", { lines: "none" }, h("ion-radio-group", { "rf-ctrl": "radioEmpty" }, h("ion-item", null, h("ion-label", null, "Tesla"), h("ion-radio", { value: "tesla" })), h("ion-item", { lines: "full" }, h("ion-label", null, "Ford"), h("ion-radio", { value: "ford" }))), this.renderChips('radioEmpty'), this.renderErrors('radioEmpty'))), h("ion-card", null, h("ion-card-header", null, h("ion-card-title", null, "Radio default value")), h("ion-list", { lines: "none" }, h("ion-radio-group", { "rf-ctrl": "radio" }, h("ion-item", null, h("ion-label", null, "Tesla"), h("ion-radio", { value: "tesla" })), h("ion-item", { lines: "full" }, h("ion-label", null, "Ford"), h("ion-radio", { value: "ford" }))), this.renderChips('radio'), this.renderErrors('radio'))), h("ion-card", null, h("ion-card-header", null, h("ion-card-title", null, "Radio patched value")), h("ion-list", { lines: "none" }, h("ion-radio-group", { "rf-ctrl": "radioPatched" }, h("ion-item", null, h("ion-label", null, "Tesla"), h("ion-radio", { value: "tesla" })), h("ion-item", { lines: "full" }, h("ion-label", null, "Ford"), h("ion-radio", { value: "ford" }))), this.renderChips('radioPatched'), this.renderErrors('radioPatched'))))); } renderRange() { return (h("span", null, h("ion-card", null, h("ion-card-header", null, h("ion-card-title", null, "Range empty value")), h("ion-item", null, h("ion-range", { "rf-ctrl": "rangeEmpty", pin: true })), this.renderChips('rangeEmpty'), this.renderErrors('rangeEmpty')), h("ion-card", null, h("ion-card-header", null, h("ion-card-title", null, "Range default value")), h("ion-item", null, h("ion-range", { "rf-ctrl": "range", pin: true })), this.renderChips('range'), this.renderErrors('range')), h("ion-card", null, h("ion-card-header", null, h("ion-card-title", null, "Range patched value")), h("ion-item", null, h("ion-range", { "rf-ctrl": "rangePatched", pin: true })), this.renderChips('rangePatched'), this.renderErrors('rangePatched')))); } renderRangeDual() { return (h("span", null, h("ion-card", null, h("ion-card-header", null, h("ion-card-title", null, "Range Dual empty value")), h("ion-item", null, h("ion-range", { "rf-ctrl": "rangeDualEmpty", dualKnobs: true, min: 0, max: 20, step: 3, snaps: true })), this.renderChips('rangeDualEmpty'), this.renderErrors('rangeDualEmpty')), h("ion-card", null, h("ion-card-header", null, h("ion-card-title", null, "Range Dual default value")), h("ion-item", null, h("ion-range", { "rf-ctrl": "rangeDual", dualKnobs: true, min: 0, max: 20, step: 3, snaps: true })), this.renderChips('rangeDual'), this.renderErrors('rangeDual')), h("ion-card", null, h("ion-card-header", null, h("ion-card-title", null, "Range Dual patched value")), h("ion-item", null, h("ion-range", { "rf-ctrl": "rangeDualPatched", dualKnobs: true, min: 0, max: 20, step: 3, snaps: true })), this.renderChips('rangeDualPatched'), this.renderErrors('rangeDualPatched')))); } renderForm() { return (h("reactive-form", { dataFormGroup: this.formGroup, dataAttributeName: "rf-ctrl", onValueChanges: value => this.handleValueChanges(value), onStatusChanges: state => this.handleStatusChanges(state), dataDebounceTime: this.debounceTime }, h("ion-card", null, h("ion-card-content", null, h("ion-item", { lines: "none" }, h("ion-label", { class: "ion-text-wrap" }, "Reactive form allows to bind a json to a collection of controls. In this example we simulate a timeout of 2 seconds to patch values on the form."), this.isLoading ? h("ion-spinner", { name: "circular", slot: "end" }) : h("ion-icon", { name: "checkmark-outline", slot: "end" })))), h("ion-card", null, h("ion-card-content", null, h("ion-item", null, h("ion-label", { position: "floating" }, "Debounce time (milliseconds)"), h("ion-input", { type: "number", value: this.debounceTime, onIonChange: ev => this.debounceTime = parseInt(ev.detail.value) })))), this.renderTextInput(), this.renderNumericInput(), this.renderSelect(), this.renderCheckbox(), this.renderToggle(), this.renderRadio(), this.renderRange(), this.renderRangeDual())); } renderFormStatus() { return (h("ion-card", { class: "form-values" }, h("ion-card-header", null, h("ion-card-title", null, "Form")), h("ion-card-content", null, h("p", null, "This is the value of the form updated via ", h("code", { class: "code" }, "\u00A0onValueChanges\u00A0"), " event."), h("p", null, "Dirty is triggered by patched values."), h("p", null, this.renderChips(this.formGroup)), this.isValidating ? h("ion-item", { lines: "none" }, "Running async validators \u00A0", h("ion-spinner", { name: "dots" })) : null, h("div", { class: "scrollable" }, h("pre", null, JSON.stringify(this.printedForm, undefined, 4)))), h("ion-footer", null, h("ion-toolbar", null, h("ion-buttons", { slot: "end" }, h("ion-button", { onClick: () => this.handlePatchRandom() }, h("ion-icon", { slot: "start", name: "refresh-outline" }), "Click to patch random value on text input and range")))))); } render() { return [ h("ion-header", { key: '323acb9ca967bc586e2b7a15076471b0fdad01aa' }, h("ion-toolbar", { key: 'ff057ae85d6d36656b7b4d1e40540d7eeacf2769' }, h("ion-title", { key: '04e68a248503273da8d21b96d7c16452ba8ebb70' }, "Reactive forms Web Component", h("br", { key: '542fa9904709a775079b861c293dc9e2b040cbf0' }), h("ion-note", { key: 'dd49011e5ff467f8b4c93def1a2a73e8f54f1729' }, "Developed by AppFeel, a brand of ", h("a", { key: 'a6854e7a25a8a8b6e8c1dba80b821af793526e67', href: "https://bitgenoma.com", target: "_blank", rel: "noreferrer" }, "https://bitgenoma.com"))))), h("ion-content", { key: '798ef79f98d2a9cdc7a64d5659fc80746cff21ca' }, h("ion-grid", { key: '3989a2add0c54814408ef181dc6e5ddd9d0efa90' }, h("ion-row", { key: '3296ea948253b06651ab123afed56bd65cc5a722' }, h("ion-col", { key: 'e0e4a5cb0bf682c783f187588218cc7c2f09685b', size: "6" }, this.renderForm()), h("ion-col", { key: '2a4cb07130e386a6050c6f041d8d32fad34d928c', size: "6" }, this.renderFormStatus())))), ]; } static get is() { return "test-component"; } static get originalStyleUrls() { return { "$": ["test-component.css"] }; } static get styleUrls() { return { "$": ["test-component.css"] }; } static get states() { return { "printedForm": {}, "isValidating": {}, "forceUpdate": {}, "isLoading": {}, "debounceTime": {} }; } } //# sourceMappingURL=test-component.js.map