AngularJS中如何在表格ng-change事件后保持输入框焦点?
问题:AngularJS表格修改数量后输入框丢失焦点
我碰到个麻烦事儿:当修改表格里商品数量的输入框值时,触发ng-change事件后,输入框会立刻失去焦点,没法连续输入数字。
(场景说明:表格中的数量输入框在输入数值后,焦点自动离开输入框,无法继续输入)
相关代码
AngularJS 控制器的ng-change逻辑
$scope.txtqty = function (item) { var id = item.Id; $http.put("/api/Products/txtqty/" + id, item) .then(function (response) { $scope.gridproducts = response.data; }) }
HTML表格代码
<tbody> <tr ng-repeat="item in gridproducts"> <td ng-click="griddelete(item.Id)"><a class="delete"><i class="fa fa-times-circle-o"></i></a></td> <td class="name">{{item.ProductName}}</td> <td>{{item.ProductRate}}</td> <td><input class="form-control qty" type="text" style="width:50px" ng-change="txtqty(item)" ng-model="item.ProductQty" value="{{item.ProductQty}}"></td> <td>{{item.TotalAmount}}</td> </tr> <tr></tr> <tfoot> <tr> <th colspan="2"></th> <th colspan="2"><b>Total</b></th> <th><b>{{gridproducts[0].TotalBill}}</b></th> </tr> <tr> <th colspan="2"><b></b></th> <th colspan="2"><b>Total Items</b></th> <th><b>25</b></th> </tr> </tfoot> </table>
问题根源
为啥会这样呢?核心原因是你替换了整个gridproducts数组:每次请求后端成功后,你直接把响应数据赋值给$scope.gridproducts,AngularJS检测到数组引用变化,就会重新渲染整个ng-repeat列表。原来的输入框是列表里的一个DOM元素,重新渲染后这个元素被销毁再重建,焦点自然就丢失了。
最优解决方案:局部更新数据,而非替换整个数组
不需要把整个数组换掉,只需要更新被修改的那一项数据就行。这样ng-repeat只会更新对应的行,不会动其他DOM元素,输入框的焦点就能稳稳保留。
修改后的控制器代码:
$scope.txtqty = function (item) { var id = item.Id; $http.put("/api/Products/txtqty/" + id, item) .then(function (response) { // 从响应数据里找到修改后的那一项 const updatedItem = response.data.find(product => product.Id === id); if (updatedItem) { // 找到原数组中对应项的位置,替换它 const targetIndex = $scope.gridproducts.findIndex(product => product.Id === id); $scope.gridproducts[targetIndex] = updatedItem; // 如果需要更新汇总的TotalBill,单独更新(因为它在数组第一项) if ($scope.gridproducts[0]) { $scope.gridproducts[0].TotalBill = response.data[0].TotalBill; } } }) }
另外,HTML输入框里的value="{{item.ProductQty}}"是多余的——ng-model已经负责数据绑定了,重复写反而可能引发冲突,建议删掉:
<td><input class="form-control qty" type="text" style="width:50px" ng-change="txtqty(item)" ng-model="item.ProductQty"></td>
备选方案:手动恢复焦点(迫不得已时用)
如果因为后端返回结构限制,必须替换整个数组,那可以在DOM重新渲染后手动把焦点切回去。步骤如下:
- 给输入框加一个唯一ID:
<td><input id="qty-input-{{item.Id}}" class="form-control qty" type="text" style="width:50px" ng-change="txtqty(item)" ng-model="item.ProductQty"></td>
- 修改控制器代码,用
$timeout等待DOM渲染完成后恢复焦点(记得给控制器注入$timeout服务):
$scope.txtqty = function (item) { var id = item.Id; // 先记下当前输入框的ID const targetInputId = `qty-input-${id}`; $http.put("/api/Products/txtqty/" + id, item) .then(function (response) { $scope.gridproducts = response.data; // 用$timeout让AngularJS先完成DOM更新,再获取焦点 $timeout(() => { document.getElementById(targetInputId).focus(); }); }) }
不过还是优先用第一种局部更新的方案,它不仅能解决焦点问题,还减少了不必要的DOM渲染,性能更好,也更符合AngularJS的数据绑定思路。
内容的提问来源于stack exchange,提问作者Umar Asif
相关产品推荐
相关产品推荐

