ASP.Net MVC - Passing posted value to view - asp.net-mvc

I am creating a search page, the user types in the textbox and click 'Search', the controller takes the FormCollection in its Action method.
I do the search and return the results to the view however I want to return what the user searched for to the view. Is the TempData the best place for this or is this a risk?

I'd say that your model for the results view should contain both the results and the search criteria.
Examples:
public class ResultsViewModel
{
public SearchModel SearchCriteria { get; set; }
...
}
public class SearchModel
{
public string Category { get; set; }
...
}
then, just populate the SearchCriteria in your results view model and you can retrieve it from there.
This assumes that your results view is strongly typed to ResultsViewModel.

TempData is primarily used when the result of an action is a redirect to another action, and you need to keep some state around.
All you need to do is add another entry to the ViewData dictionary with "what the user searched for". Something roughly like this:
public ActionResult Search(FormCollection form)
{
// search algorithm
ViewData["keywords"] = form["keywords"];
return View();
}

The TempData is if you need that item the next time the user requests something else. Using the ViewData is what you're looking for. Also remember, the value he/she searched for is still available in the View as Request[searchBoxName].

Create a strongly typed view using a view model that will pass all the information you want on to the view and encode all user imputed information.
public class ResultsSetViewModel
{
public string Query { get; set; }
public IList<Result> Results { get; set; }
}
Encode the user imputed data.
<h3>Search Results For: <%=Html.Encode(Model.Query) %></h3>

Using both the suggestions above will work, however it is better practice to add the data you need into the model than passing it via the ViewData.
If you pass it in the model then you get the advantages of it being strongly typed and remove the need to add logic to cast the ViewData entry into your view.

Related

ASP.NET MVC ViewModel with IEnumerable Property

What I am Attempting
I am trying to bind my ViewModel to my View which contains a Customer Name and a list of Contacts to reach that customer. Once the view is populated I also want the user to have the ability to add, delete, and edit contacts in the View.
What I've got so far
Currently I am able to bind to the ViewModel using the EditorTemplate and edit the data with no problems. The problem comes in when I want to add or remove rows in the list, if a user deletes row 5 from the middle of the ViewModel bound Editable List only rows 1 - 4 will end up being posted to the server.
The Code
Here is what my ViewModel looks like:
public class CustomerViewModel{
[Required]
public int CustomerUniqueID { get; set; }
[Required]
public string Name { get; set; }
public IEnumerable<CustomerContacts> Contacts { get; set; }
public CustomerViewModel() { }
public CustomerViewModel(Customer customer)
{
ICustomerContactRepository contacts = new LockShop.Domain.Concrete.EFCustomerContactRepository();
this.Contacts = contacts.FindContacts(customer.UniqueID);
this.Name = customer.Name;
}
}
I am passing my ViewModel to my view like this:
public ActionResult Edit(){
Customer customer = repository.Customers.FirstOrDefault(c => c.UniqueID == 23128);
return View(new CustomerViewModel(customer));
}
When it is time to render the list, I simply use the EditorFor helper:
#Html.EditorFor(model => model.Contacts)
What I love about this approach is that when I click the save button all of my changes are automatically posted to the controller!
My Question
What are the best practices of how to add the functionality of adding and removing rows to a list that is generated by an IEnumerable from my ViewModel?
What I Have Tried
I've read Steven Sanderson's blog post which talks about editing a variable length list in ASP.NET MVC. His post seems to only cover binding to a model and doesn't cover the possibility of binding to a list in a ViewModel. When I implemented his solution the post data from the Contacts list that got sent back to the controller was null.
Please feel free to ask any questions for clarification and thanks for your time.

Proper way to re-set 'read only' data in the ViewModel?

