MVC 3 + knockoutjs:在使用EditorFor作为布尔字段时添加data-bind属性

awr*_*ley 6 asp.net-mvc-3 knockout.js

使用@Html.EditorFor(model =>model.IsClient),其中IsClient是布尔值,使用Not Set, Yes and No选项呈现下拉列表.

一切都很好.

现在我想将knockoutjs与我喜欢的结果下拉列表一起使用,那么如何使用@ Html.EditorFor添加data-bind属性,我需要使用knockoutjs来处理这个下拉列表?

我试过了:

@Html.EditorFor(model => model.IsClient, new Dictionary<string, object> { { "data-bind", "value: Account.IsClient" } })
Run Code Online (Sandbox Code Playgroud)

但是,它使用对象additionalViewData参数,并且它不呈现data-bind属性.这可能很自然,因为此参数可能与渲染标记的Html属性无关.

但是,找不到任何合理的文档,并且没有其他重载看起来可能是我想要的候选者.

TIA有什么建议.

Dar*_*rov 10

Brad Wilson 在ASP.NET MVC 2中发布了关于显示和编辑器模板的博客.因此,你可以修改boolean的默认模板并添加你需要的属性(~/Views/Shared/EditorTemplates/MyTemplate.cshtml):

@{
    bool? value = null;
    if (ViewData.Model != null) 
    {
        value = Convert.ToBoolean(ViewData.Model, System.Globalization.CultureInfo.InvariantCulture);
    }

    var triStateValues = new List<SelectListItem> 
    {
        new SelectListItem 
        { 
            Text = "Not Set",
            Value = String.Empty,
            Selected = !value.HasValue 
        },
        new SelectListItem 
        { 
            Text = "True",
            Value = "true",
            Selected = value.HasValue && value.Value 
        },
        new SelectListItem 
        { 
            Text = "False",
            Value = "false",
            Selected = value.HasValue && !value.Value 
        },
    };
}

@if (ViewData.ModelMetadata.IsNullableValueType) 
{
    <!-- TODO: here you can use any attributes you like -->
    @Html.DropDownList(
        "", 
        triStateValues, 
        new { 
            @class = "list-box tri-state", 
            data_bind="value: " + ViewData.TemplateInfo.GetFullHtmlFieldName("") // you could also use ViewData.ModelMetadata.PropertyName if you want to get only the property name and not the entire navigation hierarchy name
        }
    )
} 
else 
{
    @Html.CheckBox("", value ?? false, new { @class = "check-box" })
}
Run Code Online (Sandbox Code Playgroud)

最后:

@Html.EditorFor(model => model.IsClient, "MyTemplate")
Run Code Online (Sandbox Code Playgroud)

或者使用以下属性修饰视图模型上的IsClient UIHint属性:

[UIHint("MyTemplate")]
public bool? IsClient { get; set; }
Run Code Online (Sandbox Code Playgroud)

然后:

 @Html.EditorFor(x => x.IsClient)
Run Code Online (Sandbox Code Playgroud)

将自动选择自定义编辑器模板.


awr*_*ley 5

knockoutjs用户的附录:

@Darin Dimitrov的答案很棒,但是对于knockoutjs来说有点过于严格,其中复杂的视图可能导致viewModel没有完全映射到@Model参数.

所以我已经使用了对象additionalViewData参数.要从Custom EditorTemplate访问additionalViewData参数,请参阅以下SO问题:

从Custom EditorTemplate代码访问additionalViewData

Digression:additionalViewData参数令人困惑,因为它对默认编辑器没有任何作用.它只与自定义编辑器模板结合在一起.

无论如何,我对Darin代码的修改如下:

@if (ViewData.ModelMetadata.IsNullableValueType) 
{
    var x = ViewData["koObservablePrefix"];
    if ((x != "") && (x != null)) { x = x + "."; }
    @Html.DropDownList(
        "", 
        triStateValues, 
        new { 
            @class = "list-box tri-state", 
            data_bind="value: " + x + ViewData.TemplateInfo.GetFullHtmlFieldName("") // or you could also use ViewData.ModelMetadata.PropertyName if you want to get only the property name and not the entire navigation hierarchy name
        }
    )
} 
else 
{
    @Html.CheckBox("", value ?? false, new { @class = "check-box" })
}
Run Code Online (Sandbox Code Playgroud)

注意这些行:

var x = ViewData["koObservablePrefix"];
if ((x != "") && (x != null)) { x = x + "."; }
Run Code Online (Sandbox Code Playgroud)

koObservablePrefix就在那里,我可以在viewModel ko.observable中添加任意前缀.如果您愿意,可以做其他事情.

我使用变量x如下:

data_bind="value: " + x + ViewData.TemplateInfo.GetFullHtmlFieldName("")
Run Code Online (Sandbox Code Playgroud)

这样,如果我没有传入additionalViewData"koObservablePrefix",它仍然有效.

那么,现在我可以写:

@Html.EditorFor(model => model.IsClient, "koBoolEditorFor", new { koObservablePrefix = "Account" })
Run Code Online (Sandbox Code Playgroud)

将呈现为:

<select class="list-box tri-state" data-bind="value: Account.IsBank" id="IsBank" name="IsBank">
Run Code Online (Sandbox Code Playgroud)

请注意"value:Account.IsBank"数据绑定属性值.

例如,如果您的视图强类型模型属于Account类型,但在您的页面的accountViewModel中,您有一个更复杂的结构,因此您需要将您的observable打包在一个帐户对象中.例如:

function account(accountId, personId, accountName, isClient, isProvider, isBank) {

    this.AccountId   = ko.observable(accountId);
    this.PersonId    = ko.observable(personId);
    this.AccountName = ko.observable(accountName);
    this.IsClient    = ko.observable(isClient);
    this.IsProvider  = ko.observable(isProvider);
    this.IsBank      = ko.observable(isBank);
}

function accountViewModel() { 

    var self = this;

    this.selectedCostCentre = ko.observable('');            
    this.Account = new account(@Model.AccountId, @Model.PersonId, '@Model.AccountName', '@Model.IsClient','@Model.IsProvider', '@Model.IsBank');
              // etc. etc
}
Run Code Online (Sandbox Code Playgroud)

如果你没有这种结构,那么代码将获取结构.这只是一个定制你的viewModel js到这个,嗯,灵活的约定的问题.

希望这不是太混乱......