diff --git a/index.html b/index.html index 63d58c0..6006115 100644 --- a/index.html +++ b/index.html @@ -63,10 +63,10 @@ Native Checkbox Radio - Un-Checked - Checked - Un-Checked Disabled - Checked Disabled + Un-Checked + Checked + Un-Checked Disabled + Checked Disabled diff --git a/wc/WComponent.js b/wc/WComponent.js index f7ffa87..69b58e4 100644 --- a/wc/WComponent.js +++ b/wc/WComponent.js @@ -22,6 +22,7 @@ class WComponent extends HTMLElement{ this.componentWillRender(); this.render(); this.componentDidRender(); + this.key = new Date().getTime(); } componentWillRender() {} @@ -60,6 +61,9 @@ class WComponent extends HTMLElement{ }); }); } + equals(component) { + return this.key === component.key; + } getAttributeParserByName(name) { if(typeof name !== 'string') { return undefined; diff --git a/wc/components/Form.js b/wc/components/Form.js index b0bbb62..136d8a0 100644 --- a/wc/components/Form.js +++ b/wc/components/Form.js @@ -21,19 +21,19 @@ class Form extends WComponent{ const formData = new FormData(); const elements = Array.from(this.querySelectorAll(FORM_ELEMENT_TYPES)); elements.forEach(elem => { - if(typeof elem.name === 'string' && elem.value !== null && elem.value !== undefined) { - if((elem.type === 'checkbox' || elem.type === 'radio') && !elem.checked) { - // Exclude unchecked checkbox & radio - return; - } - const existingValue = formData.get(elem.name); - if(existingValue === null) { - formData.set(elem.name, elem.value); - } else if(Array.isArray(existingValue)) { - existingValue.push(elem.value); - } else { - formData.set(elem.name, [existingValue, elem.value]); - } + // Exclude elements with invalid name or value + if(typeof elem.name !== 'string' || typeof elem.value !== 'string') { return; } + // Exclude unchecked checkbox & radio + if(elem.type === 'checkbox' && !elem.checked) { return; } + if(elem.type === 'radio' && !elem.checked) { return; } + + const existingValue = formData.get(elem.name); + if(existingValue === null) { + formData.set(elem.name, elem.value); + } else if(Array.isArray(existingValue)) { + existingValue.push(elem.value); + } else { + formData.set(elem.name, [existingValue, elem.value]); } }); return formData; @@ -64,8 +64,12 @@ class Form extends WComponent{ const slot = this.shadowRoot.querySelector('slot'); slot.addEventListener('slotchange', () => this.bindFormAccess()); + // Bind submit event on submit button click const submitBtn = this.querySelector('input[type="submit"], button[type="submit"]'); submitBtn.addEventListener('click', this.submitHandler); + + // Bind radio click callback for name group control + this.querySelectorAll('w-radio').forEach(radio => radio.addEventListener('click', this.radioClickCallback)); } /** * Bind direct form access from document by name. @@ -82,23 +86,23 @@ class Form extends WComponent{ * Dynamically add getters & setters for form elements to get direct access by name. */ bindFormElementAccess() { - // Bind direct form elemnt access from form by name this.querySelectorAll(FORM_ELEMENT_TYPES).forEach(elem => { - if(typeof elem.name === 'string' - && !Object.getOwnPropertyDescriptor(this, elem.name)) { - // Dynamically add getter if there is none defined. - Object.defineProperty(this, elem.name, { - get: () => { - const list = Array.from(this.querySelectorAll(`[name='${elem.name}']`)); - if(list.length > 1) { - const value = list.filter(item => item.checked).map(item => item.value); - list.value = value.length === 1 ? value[0] : value; - return list; - } - return list.length === 0 ? undefined : list[0]; + // Exclude elements with invalid name + if(typeof elem.name !== 'string') { return; } + // Exclude elements which getters are already set + if(Object.getOwnPropertyDescriptor(this, elem.name)) { return; } + + Object.defineProperty(this, elem.name, { + get: () => { + const list = Array.from(this.querySelectorAll(`[name='${elem.name}']`)); + if(list.length > 1) { + const value = list.filter(item => item.checked).map(item => item.value); + list.value = value.length === 1 ? value[0] : value; + return list; } - }); - } + return list.length === 0 ? undefined : list[0]; + } + }); }); } render() { @@ -112,6 +116,17 @@ class Form extends WComponent{ DOM.create('slot', {}, form); } + radioClickCallback = e => { + const radio = e.target; + if(typeof radio.name !== 'string' || radio.disabled) { return; } + + const radios = Array.from(this.querySelectorAll(`w-radio[name='${radio.name}']`)); + radios.forEach(r => { + if(!r.equals(radio)) { + r.checked = false; + } + }); + } submitHandler = e => { this.dispatchEvent(this.events.submit); this.shadowRoot.querySelector('form').submit();