I'm trying to figure out a 'best practice' here and hope I'm not too far off. Here's the scenario:
Let's say we have two Actions. One for sending out a Razor View that contains a form. On this form, there is a <select> that is loaded with values from our ViewModel (let's say it's an expiration date field or something). There is also fields for the user to fill in, such as Name.
The other Action takes in the POST of the view, with the model as the parameter:
[HttpPost]
[ActionName("MyForm")]
public virtual ActionResult MyForm(MyViewModel model) {
// logic here
}
Now, if there's some sort of issue with the model (let's say, we couldn't charge their credit card). We could do ModelState.AddModelError and then pass back the model:
return View(model);
However, is this a best practice? What about properties that are in our ViewModel, but we didn't "return back" from the page, such as our expiration date field values. These will now be NULL and we'll have an exception when loading the view.
Would best practice be to always "recreate" the view model before sending it back (somehow taking in the values they typed in, overlaying them onto the default viewmodel?) or should we have some method that always sets those "readonly" fields, and gets called in both Actions?
Hopefully this question makes sense. I don't know what to call the properties that are just "read only" on the view, and not passed back as part of the FORM POST.
Thanks.
You call read-only data, some call it system data, some buddy data (using enricher classes and Structuremap), often it is referred to as hydrating.
I usually approach it similar to this example ("a method which always sets those fields") or I'll create an action filter (OnActionExecuted) which injects buddy data depending on the type of the view model.
For example:
public class ContactFormData
{
// data which gets posted back
public string Name {get; set;}
public string CountryCode {get; set;}
// buddy data
public SelectList Countries {get; set;}
}
[HttpGet]
[ActionName("ContactForm")]
public virtual ActionResult ContactForm() {
var m = new ContactFormData();
return ShowContactForm(m);
}
[HttpPost]
[ActionName("ContactForm")]
public virtual ActionResult ContactForm(ContactFormData formdata) {
if (ModelState.IsValid)
{
// logic & redirect
return redirect;
}
return ShowContactForm(formdata);
}
private ActionResult ShowContactForm(ContactFormData formdata)
{
formData.Countries = GetCountriesSelectListSomewhere();
return View(m);
}

MVC 3 Model Binding without Key

I have a question about ASP.NET MVC3 model binding. If I have a class I'm trying to use as a model, but I don't want the key put on the page, the model doesn't bind on a POST. Here is an example:
//Data Model
public class MyModel
{
[Key]
public string MyKey {get;set;} //Perhaps this is an ssn that I don't want on the form.
public string MyValueToGet {get;set;} //This is the value I want the user to enter.
}
//Conroller code.
public ViewResult Index()
{
MyModel model = new MyModel{ MyKey = "SecretInfo", MyValueToGet = "" };
return View(new model);
}
public ActionResult Edit(MyModel model)
{
repository.SaveChanges(model)
}
//View code.
#using(Html.BeginForm("Edit", "Home", FormMethod.Post))
{
Enter a value: #Html.EditorFor(m => m.MyValueToGet)
<input type="submit" value="Salve" />
}
So my problem is that model is null when the Edit method is called upon form submission. I can fix this by putting MyKey somewhere on the page (perhaps as a hidden field), but that is unacceptable if it is some sort of sensitive data. Is there a way to solve this problem? I am new to MVC, so I appreciate any help.
Create another unique but otherwise meaningless identifier like an (auto increment int) and use that to bind.
in other words modify your model to something like:
public class MyModel
{
[Key]
public int ID {get; set;}
public string MyKey {get;set;} //Now this can be sensitive, it doesn't matter because you no longer rely on it.
public string MyValueToGet {get;set;} //This is the value I want the user to enter.
}
EDIT
I believe your best choice would be to change the MyModel object, as it's design is flawed. The primary key in the majority of cases (and I think this is one of them) should be a simple auto incrementing integer, meaningless apart from it's role as the table's key.
While Luke's suggestion to use Session is a viable option and a solution that would work, I would personally do something similar to what I'll explain here, as it would seem to me to be more of the 'mvc way' of doing things.
Data model:
Either change your current model to something like what I suggest above, or, if that is not feasible for whatever reason (breaking dependancies or FK relationships), create a new table that can be used as a join, or proxy, if you will:
public class Proxy
{
public int ProxyId {get;set;}
public MyModel MyModel {get; set;}
}
Obviously, you'd have to do some work to populate this table, but you would then be able to use it to fetch records from MyModel without accessing the MyKey property directly.
It's not considered good practice to use your data models directly in your views, so you want to create a view model as well
public class MyModelViewModel
{
public int ModelId {get; set;}
public string ModelValueToGet {get; set;}
}
Notice we don't even need the key containing sensitive data in the view model.
Then type your view to the viewModel, not the data model, and include a hidden field for the ModelId
#using(Html.BeginForm("Edit", "Home", FormMethod.Post))
{
Enter a value: #Html.EditorFor(m => m.ModelValueToGet)
#Html.HiddenFor(m => m.ModelId)
<input type="submit" value="Save" />
}
Now in your controller you have your get method
public ViewResult Index()
{
//fetch the users record from the database
//if you're using the Proxy table, you'll want to write a LINQ query here
//instantiate a viewModel and populate it's properties using the fetched record
//remember, the viewModel.ModelId should be set to MyModel.ID or Proxy.ProxyId
//render the view
}
And the post method
public ViewResult Edit (MyModelViewModel viewModel)
{
//fetch the users record from the database using viewModel.ModelId
//If you're using the proxy table, you'll need to use that LINQ query again here
//update the record you fetched with the new data the user just entered
//you have complete control here of what gets updated and what stays the same
//pass the updated record to the repository to save the changes.
//redirect the user to be on their merry way
}
I think that's about as well as I can lay it out. Hope it makes sense.
An alternative is to encrypt the id before sending it to the client. Check this post for more information on how to accomplish this.
Asp MVC 3: Modifiy Values Sent to View

