Addition for knockoutjs users:
@Darin Dimitrov's answer is great, but too tough to use with knockoutjs, where complex views can lead to viewModels that are not fully mapped to the @Model parameter.
So, I used the objectViewView parameter. To access the optional ViewData parameter from a custom EditorTemplate, see the following SO question:
Access additional data types from custom EditorTemplate code
Digression: The extraViewData parameter is confused in that it does nothing with the default editor. It is included only in its own template template.
In any case, my corrections to Darin's code are as follows:
@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" }) }
Pay attention to the lines:
var x = ViewData["koObservablePrefix"]; if ((x != "") && (x != null)) { x = x + "."; }
koObservablePrefix is, so I can add an arbitrary prefix to my viewModel ko.observable. You can do other things if you want.
I use the variable x as follows:
data_bind="value: " + x + ViewData.TemplateInfo.GetFullHtmlFieldName("")
That way, if I don't go into the extra view of "koObservablePrefix", everything will work.
So now I can write:
@Html.EditorFor(model => model.IsClient, "koBoolEditorFor", new { koObservablePrefix = "Account" })
which will be displayed as:
<select class="list-box tri-state" data-bind="value: Account.IsBank" id="IsBank" name="IsBank">
Note the value of the attribute : Account.IsBank .
This is useful if, for example, your models with a strongly typed model are of type Account, but your ViewModel has a more complex structure for your page, so you need to pack your observable objects into an account object. 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');
If you do not have such a structure, then the code will get a structure. This is just a question of adapting your viewModel js to this, uhmmm, flexible convention.
Hope this is not too confusing ...