MVC Pass display data between views

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

Answers


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. Hope this helps...


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

More here


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


Need Your Help

getting original file name of uploaded file grails

grails file-upload

I am getting the uploaded file using request.getFile("file") where "file" is the name of file input element in my gsp file. I am able to parse this file fine but when I try to get the file's original

Can't get ng-cloak to work

angularjs ngcloak

I have a custom directive where I present the values in it using {{}} but the problem is when the page is reloading I get to see {{}} there before setting the values. I tried using ng-cloak but too...