Node.js+Express+Handlebars:如何通过下拉菜单更新表行全部字段?
解决选中库存编号后自动填充描述和售价的问题
你的核心需求是选中下拉菜单里的库存编号后,自动把对应库存的描述和售价填充到新增行的输入框里。原代码的问题在于下拉选项没有携带额外的库存数据,导致前端无法获取对应字段的值,以下是修正方案:
问题分析
- 下拉菜单的
<option>只显示了partnr,没有存储对应的description和salesprice,所以updateFields函数无法拿到正确的填充值 updateFields函数错误地使用selectedOption.text(也就是partnr)去填充描述和售价字段fetch_data函数逻辑冗余,重复给同一个select元素赋值,且返回的数据结构没有包含完整的库存信息
修正步骤
1. 修改后端接口,返回完整库存数据
在Express的/parts/get_stock接口里,返回包含itemnr、description、salesprice的完整对象:
// Express 后端示例 app.get('/parts/get_stock', async (req, res) => { try { // 查询STOCK表的完整数据 const stockItems = await db.query('SELECT itemnr, description, salesprice FROM STOCK'); res.json(stockItems.rows); } catch (err) { console.error(err); res.status(500).json({ error: 'Failed to fetch stock' }); } });
2. 修正前端下拉菜单的渲染逻辑
去掉冗余的{{#selected}}块,直接渲染所有库存选项,并把description和salesprice存在option的data-*属性里:
<!-- 替换原下拉菜单代码 --> <select name="partnr" id="partnr" onchange="updateFields(this)" required> <option value="">Select a part</option> {{#each stockItems}} <option value="{{itemnr}}" data-description="{{description}}" data-salesprice="{{salesprice}}"> {{itemnr}} </option> {{/each}} </select>
3. 修复updateFields函数
从选中的option的data属性里获取对应的描述和售价,填充到输入框:
function updateFields(selectElement) { const row = selectElement.closest('tr'); const selectedOption = selectElement.options[selectElement.selectedIndex]; // 从data属性获取对应值 const description = selectedOption.dataset.description || ''; const salesPrice = selectedOption.dataset.salesprice || ''; // 填充到对应输入框 row.querySelector('[name="description"]').value = description; row.querySelector('[name="sales_price"]').value = salesPrice; }
4. 简化fetch_data函数(可选)
如果不需要动态加载库存(因为Handlebars已经渲染了stockItems),可以去掉自动加载逻辑;如果需要动态加载,修正后的函数如下:
function _(element) { return document.getElementById(element); } function fetch_data() { fetch('/parts/get_stock') .then(response => response.json()) .then(stockItems => { const select = _('partnr'); let html = '<option value="">Select a part</option>'; stockItems.forEach(item => { html += `<option value="${item.itemnr}" data-description="${item.description}" data-salesprice="${item.salesprice}"> ${item.itemnr} </option>`; }); select.innerHTML = html; }) .catch(err => console.error('Failed to fetch stock:', err)); } // 可选:如果需要聚焦时加载库存 // _('partnr').onfocus = fetch_data;
5. 修正新增表单的提交路径
原表单的action="/parts/addit/{{partnr}}"是静态值,改成后端从表单参数里获取partnr:
<!-- 修改新增表单 --> <form action="/parts/addit" method="POST"> <input type="hidden" name="job_nr" value="{{job_nr}}"> <button type="submit">Add</button> </form>
后端接口获取参数并插入数据:
app.post('/parts/addit', async (req, res) => { const { job_nr, partnr, description, quantity, sales_price } = req.body; // 插入到ITEMSUSED表 await db.query( 'INSERT INTO ITEMSUSED (itemnr, description, salesprice, quantity, job_nr) VALUES ($1, $2, $3, $4, $5)', [partnr, description, sales_price, quantity, job_nr] ); res.redirect(`/jobs/updatejob/${job_nr}`); });
完整修正后的前端代码
<div class="text-center"> <h1 class="top-label">Spares Used for Job {{ thisjobnr }}</h1> </div> <div class="container text-center"> <div class="top-menu"> <a href="/jobs/updatejob/{{ thisjobnr }}" class="btnGeneral">Back</a> </div> </div> <p></p> <div style="overflow-x:auto;" > <table class="table center"> <thead> <tr> <th class="tblabel th">Part Number</th> <th class="tblabel th">Description</th> <th class="tblabel th">Quantity</th> <th class="tblabel th text-right">Sales Price</th> <th class="tblabel th text-right">Action</th> </tr> </thead> <tbody> {{#each rows}} <tr> <td class="tbtext td">{{partnr}}</td> <td class="tbtext td">{{description}}</td> <td class="tbtext td">{{quantity}}</td> <td class="tbtext td">{{sales_price}}</td> <td> <form action="/parts/delete/{{partnr}}" method="POST"> <input type="hidden" name="job_nr" value="{{job_nr}}"> <button type="submit">Delete</button> </form> </td> </tr> {{/each}} <tr> <td> <div class="select"> <select name="partnr" id="partnr" onchange="updateFields(this)" required> <option value="">Select a part</option> {{#each stockItems}} <option value="{{itemnr}}" data-description="{{description}}" data-salesprice="{{salesprice}}"> {{itemnr}} </option> {{/each}} </select> </div> </td> <td><input type="text" name="description" readonly></td> <td><input type="text" name="quantity" required></td> <td><input type="text" name="sales_price" readonly></td> <td> <form action="/parts/addit" method="POST"> <input type="hidden" name="job_nr" value="{{job_nr}}"> <button type="submit">Add</button> </form> </td> </tr> </tbody> </table> </div> <script> function updateFields(selectElement) { const row = selectElement.closest('tr'); const selectedOption = selectElement.options[selectElement.selectedIndex]; const description = selectedOption.dataset.description || ''; const salesPrice = selectedOption.dataset.salesprice || ''; row.querySelector('[name="description"]').value = description; row.querySelector('[name="sales_price"]').value = salesPrice; } </script> <script> function _(element) { return document.getElementById(element); } function fetch_data() { fetch('/parts/get_stock') .then(response => response.json()) .then(stockItems => { const select = _('partnr'); let html = '<option value="">Select a part</option>'; stockItems.forEach(item => { html += `<option value="${item.itemnr}" data-description="${item.description}" data-salesprice="${item.salesprice}"> ${item.itemnr} </option>`; }); select.innerHTML = html; }) .catch(err => console.error('Failed to fetch stock:', err)); } // 可选:如果需要聚焦时加载库存 // _('partnr').onfocus = fetch_data; </script>
内容的提问来源于stack exchange,提问作者Stef
相关产品推荐
相关产品推荐

