javascript - 我可以用更新的 Object.fromEntries(new FormData(form)) 替换表单元素循环解析吗?
问题描述
我可以安全地更换这个吗
document.getElementById("frm-Main").addEventListener("submit", (e) =>
{
e.preventDefault();
// const frmMain = e.target;
const frmMain = document.querySelector('#frm-Main');
let params = [];
const inputs = frmMain.getElementsByTagName("input");
console.log(inputs);
for (let i = 0; i < inputs.length; i++)
{
if (inputs[i].type == 'radio' && inputs[i].checked != true) continue;
if (inputs[i].type == 'checkbox' && inputs[i].checked != true) continue;
params.push({ id: inputs[i].name, value: inputs[i].value });
}
const selects = frmMain.getElementsByTagName("select");
for (let i = 0; i < selects.length; i++)
{
params.push({ id: selects[i].name, value: selects[i].value });
}
fetch(url,
{
method: 'POST',
body: JSON.stringify(params),
headers: { 'Content-type': 'application/json; charset=UTF-8' }
}).then(response => response.json()).then(data => console.log(data));
});
有了这个 ?
document.getElementById("frm-Main").addEventListener("submit", (e) =>
{
e.preventDefault();
const frmMain = document.querySelector('#frm-Main');
const formData = Object.fromEntries(new FormData(frmMain));
fetch(url,
{
method: 'POST',
body: JSON.stringify(formData),
headers: { 'Content-type': 'application/json; charset=UTF-8' }
}).then(response => response.json()).then(data => console.log(data));
});
解决方案
在某些情况下,是的,但它通常不是安全的。这将取决于表单数据是否包含具有重复名称的条目。例如,多选可以,复选框组也可以。您还可能在 Safari 中遇到与 radiogroup API 变化相关的问题。
此外,FormData 值可以是文件。您可能需要执行额外的自定义序列化以将这些值转换为 JSON 表示。
以下交互式演示显示了Object.fromEntries
在 formdata 包含具有通用名称的成员的情况下 using 将如何无法正常工作(没有一些额外的先前转换)。如果作为多部分或 urlencoded 表单数据而不是 JSON 表示形式发送,它还显示了在 HTTP 请求正文中出现的输出。
let controller;
form.addEventListener('submit', event => {
event.preventDefault();
update();
});
form.addEventListener('change', update);
update();
function renderEntry([ key, value ]) {
return `[ ${ JSON.stringify(key) }, ${ JSON.stringify(value) } ]`
}
async function update() {
controller?.abort();
let { signal } = controller = new AbortController;
let formData = new FormData(form);
let object = Object.fromEntries(formData);
let rawUSP = await new Response(new URLSearchParams(formData)).text();
let rawFD = await new Response(formData).text();
if (!signal.aborted) {
outputEntries.innerText = Array.from(formData, renderEntry).join('\n');
outputFromEntries.innerText = JSON.stringify(object, null, 2);
outputRawUSP.innerText = rawUSP;
outputRawFD.innerText = rawFD;
}
}
code {
display: block;
padding: 1em 1ch;
white-space: pre;
}
dd, dl, dt {
all: unset;
display: block;
}
dt {
background-color: aquamarine;
font-weight: 700;
padding: 0 1ch;
}
.fields {
display: grid;
grid-gap: 0.5em;
grid-template-columns: min-content 1fr;
}
form {
display: grid;
grid-gap: 1em;
grid-template-columns: 1fr 1fr 1fr;
}
.field {
display: flex;
flex-direction: column;
}
.field:last-child {
grid-column: 1 / span 3;
}
output {
background: azure;
display: block;
font-family: monospace;
margin: 1em 0;
padding: 1ch;
}
<form id="form">
<div class="field">
<label for="foo">Foo</label>
<select multiple id="foo" name="foo">
<option selected>bar</option>
<option selected>baz</option>
</select>
</div>
<div class="field">
<label for="qux">Qux</label>
<input id="qux" name="qux" value="quux">
</div>
<fieldset>
<legend>Corge</legend>
<div class="fields">
<input checked id="grault" name="corge" type="checkbox" value="grault">
<label for="grault">grault</label>
<input id="garply" name="corge" type="checkbox" value="garply">
<label for="garply">garply</label>
<input checked id="waldo" name="corge" type="checkbox" value="waldo">
<label for="waldo">waldo</label>
</div>
</fieldset>
<div class="field">
<label for="output">Output</label>
<output id="output">
<dl>
<dt>FormData entries</dt>
<dd><code id="outputEntries"></code></dd>
<dt>Object.fromEntries</dt>
<dd><code id="outputFromEntries"></code></dd>
<dt>FormData application/x-www-form-urlencoded body</dt>
<dd><code id="outputRawUSP"></code></dd>
<dt>FormData multipart/form-data body</dt>
<dd><code id="outputRawFD"></code></dd>
</dl>
</output>
</div>
</form>
结果缺少“bar”和“grault”(在表单的初始状态)的原因Object.fromEntries
是,随着条目的消费,每次在条目中遇到相同的名称时,都会导致覆盖之前的属性。一个对象只能有一个给定键的自有属性。
这并不意味着您永远不应该将它用于快速的表单到对象的映射。如果您正在使用一个简单的表单,其中所有字段都是单数文本值,那就没问题了。但是您可能需要一个更强大的解决方案来避免以后产生危险,以防有人添加确实需要映射到 JSON 中的其他表示的新字段。
如果创建一个更健壮的映射函数,Object.fromEntries
仍然可能有用。您只需要首先执行从表单到其“json 条目”的映射(想想Object.fromEntries(getFormEntries(form))
)。该FormData
类型不仅不以对 JSON 有意义的方式对数组进行建模——它也不知道其他 JSON 类型,如 Number 和 Boolean。
中间的那个getFormEntries
函数可以写成产生[ name, value ]
对的生成器函数。派生值时,您可能会查看表单控件本身的类型,以确定如何在 JSON 中表示它们的值。例如,element.type === 'select-multiple'
告诉您将 JSON 值基于element.selectedOptions
, whileelement.type === 'number'
或element.type === 'range'
建议您想要element.valueAsNumber
而不是element.value
.
// Pseudo code! — a real implementation would need to account
// for RadioNodeList/its children, deciding whether checkboxes
// are booleans, string-valued, or arrays, and lots of other
// things — but the gist is something like this:
function formToJSON(form) {
return Object.fromEntries(getFormEntries(form));
}
function * getFormEntries(form) {
for (let control of form.elements) {
if (!control.disabled) {
yield [ control.name, getFormControlValue(control) ];
}
}
}
function getFormControlValue(control) {
switch (control.type) {
case 'number':
return control.valueAsNumber;
case 'select-multiple':
return Array.from(control.selectedOptions, option => option.value);
/* ... */
default:
return control.value;
}
}
您还希望排除disabled
. 查看FormData
构造算法可以在这里提供线索,因为您可能想要复制其规则来决定哪个表单控制模型可提交值 - 扭曲只是您想要以不同方式导出这些值的表示。
请注意,将表单值映射到 JSON 没有正确答案。例如,我们应该如何表示 a 的值<input type="date">
?我们可能想使用element.value
( "2021-02-09"
),但也许我们更喜欢用element.valueAsDate.toISOString()
( "2021-02-09T00:00:00.000Z"
) 来表示它。答案将取决于您的具体需求和您正在开发的合同。
推荐阅读
- c++ - VS 2017 & 2019 运行 c++ 真的很慢
- python - 如何将 Matlab 代码转换为 Python?
- r - 如何结合 Lapply() 和 dbListFields() 来获取 DATABASE 中每个表的所有列名?
- javascript - 视频html5结束时淡入图标
- ruby-on-rails - Mongo::Error::OperationFailure Unknown modifier: $pushAll 更新模型时(mongoid、angular、rails)
- php - jquery在while循环php中获取select2的数据属性
- spring - 如何使用 setItems for grid 将数据添加到 Vaadin 中的 CRUD?
- typescript - FirebaseX 没有 firebaseInstanceId
- javascript - HTML5 EventSource 数据函数处理以前的值,而不是当前值
- python-3.x - 找不到pytest模块