'HTML input set type from value
Is there any known way of getting the input tag type by checking the default value set for that input?
i.e. if the input default value is set to true or false the input type should be checkbox and the same for date and all other types
here is the code that I want to detect the field value (there are date props and bool props, but they are all rendered as type text input)
Object.keys(user).map(key => <h4>{key} : <input type="auto-detect" value={user[key]} /></h4>)
And this is the value of user
that I get from the api
{
id: 18273009, // input type="number"
firstName: "admin", // input type="text"
lastName: "user", // input type="text"
email: "[email protected]", // input type="email"
phone: "9178888888", // input type="tel"
address: "123 abc Dr", // input type="text"
isLogged: "b2e74b00-e1a6-44bd-833f-7dba2e98c10e", // input type="text"
loginTime: "2022-04-12T17:20:00.2176106", // input type="datetime"
dateJoined: "2022-03-30T14:12:13.1303726", // input type="datetime"
isMember: false, // input type="checkbox"
isAdmin: false, // input type="checkbox"
isSysAdmin: false, // input type="checkbox"
planType: "Free", // input type="text"
streamsToday: 194, // input type="number"
listIpAddress: [], // don't render, or render <select>
}
Solution 1:[1]
Create Form Controls
from an Object Literal Key/Value Pairs
A few values from user
object are invalid (you should at least put an email address for email
and 10 digits for phone
). So I have corrected and modified user
object so it can simulate real data.
type="datetime"
is deprecated, use type="datetime-local"
Figure I
In the example below are 7 functions:
Function | Purpose |
---|---|
formatDate(d) |
Date values must be formatted before being set to a type="datetime-local" input. |
typeFilter(k, v) |
Determines what type of form control should be used according to value. |
formControl(k, v, t) |
Generates an object for each key/value. Each object has a value of a htmlString of a form control |
userData(o) |
Processes returns from typeFilter() and formControl() and will return a single object that contains all of the htmlSrings. |
parseHTML(h, n, p=) |
Parses htmlString into real HTML. |
makeLabel(n, p=) |
Creates a <label> |
setID(n, a=) |
Set's/changes attributes of an element |
Details are commented in example below
const user = {
idn: 18273009, // input type="number"
first: 'Zer0', // input type="text"
last: '0ne', // input type="text"
email: '[email protected]', // input type="email"
phone: 14085551234, // input type="tel"
address: '1234 Main St. Springfield, IL 62777', // textarea
logHash: 'b2e74b00-e1a6-44bd-833f-7dba2e98c10e', // textarea
timeLogin: '2022-04-12T17:20:00.2176106', // input type="datetime-local"
dateJoined: '2022-03-30T14:12:13.1303726', // input type="datetime-local"
isMember: true, // input type="checkbox"
isAdmin: false, // input type="checkbox"
isSysAdmin: false, // input type="checkbox"
servicePlan: 'Free', // input type="text"
currentStreams: 194, // input type="number"
listIpAddress: ['127.0.0.1', '155.120.3.2', '213.11.77.6'], // select
};
/**
* timeLogin and dateJoined values must be formatted in
* in order to be assigned as a value of an
* <input type="datetime-local">
* LINE 90
*/
const formatDate = (dateTime) => {
const d = new Date(dateTime);
d.setMinutes(d.getMinutes() - d.getTimezoneOffset());
return d.toISOString().slice(0, 16);
};
/**
* A <key/value> pair is passed and a <key/value/tag> trine is
* returned. It checks values to determine which type of
* form control is needed. A string selector of a tagName or
* a string value of the type property is the <tag> of a
* return.
* Line 122
*/
const typeFilter = (key, val) => {
return Array.isArray(val)
? [key, val, 'select']
: Number.isInteger(val) &&
val.toString().length === 11 &&
val.toString().charAt(0) === '1'
? [key, val, 'tel']
: val.valueOf() === true || val.valueOf() === false
? [key, val, 'checkbox']
: val.toString().includes('@')
? [key, val, 'email']
: Number.isInteger(val)
? [key, val, 'number']
: Date.parse(val)
? [key, val, 'datetime-local']
: typeof val === 'string' && val.length >= 20
? [key, val, 'textarea']
: [key, val, 'text'];
};
/**
* Passes <key/value/tag> trine and returns an array of objects
* -- each object contains a key and htmlString value. Each
* htmlString represents a form control:
* ex. <input name="<key>" type="<tag>" value="<value>">
* LINE 134
*/
const formControl = (key, value, tag) => {
const html = {};
let htmlStr;
switch (tag) {
case 'select':
htmlStr = `<select name="${key}"><option selected>Select an option</option>`;
value.forEach((val) => (htmlStr += `<option>${val}</option>`));
html[key] = htmlStr + `</select>`;
break;
case 'tel':
let val = '' + value;
let hyphenated = val
.split('')
.map((n, i) =>
i === 1
? '-' + n
: i === 4
? '-' + n
: i === 7
? '-' + n
: n
)
.join('');
htmlStr = `<input name='${key}' type='tel' value='${hyphenated}'>`;
html[key] = htmlStr;
break;
case 'checkbox':
let end = value === true ? ` checked>` : ` >`;
htmlStr = `<input name='${key}' type='checkbox'` + end;
html[key] = htmlStr;
break;
case 'email':
htmlStr = `<input name='${key}' type='email' value='${value}'>`;
html[key] = htmlStr;
break;
case 'number':
htmlStr = `<input name='${key}' type='number' value='${value}'>`;
html[key] = htmlStr;
break;
case 'datetime-local':
let dateTime = formatDate(value);
htmlStr = `<input name='${key}' type='datetime-local' value='${dateTime}'>`;
html[key] = htmlStr;
break;
case 'textarea':
htmlStr = `<textarea name='${key}' rows='3' cols='20'>${value}</textarea>`;
html[key] = htmlStr;
break;
case 'text':
htmlStr = `<input name='${key}' type='text' value='${value}'>`;
html[key] = htmlStr;
break;
default:
break;
}
return html;
};
/**
* This is the main function that passes in the <user> object
* and returns a modified <user> object -- the values are
* htmlStrings of form controls with the original values embeded
* in the htmlStrings.
*/
const userData = (obj) => {
let types = [];
/*
- Converts <user> object into an array of pairs.
- On each iteration, the return of typeFilter() is stored in
an array <types>.
*/
for (const [key, value] of Object.entries(obj)) {
types.push(typeFilter(key, value));
}
// console.log(types);
/*
- The <types> array is ran through with .map() and .reduce()
- .map() will run formControl() on each trine of <types> --
the return value is an object with one key and one value.
- .reduce() will merge all of those objects into a single
object
- The single object is then returned.
*/
let html = types
.map(([key, value, tag]) => formControl(key, value, tag))
.reduce((obj, htm) => Object.assign(obj, htm), {});
// console.log(html);
return html;
};
/**
* Just a wrapper for .insertAdjacentHTML().
* @param position is optional, if not defined it defaults to
* 'beforeend'.
*/
const parseHTML = (htmlString, node, position = 'beforeend') => {
node.insertAdjacentHTML(position, htmlString);
};
/**
* Creates a <label> with a formatted name of the element it is
* paired with.
*/
const makeLabel = (node, position = 'beforebegin') => {
let text = node.name.replace(/.*?([A-Z][a-z]*)/g, ' $1');
parseHTML(`<br><label>${text}: </label>`, node, position);
if (node.matches('textarea')) {
const vert = node.previousElementSibling;
vert.style.verticalAlign = 'top';
}
};
/**
* Sets any given attribute's value of a given element. Note, the
* second @param is an array that should have the attribute's
* name and value. If not defined, then it will pass a default:
* ex. ['id', 'IDN18273009x1650056016986']
* The value is "IDN"(user.idn)"x"(UNIX timestamp)
*/
const setID = (node, attr = ['id', 'IDN' + user.idn +'x'+ Date.now()]) =>
node.setAttribute(attr[0], attr[1]);
const html = userData(user);
parseHTML(`<form></form>`, document.body);
const F = document.forms[0];
const FC = F.elements;
parseHTML(html.listIpAddress, F);
parseHTML(html.isMember, F);
parseHTML(html.isSysAdmin, F);
parseHTML(html.address, F);
parseHTML(html.phone, F);
const ipList = FC.listIpAddress;
const member = FC.isMember;
const sysAdmin = FC.isSysAdmin;
const address = FC.address;
const phone = FC.phone;
makeLabel(ipList);
makeLabel(member);
makeLabel(sysAdmin);
makeLabel(address);
makeLabel(phone);
parseHTML(html.dateJoined, ipList, 'afterend');
const joined = FC.dateJoined;
makeLabel(joined);
setID(F);
html {
font: 2ch/1 'Segoe UI';
}
input,
textarea,
select,
label {
display: inline-block;
margin: 0 0.5rem 0.5rem 0;
font: inherit;
}
label {
vertical-align: baseline;
min-width: 9ch;
text-align: right;
text-transform: capitalize;
}
[type='checkbox'] {
margin: 0;
vertical-align: middle;
}
[type='tel'] {
width: 13.5ch;
}
Solution 2:[2]
Deciding the types based on the values alone is not feasible. Cases in point - consider that both firstName
and email
have value admin
. So something along the following lines would suffice.
function autoDetectInputType ( { key, value } ) {
// a switch statement would be fine as well if you like
if ( key === 'email' ) return 'email';
if ( /(?:time|date)/.test( key ) ) return 'datetime';
if ( 'number' === typeof ( value ) ) return 'number';
// and so on and so forth...
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | zer00ne |
Solution 2 | Igwe Kalu |