You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);
        });
    }

};

错误分析

  1. Submit绑定错误:data-bind="submit: closeBug(detail())"会在页面加载时立即执行closeBug,而非点击按钮时触发,导致传参时机错误。
  2. 参数引用错误:closeBug方法中错误使用self.localDetail,但实际参数是localDetail,且self.detail是Knockout observable,需用self.detail()获取当前值。
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 15:10:44