MVC Partial Model Updates

I often find myself in the situation where I only want to present and edit some fields from my model. Let's say I have a model that represts an address, perhaps I just want the form to update the city and post code fields (bad example, but hopefully it explains the scenario).
I know of two methods:
1) Persist the unwanted fields in hidden input elements on the form, or...
2) Create a dedicated view model that just defines the fields I need.
I favour option #2, but I don't have a nice clean way of merging the data from the view model back into the 'real' model within the controller action. At the moment, I follow this approach...
1) Store the record I'd in a hidden field on the view model
2) When the page posts back, the controller retrieves the original record and I manually assign each field from the view model to the real model
3) Save the real model back to the data store.
This works, but it is quite a lot of work and very easy to miss an assignment/reassignment and I was wondering if anyone knew of another approach?
Use the System.ComponentModel.DataAnnotations.MetadataType.
Something like:
public class BaseClassOfProperties
{
public string Name { get; set; }
}
public interface INameViewableProperties
{
[Display(name = "Your Name")]
string Name { get; set; }
}
public interface INameHiddenProperties
{
//[scaffoldColumn(false)] this completely hides the fields
[UIHint("Hidden")] // i think...
string Name { get; set; }
}
[MetadataType(typeof(INameViewableProperties)]
public class NameViewAbleProperties : BaseClassOfProperties
{
}
[MetadataType(typeof(INameHiddenProperties)]
public class NameHiddenProperties : BaseClassOfProperties
{
}

Storing state in asp.net MVC

I'm building an asp.net MVC 2 app.
I have a list view which lists items based on a parameter. In the database I have a parent and child table, so my list view lists all the child records for where the parent's id matches the value specified in the parameter.
This is my controller and model:
public ActionResult List(int ParentID)
{
return View(new Models.ChildListModel(ParentID));
}
public class ChildListModel
{
public int ParentID {get;set;}
public ManagementUserListModel(int iParentID)
{
this.ParentID = iParentID;
this.Children = DataAccessLayer.ListChildrenForParent(iParentID);
}
public List<Child> Children {get;set;}
}
I also have a details and create action for that controller. The details and create view have a "back to list" action, which I want to go back to the list view, and maintain the original ParentID. So far I've been doing this by creating a hidden field called ParentID in the list, edit, create and details views, so that the model's ParentID property will get populated correctly:
<%= Html.HiddenFor(model => model.ParentID) %>
Then in the "Back to List" action in each view I pass the ParentID:
<%=Html.ActionLink("Back to List", "List", new {ParentID = Model.ParentID}) %>
This all works, but I'm not a big fan of storing raw IDs in the html. Are there any better ways to do this? Is there some built in way to encrypt the data (kind of like the standard asp.net viewstate did?) I'm just trying to achieve some sort of tamper resistance, and trying to avoid using session state (TempData, etc) because I don't want to have to handle session timeouts.
You may take a look at this article. You could use the new Html.Serialize extension method in your view which allows you to serialize entire objects and encrypt it:
<%= Html.Serialize("person", Model, SerializationMode.Encrypted) %>
Which serializes the Model into a hidden field and encrypts the value. To get the model back you use the DeserializeAttribute in the controller action to which the form is submitted:
public ActionResult Edit([Deserialize]Person person) { }
Another way to do this that doesn't expose the id and also doesn't add to the weight of the subsequent form post (like Webform's ViewState does and Html.Serialize would) is to use the session as the backing store inside your Model class.
public class ChildListModel
{
public int ParentID {
get
{
return (int)HttpContext.Current.Session["ChildListModel.ParentID"];
}
set
{
HttpContext.Current.Session["ChildListModel.ParentID"] = value;
}
}
public ManagementUserListModel(int iParentID)
{
this.ParentID = iParentID;
this.Children = DataAccessLayer.ListChildrenForParent(iParentID);
}
public List<Child> Children {get;set;}
}
If you like, you could even store the entire Parent object in your model that way instead of just it's ID - that would add to the session size on the server which may or may not be desirable (depending on how long your session lasts and whether it is set to be stored in memory or in sql server, etc.)
The easiest way would be to keep the parentid in the URL. It will look a bit strange for the Create Action but I still think that this the less troublesome way.
If you keep state, you will alwas have the problem that you can not hit F5 and you can not bookmark the page.
The backlink is a simple ActionLink in this case.
Urls would be:
/YourController/List/YourParentParameterValue
/YourController/Detail/YourParentParameterValue/YourDetailParameterValue
/YourController/Create/YourParentParameterValue

Resources