在Knockout JS中绑定和检查单选按钮

时间:2014-03-03 14:31:26

标签: jquery asp.net-mvc knockout.js

在击倒JS时遇到更多问题,并希望有人能够帮助我。 (我不得不问关于Knockout的问题,我想知道是否应该坚持使用jQuery)

我正在尝试使用两个或三个选项动态创建单选按钮列表。每一行都是一组独立的按钮。

我可以把这个清单写好,但我正在努力找到合适的人来检查。

我的HTML是:

<div data-bind="with: functions" id="ActionWindow">
<h2>bob<span data-bind="text: pgroupname"></span></h2>

<h2>Read & Update Functions</h2>

<div>
<table>
    <thead class="FunctionsTableHead">
        <tr>
            <th class="FunctionName">Function</th>
            <th>None</th>
            <th>Read</th>
            <th>Write</th>
        </tr>
    </thead>

    <tbody class="FunctionsTable" data-bind="foreach: pReadFunctions">
        <tr>
            <td class="FunctionName" data-bind="text: FunctionName"></td>
            <td><label><input type="radio" data-bind="attr: { name: FunctionNum + '-ReadAccessLevel'}, checkedValue: 0, checked: AccessLevel" /></label></td>
            <td><label><input type="radio" data-bind="attr: { name: FunctionNum + '-ReadAccessLevel'}, checkedValue: 1, checked: AccessLevel" /></label></td>
            <td><label><input type="radio" data-bind="attr: { name: FunctionNum + '-ReadAccessLevel'}, checkedValue: 2, checked: AccessLevel" /></label></td>
        </tr>
    </tbody>

</table>
</div>

<h2>Action Functions</h2>

    <table>
    <thead class="FunctionsTableHead">
        <tr>
            <th class="FunctionName">Function</th>
            <th>None</th>
            <th>Action</th>
        </tr>
    </thead>

    <tbody class="FunctionsTable" data-bind="foreach: pActionFunctions">
        <tr>
            <td class="FunctionName" data-bind="text: FunctionName"></td>
            <td><label><input type="radio" data-bind="attr: { name: FunctionNum + '-ActionAccessLevel'}, checkedValue: 0, checked: AccessLevel" /></label></td>
            <td><label><input type="radio" data-bind="attr: { name: FunctionNum + '-ActionAccessLevel'}, checkedValue: 1, checked: AccessLevel" /></label></td>
        </tr>
    </tbody>

</table>

我已经为两个单选按钮列表尝试了两种不同的方法,但两种方法都没有效果:

<script type="text/javascript">

//slide animation for lefthand menu 
$('.ListDeptLink').click(function () {
    $(this).nextAll('.SectListDiv').first().slideToggle(500);
});

$('.ListSectLink').click(function () {

    var url = '@Url.Action("DisplayGroup", "AjaxGroup")' + '?GroupId=' + $(this).attr("GroupId") + "&GroupName=" + encodeURIComponent($(this).attr("GroupName"));

    $.ajax({
        url: url,
        contentType: "application/json; charset=utf-8",
        type: 'POST',
        context: this,
        timeout: 60000,
        dataType: 'json',
        tryCount: 0,
        retryLimit: 3,
        success: function (data) {
            viewModel.functions(new functionsModel(data))
        },
        error: function (httpRequest, textStatus, errorThrown) {
            alert("Error");
        }
    });
});

function functionsModel(data) {
    this.pgroupname = ko.observable(data ? data.GroupName : "");
    this.FunctionNum = data ? data.FunctionNum : "";
    this.AccessLevel = data ? data.AccessLevel : "";

    this.pReadFunctions = ko.observableArray((data ? data.ReadFunctionList : [])
        .map(function (item) {
            return {
                FunctionNum: item.FunctionNum,
                FunctionName: item.FunctionName,
                AccessLevel: ko.observable(item.AccessLevel)
            };
        })
    );

    this.pActionFunctions = ko.observableArray((data ? data.ActionFunctionList : [])
        .map(function (item) {
            return {
                FunctionNum: item.FunctionNum,
                FunctionName: item.FunctionName,
                AccessLevel: ko.observable(item.AccessLevel)
            };
        })
    );
};

var wrapper = function () {
    this.functions = ko.observable(new functionsModel(null));
};

var viewModel = new wrapper();

ko.applyBindings(viewModel);

最后回来的数据示例:

