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

时间:2011-09-04 22:07:34

标签: 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" } })

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

但是,找不到任何合理的文档,其他任何重载都不适合我想要的东西。

任何建议。

2 个答案:

答案 0 :(得分:9)

Brad Wilson blogged关于ASP.NET MVC 2中的显示和编辑器模板。因此,您可以修改布尔值的默认模板并添加所需的属性(~/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" })
}

最后:

@Html.EditorFor(model => model.IsClient, "MyTemplate")

或使用UIHint属性装饰视图模型上的IsClient属性:

[UIHint("MyTemplate")]
public bool? IsClient { get; set; }

然后:

 @Html.EditorFor(x => x.IsClient)

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

答案 1 :(得分:5)

knockoutjs用户的附录:

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

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

Access additionalViewData from Custom EditorTemplate code

<强>题外话: 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" })
}

注意以下几行:

var x = ViewData["koObservablePrefix"];
if ((x != "") && (x != null)) { x = x + "."; }

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

我使用变量x如下:

data_bind="value: " + x + ViewData.TemplateInfo.GetFullHtmlFieldName("")

这样,如果我没有传入additionalViewData“koObservablePrefix”,它仍然有效。

所以,现在我可以写:

@Html.EditorFor(model => model.IsClient, "koBoolEditorFor", new { koObservablePrefix = "Account" })

将呈现为:

<select class="list-box tri-state" data-bind="value: Account.IsBank" id="IsBank" name="IsBank">

请注意“值:Account.IsBank”数据绑定属性值。

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

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
}

如果你没有这种结构,那么代码将获取结构。这只是将viewModel js定制为这个,非常灵活的约定。

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