MVC 3 + knockoutjs: adding a data binding attribute when using EditorFor for a boolean field

Using @Html.EditorFor(model =>model.IsClient) , where IsClient is a boolean, displays a drop-down list with Not Set, Yes and No as parameters.

All is well and good.

Now I want to use knockoutjs with the resulting list that I like, so how do I add a data binding attribute using @ Html.EditorFor, which I need to work with knockout to work with this dropdown menu?

I tried:

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

However, this uses the objectViewViewData parameter, and it does not display the data binding attribute. This is probably natural, since this parameter is probably not related to the HTML attributes for the displayed tag.

However, it cannot find reasonable documentation, and no other overloads seem likely candidates for what I want.

TIA any suggestions.

+6
source share
2 answers

Brad Wilson on the blog on display and editor templates in ASP.NET MVC 2. Thus, you can change the default template for booleans and add the attributes you need ( ~/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" }) } 

and finally:

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

or decorate the IsClient property in your model using the UIHint attribute:

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

and then:

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

will automatically select a custom editor template.

+9
source

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'); // etc. etc } 

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 ...

+5
source

Source: https://habr.com/ru/post/896604/


All Articles