ReadFunctionList":[],"ActionFunctionList":     [{"FunctionNum":582,"FunctionName":"Name1","FunctionType":Ignorethis,"GroupNum":1,"AccessLevel":0},{"FunctionNum":502,"FunctionName":"Name2","FunctionType":IgnoreThis,"GroupNum":2,"AccessLevel":0},"GroupName":"Name1"}

AccessLevel决定了应该勾选的内容,因此在本例中,两个项目都应该勾选第一个单选按钮。

提前感谢您的帮助:)

编辑:我已将代码更新到目前为止

2 个答案:

答案 0 :(得分:1)

Javascript问题

我认为包装器中的函数应该是一个可观察的,而不是observableArray。

var wrapper = function () {
    this.functions= ko.observable(new functionsModel(null));
};

您的pReadFunction和pActionFunction项应具有AccessLevel属性作为observable。您无法通过将单选按钮更改为视图模型获得更新。

// Inside $('.ListSectLink').click
this.pReadFunctions = ko.observableArray( (data ? data.ReadFunctionList : [] )
    .map( function(item) {
        return {
            FunctionNum: item.FunctionNum,
            FunctionName: item.FunctionName,
            FunctionType: item.FunctionType,
            GroupNum: item.GroupNum,
            AccessLevel: ko.observable(item.AccessLevel)
        }; 
    })
);

// Repeat for pActionFunctions

在你的jsFiddle中,不要在你的点击处理程序中使用AJAX调用,只需直接分配你的样本数据。如果您真的想模拟异步回调,请使用$ .Deferred / resolveWith。

$('.ListSectLink').click(function () {
    var testData = ...
    viewModel.functions( new functionsModel(testData) );
});

$('.ListSectLink').click(function () {
    var testData = ...
    var deferred = $.Deferred();
    deferred.done( function(data) {
        this.functions( new functionsModel(data) );
    });

    setTimeout( function() {
        deferred.resolveWith(viewModel, [testData]);
    }, 100);

});

HTML /绑定问题

在你的jsFiddle中它缺少包装器 - &gt;函数绑定。这可能是样本不起作用的原因。假设有类似的东西。

<div data-bind="with: functions" />

输入无线电检查绑定不应该针对$ root。这将是您示例中的 wrapper 实例。给定样本JSON,AccessLevel位于每个ReadFunctionList和ActionFunctionList数组项上,因此使用“$ data.AccessLevel”(或只是“AccessLevel”)来检查数据绑定

检查的绑定与元素的值和后备可观察值进行比较。但元素的绑定是字符串,但在您的示例JSON中,它是数字。这不符合。添加checkedValue数据绑定表达式以覆盖每个单选按钮的“值”为数字。

您还需要为pReadFunction单选按钮使用名称attr绑定,就像使用pActionFunction单选按钮将每行分组到一个集合中一样。您还可以考虑在Read和Action中添加另一个前缀,以防FunctionNum在两个集合之间不唯一。

<input type="radio" data-bind="
    attr: { name: 'Action-' + FunctionNum + '-AccessLevel'}, 
    checkedValue: 0,
    checked: AccessLevel" />

<input type="radio" data-bind="
    attr: { name: 'Action-' + FunctionNum + '-AccessLevel'}, 
    checkedValue: 1,
    checked: AccessLevel" />

奖励积分

您也可以使用点击绑定而不是使用jQuery。 click函数将是包装类的函数。您需要传递Url,因为它看起来像是在服务器端生成的。我正在使用一个按钮连接示例的数据绑定。你可能需要帮助razor引擎在data-bind属性中解决客户端和服务器端的问题。我将把它作为练习留给读者;)

这里的好处是所有模型操作都包含在viewModel中。另请注意click数据绑定表达式上的 bind 语句。这是你在函数中获取参数的方法。如果“wrapper”不是根对象,则可能需要将其更改为$ data或$ parent,具体取决于您的realBindings / viewModel结构。在jsFiddle它会工作。

<button data-bind="click: getListSectLink.bind($root, @Url.Action(...)">Get</button>

function wrapper() {
    this.functions = ko.observable( new FunctionsModel(null ) );
    this.getListSectLink = function(url) {
         // ajax stuff
         success: function(data) {
             this.functions( new functionsModel(data) );
         }
    };
}

答案 1 :(得分:1)

问题在于Knockout使用精确比较来确定要选择哪个单选按钮,并且绑定到整数checked值,同时为单选按钮提供字符串attr值。解决方案是使用checkedValue绑定(而不是attr)为单选按钮提供整数值:

<input type="radio" data-bind="checkedValue: FunctionNum, checked: $root.AccessLevel" />

参考:http://knockoutjs.com/documentation/checked-binding.html#parameters