148 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
			
		
		
	
	
			148 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
| import { convertToCamelCase } from '~/lib/utils/text_utility';
 | |
| 
 | |
| export const serializeFormEntries = (entries) =>
 | |
|   entries.reduce((acc, { name, value }) => Object.assign(acc, { [name]: value }), {});
 | |
| 
 | |
| export const serializeForm = (form) => {
 | |
|   const fdata = new FormData(form);
 | |
|   const entries = Array.from(fdata.keys()).map((key) => {
 | |
|     let val = fdata.getAll(key);
 | |
|     // Microsoft Edge has a bug in FormData.getAll() that returns an undefined
 | |
|     // value for each form element that does not match the given key:
 | |
|     // https://github.com/jimmywarting/FormData/issues/80
 | |
|     val = val.filter((n) => n);
 | |
|     return { name: key, value: val.length === 1 ? val[0] : val };
 | |
|   });
 | |
| 
 | |
|   return serializeFormEntries(entries);
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Check if the value provided is empty or not
 | |
|  *
 | |
|  * It is being used to check if a form input
 | |
|  * value has been set or not
 | |
|  *
 | |
|  * @param {String, Number, Array} - Any form value
 | |
|  * @returns {Boolean} - returns false if a value is set
 | |
|  *
 | |
|  * @example
 | |
|  * returns true for '', [], null, undefined
 | |
|  */
 | |
| export const isEmptyValue = (value) => value == null || value.length === 0;
 | |
| 
 | |
| /**
 | |
|  * A form object serializer
 | |
|  *
 | |
|  * @param {Object} - Form Object
 | |
|  * @returns {Object} - Serialized Form Object
 | |
|  *
 | |
|  * @example
 | |
|  * Input
 | |
|  * {"project": {"value": "hello", "state": false}, "username": {"value": "john"}}
 | |
|  *
 | |
|  * Returns
 | |
|  * {"project": "hello", "username": "john"}
 | |
|  */
 | |
| export const serializeFormObject = (form) =>
 | |
|   Object.fromEntries(
 | |
|     Object.entries(form).reduce((acc, [name, { value }]) => {
 | |
|       if (!isEmptyValue(value)) {
 | |
|         acc.push([name, value]);
 | |
|       }
 | |
|       return acc;
 | |
|     }, []),
 | |
|   );
 | |
| 
 | |
| /**
 | |
|  * Parse inputs of HTML forms generated by Rails.
 | |
|  *
 | |
|  * This can be helpful when mounting Vue components within Rails forms.
 | |
|  *
 | |
|  * If called with an HTML element like:
 | |
|  *
 | |
|  * ```html
 | |
|  * <input type="text" placeholder="Email" value="foo@bar.com" name="user[contact_info][email]" id="user_contact_info_email" data-js-name="contactInfoEmail">
 | |
|  * <input type="text" placeholder="Phone" value="(123) 456-7890" name="user[contact_info][phone]" id="user_contact_info_phone" data-js-name="contactInfoPhone">
 | |
|  * <input type="checkbox" name="user[interests][]" id="user_interests_vue" value="Vue" checked data-js-name="interests">
 | |
|  * <input type="checkbox" name="user[interests][]" id="user_interests_graphql" value="GraphQL" data-js-name="interests">
 | |
|  * ```
 | |
|  *
 | |
|  * It will return an object like:
 | |
|  *
 | |
|  * ```javascript
 | |
|  * {
 | |
|  *   contactInfoEmail: {
 | |
|  *     name: 'user[contact_info][email]',
 | |
|  *     id: 'user_contact_info_email',
 | |
|  *     value: 'foo@bar.com',
 | |
|  *     placeholder: 'Email',
 | |
|  *   },
 | |
|  *   contactInfoPhone: {
 | |
|  *     name: 'user[contact_info][phone]',
 | |
|  *     id: 'user_contact_info_phone',
 | |
|  *     value: '(123) 456-7890',
 | |
|  *     placeholder: 'Phone',
 | |
|  *   },
 | |
|  *   interests: [
 | |
|  *     {
 | |
|  *       name: 'user[interests][]',
 | |
|  *       id: 'user_interests_vue',
 | |
|  *       value: 'Vue',
 | |
|  *       checked: true,
 | |
|  *     },
 | |
|  *     {
 | |
|  *       name: 'user[interests][]',
 | |
|  *       id: 'user_interests_graphql',
 | |
|  *       value: 'GraphQL',
 | |
|  *       checked: false,
 | |
|  *     },
 | |
|  *   ],
 | |
|  * }
 | |
|  * ```
 | |
|  *
 | |
|  * @param {HTMLInputElement} mountEl
 | |
|  * @returns {Object} object with form fields data.
 | |
|  */
 | |
| export const parseRailsFormFields = (mountEl) => {
 | |
|   if (!mountEl) {
 | |
|     throw new TypeError('`mountEl` argument is required');
 | |
|   }
 | |
| 
 | |
|   const inputs = mountEl.querySelectorAll('[name]');
 | |
| 
 | |
|   return [...inputs].reduce((accumulator, input) => {
 | |
|     const fieldName = input.dataset.jsName;
 | |
| 
 | |
|     if (!fieldName) {
 | |
|       return accumulator;
 | |
|     }
 | |
| 
 | |
|     const fieldNameCamelCase = convertToCamelCase(fieldName);
 | |
|     const { id, placeholder, name, value, type, checked } = input;
 | |
|     const attributes = {
 | |
|       name,
 | |
|       id,
 | |
|       value,
 | |
|       ...(placeholder && { placeholder }),
 | |
|     };
 | |
| 
 | |
|     // Store radio buttons and checkboxes as an array so they can be
 | |
|     // looped through and rendered in Vue
 | |
|     if (['radio', 'checkbox'].includes(type)) {
 | |
|       return {
 | |
|         ...accumulator,
 | |
|         [fieldNameCamelCase]: [
 | |
|           ...(accumulator[fieldNameCamelCase] || []),
 | |
|           { ...attributes, checked },
 | |
|         ],
 | |
|       };
 | |
|     }
 | |
| 
 | |
|     return {
 | |
|       ...accumulator,
 | |
|       [fieldNameCamelCase]: attributes,
 | |
|     };
 | |
|   }, {});
 | |
| };
 |