DisplayFor is not keeping data in ViewModel on PostBack - asp.net-mvc-3

This question has been asked before by other, but still don't get it.
I am using MVC3 Razor and I have screen for entering trouble tickets.
Once data (a textarea note) is entered, the controller redirects back to the same screen.
Most of the data is for display only. If I use DisplayFor or DisplayTextFor, the data
is not being posted back. I have used HiddenFor. That works. However, I keep hearing
from others that HiddenFor is not ideal. I don't want to editorfor because, I want to easily disable the field. (I follow working says HiddenFor is wrong, but won't say why. :< lol)
Razor
#Html.DisplayTextFor(m => m.Ticket.Name)
ViewModel
public class TicketDetailsViewModel
{
[DisplayName("Customer Name")]
public string Name { get; set; }
Control
[HttpPost]
public ActionResult Detail(TicketDetailsViewModel viewModel)
return RedirectToAction("Detail");

Only values that are part of an input or other form control will be posted back to the client. DisplayFor is just meant for the model to display its data, nothing else.
You have two options; use a hidden field so that the model is populated with the data each time, or fetch the value again. The former method would be fine if your data isn't sensitive because an end user can change the hidden value and repost your form. So for example a UnitPrice would NOT be something you'd want to ever get from the client as even in a hidden field a malicious user can modify the unit price. In that case you'd always want to reload the data yourself and repopulate the model.
There's nothing wrong with that either; if the data isn't editable and you need to display it, you need to get it every time. I'd personally err on the side of caution and refetch the read-only data each time on the server and not put the data into hidden fields.

Related

Django add fields to form dynamically based on number of values in model choice field

Objective:
From a Django Form, modelchoice field i am making the widget display multiple checkboxes. But for each checkbox I would like to display exactly one textbox and then submit. I need to know if the checkbox is not selected, it's id still and the possible textbox value. How do I acheive this, if it is Ajax. please elaborate. as I am fairly new to django and haven't worked much with ajax.
So you have to possible approaches here,
Simpler(but a very tardy approach):
Submit the form after user's checkbox input, process the input in views.py and accordingly serve the other part of the form on a different template. This will lead you to reloading the page and changing URL's for the same form. This approach is fine if you are doing it only to learn Django at first.
The better approach. You can use on page JavaScript/JQuery to determine whether the checkbox is ticked or no and the accordingly show the checkbox. You can do something like
if(document.getElementById('yourCheckBoxID').checked)
{
$("#FormFieldID").show();
}
else
{
$("#FormFieldID").hide();
}
If you are doing the latter, do remember to NOT set the input field as "required", as it may throw up errors when not showing the text field. Use some sort of JS form validation if you have to.
Hope this helps!

MVC 3 Razor-Partial Validation of Model

I am currently working on a project in MVC 3 where I am leveraging Entity Framework to persist one data model over two Views which each contain one HTML Form (similar to wizard-based design).
Yet after the user fills out the appropriate fields on the first View and submits the form, client-side validation for the entire model is triggered, and validation errors are shown for fields that will not even be available for input until the second View instantiates.
I have currently implemented a workaround where I simply turn off client-side validation for the first View entirely, but I am certainly not keen on the idea of populating my model with data that has not been validated at all. This is bad. M'kay.
Is there any way to partially validate the fields on the first View and not trigger valdiation for the whole data model?
That's where ViewModels comes. Instead of directly binding the domain model with the views, you should create view models over them and bind to the views.
If you are not required to put validation on the EF models directly then you can use the MetadataType to do partial validation as needed. Take a look at my long example here on stackoverflow.
Thanks for the input all. However, I was able to obtain a solution in a very simple way. By placing the following code in the HttpPost element of the first View...
if (ModelState.IsValidField("FirstField") && ModelState.IsValidField("SecondField"))
return RedirectToAction ("NameOfAction", model);
else
return View();
...I was able to achieve partial field validation. However, this field-specific approach will ONLY work provided the Submit button on the first View has class "cancel" and the additional validation errors that are generated (for the fields that are NOT present on the first View) are manually cleared before the above if statement. To do this, use:
ModelState["FieldName"].Errors.Clear();
No major change in architecure. No partial Views. No handing off unvalidated Data.
Works very well...
NOTE: If the second View loads with validation errors, use:
ModelState.Clear();
in the Action where the second View is initially called. This will make the second View load clean and error free, while still showing the validation errors later upon final form submission.

asp.net mvc - Data lost on post back

I have a form that only has a checkbox. If the checkbox is not checked then the page won't be valid and won't submit the data. On the view I have out putted some fields from the model object, just to let the user know what they are deleting. When I click the Submit button and the model state is not valid the page will show the validation error but the data from the model is no longer there!
Does anyone know how I can persist this data without making another call to the database to populate the model object?
Thanks
ASP.Net MVC will not magically persist model info.
You need to either include it in hidden fields in the form (which an attacker can modify), or re-fetch it from the database.
If you don't repopulate the Model from the DB, you have to postback all the data, using hidden fields for example.
Anyway, I don't understand how the model validation could fail, being just a checkbox :/.

How to pass rows added client-side in MVC3 webgrid back to controller?

I have a Model that contains some string fields and a List. In the view I use EditFor for the fields, and want to use WebGrid for the List.
Display works fine. But then I use jQuery client-side to let the user add rows to the table generated by the webgrid, which also show up fine on the UI.
However these rows don't appear to be tied back to my model's List upon a submit. In the controller I see that the posted model's string fields contain whatever was entered by the user. But the model's List is empty - it doesn't contain the original values nor the newly added values.
How can I cause the model posted back to the controller to be fully populated, including whatever the webgrid-generated table looks like now, just like fields are?
I see several other questions like this for webgrid on the forum, but none with an answer.
I suppose you are comming from WebForms world. Well this is different in MVC. Here you are back to plain old html and its input fields. No viewstate to hold your rows. Check this link: http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx.
Basically you need have consecutive index (or ID) for each row.

MVC3-ValidationFailure-RetainValues

I am using MVC3 in ASP.NET. In the Post Action,
I have an View, In which it should let me edit the values. Some fields are read-only and some fields are editable. If there is a validation failure then it should retain the values and display the same View, if there are no error then it should let me to submit the form.
Control1:
Name I have to display as label as It has to be read-only
#Html.DisplayFor(Model => Model.Absentee.Name)
Control2:
I have to display this one as textbox so that it can be edited. This is a required Field.
#Html.TextBoxFor(Model => Model.AbsStart)
#Html.ValidationMessageFor(Model => Model.AbsStart) (This Makes it as a required Field)
Control 3:
This is also a textbox but not required field.
#Html.DisplayFor(Model => Model.AbsEnteredAt)
When there are no validation errors, things works great, but when there is a validation error, then I will be in the same view(Which is what I want) but the value in First control is not retained.
So Basically, when the validation failure, the value is not retained in the DisplayFor Control
What do I do to sort this?
Thank you
As 99% of my answers in the asp.net-mvc tag this one is the same: USE VIEW MODELS. Don't have your controller actions take/pass domain models to/from views. Design view models for each view. View models are classes which are specifically tailored to meet the requirements of this given view. So let's suppose that you have two different views which allow you to edit/update the same domain model. Then you will design two different view models and each view model will contain only the properties that are used by the respective view along with their specific validation requirements.
As far as the DisplatFor helper is concerned, it generates a simple label. Labels are never sent to the server when you submit a form. So you can hope to populate/validate on your controller action only the properties that are contained on the form. For properties that should not be displayed you could use either hidden fields or a single hidden field containing only some unique identifier so that you can fetch from your datastore back the original values (as the user is not supposed to modify them anyways).
As far as the mapping between the view model that you should design and the original domain model is concerned, this mapping is usually performed inside the controller and to simplify it I would recommend you to use AutoMapper.
When there is an error, what is the viewModel you are returning? DisplayFor does not get sent back to the server ... add this under the displayfor:
#Html.HiddenFor(Model => Model.Absentee.Name)

Resources