Retaining Form Values After Post (not part of model)

19,824

Solution 1

So here is how I typically solve this problem. My notes are purely my opinion (religous?) about naming classes in an MVC project to keep clear their purpose.

Couple of interfaces to keep it extensible:

// be specific about what type of results, both in the name of the 
// interface and the property needed, you don't want to have overlapping
// properies on your classes, I like suffixing interfaces that are specific
// to a View or Partial View with View
public interface IPersonSearchResultsView
{
   IEnumerable<EFPerson> PersonSearchResults { get; }
}

public interface IPersonSearchCriteriaView
{
  PersonSearchCriteriaModel PersonSearchModel { get; }
}

Couple of classes

// I like suffixing classes that I only use for MVC with Model
public PersonSearchCriteriaModel
{
  public string Name {get; set;}  
  public string Company {get; set;}
  public string DateStart {get; set;}
  public string DateEnd {get; set;}
}

// I like suffixing classes that I used passed to a View/Partial View
// with ViewModel    
public class PersonSearchViewModel : IPersonSearchResultsView, 
                                     IPersonSearchCriteriaView
{
  public IEnumerable<EFPerson> PersonSearchResults { get; set; }
  public PersonSearchCriteriaModel PersonSearchModel { get; set; }
}

Now for your controllers, I'll set them up in a way that would also allow you to do Ajax in the future.

public PersonController : Controller
{
  public ActionResult Search()
  {
    var model = new PersonSearchViewModel();
    // make sure we don't get a null reference exceptions
    model.PersonSearchModel = new PersonSearchCriteriaModel ();
    model.PersonSearchResults = new List<EFPerson>();
    return this.View(model);
  }

  [HttpPost]
  public ActionResult Search(PersonSearchViewModel model)
  {
    model.PersonSearchResults = this.GetPersonResults(model.PersonSearchModel);

    return this.View(model)
  }

  // You could use this for Ajax
  public ActionResult Results(PersonSearchViewModel model)
  {
    model.PersonSearchResults = this.GetPersonResults(model.PersonSearchModel);

    return this.Partial("Partial-SearchResults", model)
  }

  private GetPersonResults(PersonSearchCriteriaModel criteria)
  {
    return DbContext.GetPersonResults(criteria)
  }
}

Create a couple of partial-views your Views.

/Views/Person/Partial-SearchCriteria.cshtml

@model IPersonSearchCriteriaView

// the new part is for htmlAttributes, used by Ajax later
@using (Html.BeginForm(..., new { id="searchCriteria" }))
{
  // Here is were the magic is, if you use the @Html.*For(m=>)
  // Methods, they will create names that match the model
  // and you can back back to the same model on Get/Post

  <label>Name:</label>
  @Html.TextBoxFor(m => Model.PersonSearchModel.Name)

  // or let mvc create a working label automagically

  @Html.EditorFor(m => Model.PersonSearchModel.Name)

  // or let mvc create the entire form..

  @Html.EditorFor(m => Model.PersonSearchModel)
}

/Views/Person/Partial-SearchResults.cshtml

@model IPersonSearchResultsView

@foreach (var person in Model.PersonSearchResults )
{
  <tr>
    <td class="list-field">
      @Html.DisplayFor(modelItem => person.Name)
   </td>

   // etc
  </tr>
 }

And Finally the view:

/Views/Person/Search.cshtml

@model PersonSearchViewModel 

@Html.Partial("Partial-SearchCriteria", Model)

// easily change the order of these

<div id="searchResults">
@Html.Partial("Partial-SearchResults", Model);
</div>

Now enabling Ajax is pretty crazy easy (simplified and my not be exactly right):

