6

My view uses ModelX to render my HTML form and my controller action takes ModelY as input when saving the form. It seems the typesafe textbox API assumes I am using the same model in both places.

Is it possible to use different models and beeing type safe without creating my own helpers ?

<% = Html.TextBoxFor(x => x.Text) %>

I would like something like this. Does it exist ?

<% = Html.TextBoxFor<InputModel,OutputModel>(input=>input.Text, output=>output.SomeOtherText)

2 Answers 2

14

I have found a solution which involves creating a new html helper. The OP is correct in saying that it is sometimes inappropriate to use the same class as a parameter for the action method as well as the viewpage. Sometimes we want to pass MORE information to the ViewPage than what the user gives back to us in the form post.

The solution I came up with is to use a HtmlHelper extension method, which I called HtmlHelperFor(T obj) which I use as below:

<% var productForm = Html.HtmlHelperFor(Model.Product); %>

and then I use it as below:

<%= productForm.TextBoxFor(x => x.Name) %>

The extension method is below:

public static HtmlHelper<T> HtmlHelperFor<T>(this HtmlHelper html, T model)
{
    var newViewData = new ViewDataDictionary(html.ViewDataContainer.ViewData) { Model = model };
    ViewContext newViewContext = new ViewContext(html.ViewContext.Controller.ControllerContext, html.ViewContext.View, newViewData, html.ViewContext.TempData, html.ViewContext.Writer);
    var viewDataContainer = new ViewDataContainer(newViewContext.ViewData);
    return new HtmlHelper<T>(newViewContext, viewDataContainer, html.RouteCollection);
}

The ViewDataContainer is an implementation of the IViewDataContainer interface found in Sysetm.Web.Mvc:

public class ViewDataContainer : System.Web.Mvc.IViewDataContainer
{
    public ViewDataContainer(System.Web.Mvc.ViewDataDictionary viewData)
    {
        ViewData = viewData;
    }

    public System.Web.Mvc.ViewDataDictionary ViewData { get; set; }
}

the above calls will allow you to have a Product object as part of the parameters of the method that accepts the POST, instead of a class which contains project which you would normally pass to your view.

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for showing your solution! I'm thinking of expanding on the idea by creating something like a ScopedMvcForm, and a corresponding helper BeginScopedForm. I would like to be able to write: using(var form = BeginScopedForm(Model.Product)) { form.TextBoxFor(...) }
I would upvote this a bunch of times if I could. Not only does it answer a direct question I had (how to do the same operation the question describes but for DisplayNameFor calls), but it also provides a good window on how a HtmlHelper gets created and how to create one given an existing view.
@TahirHassan how can we do same for ASP.NET Core ?
@tchelidze The same technique works, virtually unmodified, in ASP.NET Core, but only if you continue to use HTML-Helpers (i.e; @Html.IdFor()) - but if you use TagHelpers, e.g. <input type="text" asp-for="Model.Property.Here" >` then unfortunately the answer is "no" and it's dang annoying. The only alternative workaround I'm able to use is to move all <form> HTML into a Partial View and use my own special render-partial methods that replace/reset the model-naming context.
1

Is it possible to use different models and beeing type safe without creating my own helpers ?

Only by using inheritance. So you will have base model with all the properties.

Consider XForms.
But I don't really understand the purpose of InputModel and OutputModel in the sample.

4 Comments

How about some examples in XForms that applies to his case.
Can you give a real sample of what you want to do? I don't really understand what you are trying to achieve (Input and Output model?).
Dmitriy Nagirnyak: The idea is that you use one model to populate the view. You might not use the same model to save data from the view. They are to different concerns.
I often have different models for view and as input parameter for action. But I still don't need input model in the view. Your view should use view model and not input model. If they are similar than you can use inheritance, aggregation or just same model for view and input (in simple cases). Additionally what output do you expect from the sample you provided? Can you give a real sample?

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.