Knockout.js ViewModel中无法访问self属性问题求助
Knockout.js中Close Bug方法无法访问当前Bug详情的修复方案
问题描述
作为JS和HTML新手,在为C# Web API开发Bug追踪前端时,点击Bug列表的「Details」打开详情面板后,点击关闭按钮无法将Bug状态设为closed。ViewModel的closeBug方法始终无法正确访问当前详情的Bug属性,尝试传参和访问self.detail均未解决。
原Index.cshtml代码
<div class="col-md-4"> <div class="panel panel-default"> <div class="panel-heading"> <h2 class="panel-title">Bugs</h2> </div> <div class="panel-body"> <ul class="list-unstyled" data-bind="foreach: bugs"> <li> <strong><span data-bind="text: Title"></span></strong>: <span data-bind="text: Description"></span> <small><a href="#" data-bind="click: $parent.getBugDetail">Details</a></small> </li> </ul> </div> </div> <div class="alert alert-danger" data-bind="visible: error"><p data-bind="text: error"> </p></div> <!-- ko if:detail() --> @* Bug Detail with Close Button *@ <div class="col-md-4"> <div class="panel panel-default"> <div class="panel-heading"> <h2 class="panel-title">Detail</h2> </div> <table class="table"> <tr><td>Title</td><td data-bind="text: detail().Title"></td></tr> <tr><td>Description</td><td data-bind="text: detail().Description"></td></tr> <tr><td>Status</td><td data-bind="text: detail().Status"></td></tr> <tr><td>Created</td><td data-bind="text: detail().Created"></td></tr> <tr><td>Owner</td><td data-bind="text: detail().Owner"></td></tr> </table> <div class="panel-body"> <form class="form-horizontal" data-bind="submit: closeBug(detail())"> <button type="submit" class="btn btn-default">Close bug</button> </form> </div> </div> </div> <!-- /ko -->
原app.js代码
var ViewModel = function () { var self = this; self.bugs = ko.observableArray(); self.error = ko.observable(); self.detail = ko.observable(); self.getBugDetail = function (item) { ajaxHelper(bugsUri + item.Id, 'GET').done(function (data) { self.detail(data); }); } var bugsUri = '/api/bugs/'; function ajaxHelper(uri, method, data) { self.error(''); // Clear error message return $.ajax({ type: method, url: uri, dataType: 'json', contentType: 'application/json', data: data ? JSON.stringify(data) : null }).fail(function (jqXHR, textStatus, errorThrown) { self.error(errorThrown); }); } // get open bugs function getAllBugs() { ajaxHelper(bugsUri, 'GET').done(function (data) { self.bugs(data); }); } // Fetch the initial data. getAllBugs(); //close bug self.closeBug = function (localDetail) { var closedBug = { OwnerId: self.localDetail.OwnerId, Description: self.localDetail.Description, Status: "closed", Title: self.localDetail.Title, Created: self.localDetail.Created }; ajaxHelper(bugsUri + self.localDetail.Id, 'DELETE', self.localDetail.Id); ajaxHelper(bugsUri, 'POST', closedBug).done(function (item) { self.bugs.push(item); }); } };
错误分析
- Submit绑定错误:
data-bind="submit: closeBug(detail())"会在页面加载时立即执行closeBug,而非点击按钮时触发,导致传参时机错误。 - 参数引用错误:
closeBug方法中错误使用self.localDetail,但实际参数是localDetail,且self.detail是Knockout observable,需用self.detail()获取当前值。 - API调用逻辑问题:DELETE请求的参数格式不正确,且删除原Bug后再POST新Bug的逻辑不符合REST规范(通常直接PUT更新状态更合理)。
修复步骤
1. 修改HTML中的Submit绑定
将表单的submit绑定改为传递函数引用,而非立即调用:
<form class="form-horizontal" data-bind="submit: closeBug"> <button type="submit" class="btn btn-default">Close bug</button> </form>
2. 修正ViewModel中的closeBug方法
//close bug self.closeBug = function () { // 获取当前详情的Bug对象 var currentBug = self.detail(); if (!currentBug) return; // 防止未选择Bug时执行 var closedBug = { Id: currentBug.Id, OwnerId: currentBug.OwnerId, Description: currentBug.Description, Status: "closed", Title: currentBug.Title, Created: currentBug.Created }; // 推荐:直接PUT更新原Bug的状态 ajaxHelper(bugsUri + currentBug.Id, 'PUT', closedBug).done(function (updatedBug) { // 更新bugs数组中的对应项 var index = self.bugs().findIndex(bug => bug.Id === updatedBug.Id); if (index !== -1) { self.bugs.splice(index, 1, updatedBug); } self.detail(null); // 关闭详情面板 }); // 若坚持原删除+创建逻辑,使用以下代码: // ajaxHelper(bugsUri + currentBug.Id, 'DELETE').done(function() { // delete closedBug.Id; // 新Bug由API生成Id // ajaxHelper(bugsUri, 'POST', closedBug).done(function (item) { // self.bugs.push(item); // self.detail(null); // }); // }); }
3. 可选:优化getBugDetail方法
若需要响应式更新,可将返回数据转为observable对象:
self.getBugDetail = function (item) { ajaxHelper(bugsUri + item.Id, 'GET').done(function (data) { // 使用ko.mapping插件快速转为observable self.detail(ko.mapping.fromJS(data)); // 无插件时手动创建: // self.detail({ // Id: ko.observable(data.Id), // Title: ko.observable(data.Title), // Description: ko.observable(data.Description), // Status: ko.observable(data.Status), // Created: ko.observable(data.Created), // OwnerId: ko.observable(data.OwnerId) // }); }); }
关键说明
- Knockout的observable必须通过
()获取或设置值,比如self.detail()获取当前详情对象。 - 表单submit绑定应传递函数名,而非函数调用,否则会立即执行。
- REST规范中更新资源优先使用PUT请求,而非删除后重新创建。
内容的提问来源于stack exchange,提问作者Krabbe's-Tykke Lobbe's-Terre
相关产品推荐
相关产品推荐