$.Ajax({
  url: '/Person/Results',
  data: $('#searchCriteria').serialize(),
  success: function(jsonResult)
  {
    $('#searchResults').innerHtml(jsonResult);
  });

Solution 2

if you are using html in mvc then check solution 2 from here, value="@Request["txtNumber1"]" worked fine for me,

<input type="text" id="txtNumber1" name="txtNumber1" value="@Request["txtNumber1"]"/>

hope helps someone.

Solution 3

What I typically do is pass the posted Model back into the view. This way the values are not cleared out.

Your code would look something like this:

<div style="float:left;">

    <div style="float:left;">
    <label>Name:</label>
    @Html.TextBox("name", Model.Name)
    </div>

<div style="float:left; margin-left:15px">
<label>Company:</label>
@Html.TextBox("company", Model.Company)
</div>

<div style="float:left; margin-left:65px">
<label>Date Range:</label>
@Html.TextBox("dateStart", Model.DateStart, new { @class = "datefield", type = "date" })
&nbsp;to&nbsp;
@Html.TextBox("dateEnd", Model.DateEnd, new { @class = "datefield", type = "date" })
</div>

When initially getting the form, you'll have to create a new Model, otherwise the Model will be null and throw an exception when properties are called on it.

Sample Model

public class SearchModel
{

    public SearchModel()
    {
        Results = new List<Result>();
    }

    public string Name {get; set;}  

    public string Company {get; set;}

    public string DateStart {get; set;}

    public string DateEnd {get; set;}

    public List<Result> Results {get; set;}
}


@foreach (var item in Model.Results)
{
<tr>
    <td class="list-field">
        @Html.DisplayFor(modelItem => item.Name)
    </td>
    <td class="list-field">
        @Html.DisplayFor(modelItem => item.CompanyName)
    </td>
    <td class="list-field">
        @Html.DisplayFor(modelItem => item.City)
    </td>
    <td>
        @Html.DisplayFor(modelItem => item.State)
    </td>
    <td class="list-field">
        @Html.DisplayFor(modelItem => item.DateCreated)
    </td>
    <td class="list-field">
        @Html.ActionLink("Edit", "Edit", new { id = item.ProfileID }) |
        @Html.ActionLink("View", "View", new { id = item.ProfileID }) |
        @Html.ActionLink("Delete", "Delete", new { id = item.ProfileID }, new { onclick = " return DeleteConfirm()" })
    </td>
</tr>

}

Here is a link on creating models for a view in MVC.

Share:
19,824
SQLGrinder
Author by

SQLGrinder

Updated on June 26, 2022

Comments

  • SQLGrinder
    SQLGrinder almost 2 years

    I have a MVC4 page that has a form with a collection of checkboxes, radio buttons and textboxes used as the search fields. Upon post the selections are parsed and the lower results grid is updated with new results. Right now all the form values are wiped out upon return and the new results are displayed in the grid - only the grid is part of the model.

    I want all the form selections to retain their values after post so the user can see (and change) the selections for next post/search. The form is popuplated with viewbags.

    @using (Html.BeginForm("Index", "Home", FormMethod.Post, new { id = "searchform" }))
    {
        @Html.ValidationSummary("Please correct the following errors")
    
    <div style="float:left;">
    
        <div style="float:left;">
        <label>Name:</label>
        @Html.TextBox("name")
        </div>
    
        <div style="float:left; margin-left:15px">
        <label>Company:</label>
        @Html.TextBox("company")
        </div>
    
        <div style="float:left; margin-left:65px">
        <label>Date Range:</label>
        @Html.TextBox("dateStart", "", new { @class = "datefield", type = "date" })
        &nbsp;to&nbsp;
        @Html.TextBox("dateEnd", "", new { @class = "datefield", type = "date" })
        </div>
    
    </div>
    
    <div style="clear: both;">
        Match Any Categories? <input type="radio" name="categoryMatchAll" value="false" checked="checked" />&nbsp;&nbsp;&nbsp;
        Match All Categories?  <input type="radio" name="categoryMatchAll" value="true" /> 
    </div>
    
    <div style="float:left;">
    
        <div id="searchform-categories" style="float:left;">
            <div class="scroll_checkboxes">
                <label>Categories</label>
                <ul>
                @foreach (var x in ViewBag.Categories)
                {
                        <li>
                            <input type="checkbox" name="categories" value="@x.Id"/>
    
                            @x.Name
    
                        </li>
                }
                </ul>
            </div>
        </div>
    
        <div id="searchform-diversity" style="float:left; margin-left:30px">    
            <div class="search-selection" style="float:left;">
                <label>Minority Owned</label>
                <ul>
                    @foreach (var x in ViewBag.Minorities)
                    {
                        <li>
                            @Html.RadioButton("minorities", (String)x.Id.ToString())
                            @x.Name
                        </li>
                    }
                </ul>
            </div>
            <div class="search-selection" style="float:left;">
                <label>Diversity Class</label>
                <ul>
                @foreach (var x in ViewBag.Classifications)
                {
                    <li>
                        @Html.RadioButton("classifications", (String)x.Id.ToString())
                        @x.Name
                    </li>
                }
            </ul>
            </div>    
        </div>  
    
    </div>    
    
    <div style="clear: both;">
        <input type="submit" value="Search Profiles" />
        <input type="submit" value="Reset" />
        </div>       
    }
    

    the data grid is bound to the model as

    @model IEnumerable<VendorProfileIntranet.Models.VendorProfile>
    
    <table id="VendorTable" width="100%" class="gradeA">
    <thead>
        <tr>
        <th>
            @Html.DisplayNameFor(model => model.Name)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.CompanyName)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.City)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.State)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.DateCreated)
        </th>
        <th>Actions</th>
        </tr>
    </thead>
    <tbody>
    
    @foreach (var item in Model)
    {
    <tr>
        <td class="list-field">
            @Html.DisplayFor(modelItem => item.Name)
        </td>
        <td class="list-field">
            @Html.DisplayFor(modelItem => item.CompanyName)
        </td>
        <td class="list-field">
            @Html.DisplayFor(modelItem => item.City)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.State)
        </td>
        <td class="list-field">
            @Html.DisplayFor(modelItem => item.DateCreated)
        </td>
        <td class="list-field">
            @Html.ActionLink("Edit", "Edit", new { id = item.ProfileID }) |
            @Html.ActionLink("View", "View", new { id = item.ProfileID }) |
            @Html.ActionLink("Delete", "Delete", new { id = item.ProfileID }, new { onclick = " return DeleteConfirm()" })
        </td>
    </tr>
    
    }
    
    </tbody>
    <tfoot>
        <tr>
            <td>&nbsp;</td>
        </tr>
    </tfoot>
    

  • SQLGrinder
    SQLGrinder over 11 years
    My understanding is that you can only have one model and that is already taken by the data grid, so I am not sure how to incorporate what you are suggesting. (see updated above)
  • Chuck Conway
    Chuck Conway over 11 years
    @Vic Yes that is true, but a Model is simply a class. You can have anything in the class.
  • Chuck Conway
    Chuck Conway over 11 years
    @Vic on the GET, the Results will be empty -- no rows are displayed. When the search is completed, the results and the search criteria are displayed.
  • SQLGrinder
    SQLGrinder over 11 years
    Thx Chuck. I am using EF5 so the data model class is auto generated from the db. I am not sure how to extend this to include the search fields in my form. Can you provide more detail of what you are suggesting? I have also seen some people suggest using TempData but still unsure how to implement this into the view as well.
  • Chuck Conway
    Chuck Conway over 11 years
    @Vic Create a new model specifically for the view. This is separate from the EF5 data model. Usually these Models are stored in the Models folder in the root of the MVC project.
  • Chuck Conway
    Chuck Conway over 11 years
    @Vic I've added a link to my answer. It explains how to create and add a Model to a MVC 4 project.
  • Erik Philips
    Erik Philips over 11 years
    I'd highly recommend getting use to creating a model independent of your business objects. I sometimes use my EF models (smaller projects) in my view models, but not AS my view models for this specific reason.
  • SQLGrinder
    SQLGrinder over 11 years
    @ErikPhilips, okay i think i get what you guys are trying to say, but hard to figure out how to wire everything together. All samples and videos I have seen focus heavily on the EF view model way. I want to learn to do things the more correct way, so if you can point me to some full code examples that would greatly help. Thx.
  • Shaiju T
    Shaiju T over 7 years
    Why down vote ? please explain to add better answer in future.