TypeScript动态元素onclick调用方法报错:readListItem未定义求助
问题
我是TypeScript新手,正在开发一款CRUD应用,该应用会生成带按钮的表格,点击按钮就能填充表单更新对应条目。现在输入条目ID点按钮能实现功能,但想直接给按钮绑定对应条目ID简化操作。
我写了两个方法:readAllItems用来获取所有条目并生成表格,readListItem根据ID获取指定条目。原生JS里直接在onclick属性加带参数的函数调用没问题,但现在生成的HTML里虽然有调用readListItem的语句,点击按钮却报错“readListItem未定义”,就算把readListItem放在readAllItems前面也没用。
编辑说明:已经更新代码,删掉了不必要的行,现在只需要把forEach循环里生成的按钮绑定到readListItem方法,让它根据条目ID执行功能。
private readAllItems() : void{ this._getListItems().then(listItems => { let html: string = '<table border=1 width=100% style="border-collapse: collapse;">'; html += '<th>ID Edit</th><th>Title</th> <th>Vendor</th><th>Description</th><th>Name</th><th>Version</th>'; listItems.forEach(listItem => { html += `<tr> <td><button class="editButton" onclick=${this.readListItem(listItem.Id)} data-item-id="${listItem.Id}">Edit</button></td> <td>${listItem.Title}</td> <td>${listItem.SoftwareVendor}</td> <td>${listItem.SoftwareDescription}</td> <td>${listItem.SoftwareName}</td> <td>${listItem.SoftwareVersion}</td> </tr>`; }); html += '</table>'; const listContainer: Element = this.domElement.querySelector('#divStatus'); listContainer.innerHTML = html; }); } private readListItem(id): void { this._getListItemByID(id).then(listItem => { document.getElementById("txtSoftwareTitle")["value"] = listItem.Title; document.getElementById("ddlSoftwareVendor")["value"] = listItem.SoftwareVendor; document.getElementById("txtSoftwareDescription")["value"] = listItem.SoftwareDescription; document.getElementById("txtSoftwareName")["value"] = listItem.SoftwareName; document.getElementById("txtSoftwareVersion")["value"] = listItem.SoftwareVersion; }) .catch(error => { let message: Element = this.domElement.querySelector('#divStatus'); message.innerHTML = "Read: Could not fetch details.. "+error.message; }); }
解决方案
你的问题出在两个核心点:
- 内联onclick的写法错误:当前代码
onclick=${this.readListItem(listItem.Id)}会在生成HTML时立即执行readListItem方法,最终onclick属性值是该方法的返回值(这里是undefined),点击按钮时自然无有效执行逻辑。 - 私有方法的作用域问题:
readListItem是类的私有方法,内联onclick代码属于全局作用域,无法访问类的私有成员,因此会提示“readListItem未定义”。
下面是两种可行的解决方式:
方法一:事件委托(推荐)
生成HTML时仅保留data-item-id属性,给表格容器绑定统一的点击事件,通过事件冒泡处理按钮点击:
private readAllItems() : void{ this._getListItems().then(listItems => { let html: string = '<table border=1 width=100% style="border-collapse: collapse;">'; html += '<th>ID Edit</th><th>Title</th> <th>Vendor</th><th>Description</th><th>Name</th><th>Version</th>'; listItems.forEach(listItem => { html += `<tr> <td><button class="editButton" data-item-id="${listItem.Id}">Edit</button></td> <td>${listItem.Title}</td> <td>${listItem.SoftwareVendor}</td> <td>${listItem.SoftwareDescription}</td> <td>${listItem.SoftwareName}</td> <td>${listItem.SoftwareVersion}</td> </tr>`; }); html += '</table>'; const listContainer: Element = this.domElement.querySelector('#divStatus'); listContainer.innerHTML = html; // 添加事件委托 listContainer.addEventListener('click', (e) => { const target = e.target as HTMLElement; if (target.classList.contains('editButton')) { const itemId = target.dataset.itemId; if (itemId) { this.readListItem(itemId); } } }); }); }
这种方式无需逐个绑定事件,后续表格内容更新时事件逻辑依然有效,同时避免了作用域问题。
方法二:创建真实DOM元素绑定事件
不通过字符串拼接HTML,而是创建真实DOM节点后直接绑定事件,用箭头函数保留类实例的this指向:
private readAllItems() : void{ this._getListItems().then(listItems => { const table = document.createElement('table'); table.style.border = '1px solid'; table.style.width = '100%'; table.style.borderCollapse = 'collapse'; // 构建表头 const headerRow = document.createElement('tr'); ['ID Edit', 'Title', 'Vendor', 'Description', 'Name', 'Version'].forEach(text => { const th = document.createElement('th'); th.textContent = text; headerRow.appendChild(th); }); table.appendChild(headerRow); // 构建表格行 listItems.forEach(listItem => { const row = document.createElement('tr'); // 编辑按钮单元格 const buttonCell = document.createElement('td'); const editButton = document.createElement('button'); editButton.className = 'editButton'; editButton.textContent = 'Edit'; // 绑定点击事件,箭头函数确保this指向当前类实例 editButton.addEventListener('click', () => { this.readListItem(listItem.Id); }); buttonCell.appendChild(editButton); row.appendChild(buttonCell); // 其他单元格 [listItem.Title, listItem.SoftwareVendor, listItem.SoftwareDescription, listItem.SoftwareName, listItem.SoftwareVersion].forEach(text => { const cell = document.createElement('td'); cell.textContent = text; row.appendChild(cell); }); table.appendChild(row); }); const listContainer: Element = this.domElement.querySelector('#divStatus'); listContainer.innerHTML = ''; listContainer.appendChild(table); }); }
这种方式通过操作真实DOM元素,直接绑定事件,彻底规避内联事件的作用域问题。
内容的提问来源于stack exchange,提问作者OMGDrAcula
相关产品推荐
相关产品推荐

