4

I'm using custom editor templates for most of my forms, but while attempting to use them to render a custom object picker I've found no easy way to pass information about the containing context to the editor template.

Specifically, my main form renders an edit for a domain object, and the editor template needs to render an AJAX picker which contains a list of objects which are dependent on the ID of the domain object. I'm passing the ID using the additionalViewData parameter currently, which I think is error prone and therefore quite ugly.

My form contains code similar to the following:

@Html.EditorFor(model => model.CategoryId, new { id = model.id })

The editor template contains code like the following:

@{
var domainObjectId = ViewData["id"] as int?;
}

I'm using a custom ModelMetadataProvider to select the object picker editor template, and hope to use a similar technique to pass information about the containing model to the editor template, but that doesn't seem be possible.

So, my questions are:

  1. Is there anyway to use a ModelMetadataProvider to pass information about the containing model to the editor template?
  2. If not, is there any neater/easier way to achieve what I'm attempting, aside from passing every piece of additional information via the weakly typed additionalViewData parameter?

Thanks in advance!

2 Answers 2

5

You need to understand that EditorTemplates are designed to be type specific, not context specific. Some thought was given to this with the AdditionalViewData parameter, but that's the best you're going to get.

If you're concerned about type safety, use ViewBag instead, which is a type-safe dynamic wrapper around ViewData.

@{
    var domainObjectId = ViewBag.Id;
}
Sign up to request clarification or add additional context in comments.

8 Comments

Thanks for your answer. Unfortunately I think you're right in that it was a design choice, but they would simply be more useful if they were context aware. I'm accepting your answer, but I think I'm going to go with a regular partial instead of editor templates for these specific fields, rather than the ViewBag or similar.
@IanNewson - I don't understand your reluctance to use ViewBag, as that's really what it's designed for. I call this the "table layout mindset". People learn that using html tables for layout is bad, so they stop using tables for everything, even actual tabular data (you see tables made out of divs). Likewise, People learn that you should use ViewModels rather than ViewData/Bag and stay away even when it is used for what it's designed for.
@IanNewson - By the way, if you intend to use a partial, you will have to pay special attention to the form control naming to make sure nested objects are taken into account. EditorTemplates largely take care of this for you. I'm honestly not a big fan of using EditorTemplates for dropdowns anyways. I also think this would be better dealt with in an external javascript file and using a data attribute instead.
In general I'm not reluctant top use ViewBag, although for two reasons it's not the best choice in this situation; 1) I'm creating a reusable component several of which may reside on one page, and they may each be bound to different domain objects 2) I'm creating a framework for other developers, and I think the explicit strongly typed nature of a partial + extension method is desirable in this case.
Id uniqueness has already been taken care of. I'm confused why you say it would be better dealt with using an external JS file + dropdown, as I don't think I've mentioned any implementation details like that? My question was solely about passing the required information to the view which needs it, not what is done with that information in the view.
|
0

I wonder if maybe the controller that is creating this view model in the first place should be the thing that chooses the list of objects. So rather than having an integer property on your view model, you could have a sub property of another type of view model, ie:

public class OuterViewModel
{
    public CategoryViewModel Category { get; set; }
}

public class CategoryViewModel
{
    public int CategoryId { get; set; }

    public IEnumerable<SelectListItem> ListOfThings { get; set; }
}

Then your original view can just have:

@Html.EditorFor(model => model.Category)

With an EditorTemplate for CategoryViewModel that looks like:

@model CategoryViewModel
@Html.DropDownFor(m => m.CategoryId, Model.ListOfThings)

The only thing you have to remember this is if you do any server side business logic validation, add model errors and return to your view you will need to re populate your list of things in your controller post action.

2 Comments

Thanks for your answer. For a regular select or similar I would agree with you, but in this case the UI widget is a custom picker populated via an AJAX call, and includes search and paging. Therefore I need to pass all of information required to generate the list server side during the AJAX call. This includes the ID of the object, as well as a few other variables, and this is the information I need to pass to the template.
Well replace the IEnumerable with an int that is the id of the outer model and rather than rendering a dropdownlist, render what ever you want?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.