HTML 从值中输入集合类型

HTML input set type from value

是否有任何已知的方法通过检查为输入设置的默认值来获取输入标签类型?

即如果输入默认值设置为 true 或 false 输入类型应该是复选框并且对于日期和所有其他类型都相同

这是我要检测字段值的代码(有date props和bool props,但它们都呈现为type text input)

Object.keys(user).map(key => <h4>{key} : <input type="auto-detect" value={user[key]} /></h4>)

这是我从 api

得到的 user 的值
{
  id: 18273009, // input type="number"
  firstName: "admin", // input type="text"
  lastName: "user", // input type="text"
  email: "admin@gmail.com", // 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>
}

仅根据值来确定类型是不可行的。恰当的例子 - 考虑 firstNameemail 都具有值 admin。因此,以下内容就足够了。

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...
}

创建表单控件

来自对象文字 Key/Value 对

user 对象中的一些值无效(您至少应该为 email 提供一个电子邮件地址,为 phone 提供 10 位数字)。所以我已经更正并修改了 user 对象,以便它可以模拟真实数据。

type="datetime" 已弃用,请使用 type="datetime-local"

图一

下例中有 7 个函数:

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

详情见下方示例

const user = {
  idn: 18273009, // input type="number"
  first: 'Zer0', // input type="text"
  last: '0ne', // input type="text"
  email: 'admin@mail.com', // 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, ' ');
  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;
}