views/mdc/assets/js/components/events/base.js in voom-presenters-0.2.0 vs views/mdc/assets/js/components/events/base.js in voom-presenters-2.0.0
- old
+ new
@@ -1,51 +1,167 @@
+import 'core-js/features/array/flat';
+import 'core-js/features/array/flat-map';
import {VErrors} from './errors';
import {VUrls} from '../../utils/urls';
export class VBase extends VUrls {
- constructor(options) {
+ constructor(options, root) {
super();
this.options = options;
+ this.root = root;
}
clearErrors() {
- new VErrors().clearErrors();
+ new VErrors(this.root).clearErrors();
}
parentElement() {
- return document.getElementById(this.options.__parent_id__);
+ return this.root.getElementById(this.options.__parent_id__);
}
- inputValues(form) {
- let params = [];
+ /**
+ * taggedInputs retrieves all components matching this event's input_tag
+ * value.
+ * @return {NodeList}
+ */
+ taggedInputs() {
+ const inputTag = this.options.input_tag;
- // If tagged input is asked for. Fetch all the matching tag elements and then call any bound components
- if (this.options.input_tag !== undefined) {
- var taggedInputs = document.querySelectorAll('[data-input-tag=' + this.options.input_tag + ']');
- for (let input of taggedInputs) {
- if (input.vComponent && typeof input.vComponent.prepareSubmit === 'function') {
- input.vComponent.prepareSubmit(params);
- }
+ if (!inputTag) {
+ return [];
+ }
+
+ const selector = `[data-input-tag="${inputTag}"]`;
+ const inputs = this.root.querySelectorAll(selector);
+
+ if (inputs.length < 1) {
+ console.warn(
+ `input_tag ${inputTag} matched 0 elements. Are you sure`
+ + 'you\'ve specified the correct value?'
+ );
+ }
+
+ return inputs;
+ }
+
+ /**
+ * inputs retrieves relevant input elements for this event.
+ *
+ * - If an `input_tag` has been provided, all matching tagged elements are
+ * included.
+ * - If this component is a input element, it is included.
+ * - If this component has input elements, its input elements are included.
+ * If not, the input elements of the nearest container (dialog or content)
+ * are included.
+ * @return {Array<HTMLElement>}
+ */
+ inputs() {
+ const components = [];
+
+ // Collect tagged components, if applicable:
+ if (this.options.input_tag) {
+ const taggedComponents = Array.from(this.taggedInputs())
+ .filter((element) => element.vComponent)
+ .map((element) => element.vComponent);
+
+ components.push(taggedComponents);
+ }
+
+ let comp = this.component();
+
+ if (comp) {
+ // Include ourselves if we're a form field component, but not a
+ // container:
+ if (comp.respondTo('prepareSubmit') && !comp.respondTo('inputs')) {
+ components.push(comp);
}
+ else if (!comp.respondTo('inputs')) {
+ // Defer to the component's closest container (card, content,
+ // dialog, or form) if the component itself does not respond to
+ // `inputs`:
+ comp = this.closestContainer();
+ }
}
- // Let input components push parameters
- let vComp = this.component();
- if (vComp && typeof vComp.prepareSubmit === 'function') {
- vComp.prepareSubmit(params);
+
+ // If the caller requested tagged_inputs assume they only want those inputs posted and
+ // DO NOT include additional input from the component
+ // I reverted this temporarily as it caused some unintended behavior in. I am going to discuss with the dev
+ // team and revisit in a later release.
+ if (comp && comp.respondTo('inputs')) { //} && !this.options.input_tag) {
+ components.push(comp);
}
+
+ // Map components to elements.
+ // Containers are mapped to their child elements.
+ // Form field components are mapped to their own element.
+ const elements = components.flat().flatMap((comp) => {
+ if (comp.respondTo('inputs')) {
+ return Array.from(comp.inputs());
+ }
+ else if (comp.respondTo('prepareSubmit')) {
+ return comp.element;
+ }
+ });
+
+ // Deduplicate:
+ return Array.from(new Set(elements));
+ }
+
+ /**
+ * inputComponents retrieves the Component for each of this event's
+ * relevant input elements.
+ * @return {Array<VBaseComponent>}
+ */
+ inputComponents() {
+ return this.inputs()
+ .filter((element) => element.vComponent)
+ .map((element) => element.vComponent);
+ }
+
+ /**
+ * inputValues retrieves submit values for each of this event's relevant
+ * input elements.
+ * @return {Array}
+ */
+ inputValues() {
+ const params = [];
+
+ this.inputComponents()
+ .filter((comp) => comp.respondTo('prepareSubmit'))
+ .map((comp) => comp.prepareSubmit(params));
+
return params;
}
component() {
- let parent = this.parentElement();
+ const parent = this.parentElement();
+
return parent ? parent.vComponent : null;
}
- validate() {
- let errors = [];
- let comp = this.component();
- if (comp) {
- errors = comp.validate();
+ validate(formData) {
+ return this.inputComponents()
+ .filter((comp) => comp.respondTo('validate'))
+ .map((comp) => comp.validate(formData))
+ .filter((errors) => errors !== true && errors !== undefined);
+ }
+
+ closestContainer() {
+ const element = this.closestContainerElement();
+
+ if (!element) {
+ return null;
}
- return errors;
+
+ return element.vComponent;
+ }
+
+ closestContainerElement() {
+ const comp = this.component();
+
+ if (!(comp && comp.element)) {
+ return null;
+ }
+
+ return comp.element.closest('[data-is-container]');
}
}