1

I have a view model that is used to display a form on one view, and then is also used to represent the POST data to an action. The action then displays another view model that contains much of the same data from the first view model. However, the first view model has several "display only" properties that are also required on the second view model (for display only on the second view also).

I am wondering what the best way to pass this "display only" data to the second view would be. Currently, the best solution I have come up with is to have a bunch of hidden form fields that contain the display only property values, and then the model gets auto-populated for the action that handles the form POST. However, using hidden form fields seems very "hackish", and there seems like there should be a better solution to passing this data to another view The action doesn't need the display only information, it is only accessing it to populate the properties of the second view model that is passed to the second view.

Let me just explain my question with code, as what I am after is probably better understood through code than words.

Models:

public class SearchFilters
{
    // ...
}

public class SearchResult
{
    public int Id { get; set; }
    public bool Selected { get; set; }
    public string SomeDisplayValue1 { get; set; }
    public string SomeDisplayValue2 { get; set; }
    // ...
}

public class ResultsViewModel
{
    public IList<SearchResult> Results { get; set; }
    // ...
}

public class DoSomethingWithSelectedResultsViewModel
{
    public IList<SearchResult> SelectedResults { get; set; }
    public string SomeOtherProperty { get; set; }
    // ...
}

Controller:

[HttpPost]
public ActionResult Results(SearchFilters filters)
{
    ResultsViewModel results = new ResultsViewModel();
    // ...
    return new View(results);
}

[HttpPost]
public ActionResult DoSomethingWithSelectedResults(ResultsViewModel model)
{
    // ...
    return View(new DoSomethingWithSelectedResultsViewModel
    {
        SelectedResults = model.Results.Where(r => r.Selected).ToList(),
        SomeOtherProperty = "...",
        // ...
    });
}

View: Results.cshtml

@model ResultsViewModel
@using (Html.BeginForm("DoSomethingWithSelectedResults", "Search"))
{
    <table>
    for (int i = 0; i < Model.Results.Count; i++)
    {
        <tr>
            <td>
                @Html.CheckBoxFor(m => Model.Results[i].Selected)

                @* I would like to eliminate these hidden inputs *@
                @Html.HiddenFor(m => Model.Results[i].Id)
                @Html.HiddenFor(m => Model.Results[i].SomeDisplayValue1)
                @Html.HiddenFor(m => Model.Results[i].SomeDisplayValue2)
            </td>
            <td>@Html.DisplayFor(m => Model.Results[i].SomeDisplayValue1)</td>
            <td>@Html.DisplayFor(m => Model.Results[i].SomeDisplayValue2)</td>
        <tr>
    }
    </table>
    <button type="submit">Do Something With Selected Results</button>
}
tereško
  • 58,060
  • 25
  • 98
  • 150
Tony
  • 602
  • 1
  • 7
  • 10

3 Answers3

2

As far as I know, one of the best way to pass data from View to another View through a Controller is to use ViewBag, ViewData or TempData. As an example, you can pass the data retrieved from View I as shown below:

TempData[DataToBePassed] = model.CustomData;

And then retrieve this data in View II similar to that:

@if(TempData[DataToBePassed] != null)
{
    var dataFromFirstView = TempData[DataToBePassed];
}

For more information take a look at When to use ViewBag, ViewData, or TempData in ASP.NET MVC 3 applications.

Murat Yıldız
  • 11,299
  • 6
  • 63
  • 63
0

You could put the model in the TempData property of the controller, that way it's automatically available in the next request.

More here

Community
  • 1
  • 1
  • This would work (or the session solution mentioned in another answer) but the instance stored in the temp data is a different instance than what is passed to the form submit action. So I would have to manually copy the property values over to the instance that MVC automatically created in the form submit action (ignoring properties that I actually care about from the form submission). This seems like it would be even worse than just having the hidden form fields. – Tony Mar 25 '16 at 20:01
  • I ended up using this method. I'm not thrilled with having to copy the values from the instance stored in temp data to the instance that is populated from the POST, but after considering security issues of having the hidden form fields, this seemed like the best way. Would be nice if there was a way you could have MVC update an existing instance when populating values from a POST. – Tony Mar 29 '16 at 17:49
0

Found what I was looking for, I just hadn't worked with MVC enough yet to know about it. The Controller.UpdateModel method does exactly what I was looking for.

Example (using the code from the question):

[HttpPost]
public ActionResult DoSomethingWithSelectedResults()
{
    // Load initial model data here, in this case I had simply cached the results in
    // temp data in the previous action as suggested by Emeka Awagu.
    ResultsViewModel model = (ResultsViewModel)TempData["results"];

    // Call UpdateModel and let it do it's magic.
    UpdateModel(model);

    // ...
    return View(new DoSomethingWithSelectedResultsViewModel
    {
        SelectedResults = model.Results.Where(r => r.Selected).ToList(),
        SomeOtherProperty = "...",
        // ...
    });
}

Using this method I was able to eliminate all the hidden form fields and did not have to write any custom copy logic, since UpdateModel deals with it automatically.

Note: I did have to implement some custom model binders to get things to work correctly with dictionaries and collections (see here, here, and here).

Community
  • 1
  • 1
Tony
  • 602
  • 1
  • 7
  • 10