Different viewmodel in partial view and rest of page? - c#

I'm trying to figure out how i can use fully different data on my page compared to what i use in a partial view.
I have a search bar at the top of my website, that top is a partial view that accepts a searchviewmodel and the bottom could be well, anything (search result, a blog, completely unrelated stuff).
Obviously i don't want to "mix" viewmodels, each of them should be unaware of the other (i'm not going to create a class to host both the partial & main viewmodel for each different page).
What is an appropriate way to handle this scenario?
Scenario is
User fills search on the top
=> if model is invalid, redisplay current page with errors (current page could for
example be today's wether, requiring WeatherViewModel for main content and
SearchViewModel for partial view)
=> else display result page (still need SearchViewModel for top banner
and then SearchResultsViewModel for the page)
I'm not finding any help online so far from googling (all i found was scenarios where it made sense to create a class to host both sub classes as they were functionally related, which isn't the case here).
My SearchViewModel looks like this:
public class SearchViewModel
{
[Required]
public string StartCity { get; set; }
[Required]
public string EndCity { get; set; }
[Required]
public DateTime Date { get; set; }
[Required]
public SearchType Type { get; set; }
[Required]
public SearchTimeType TimeType { get; set; }
}
It corresponds only to the top search and can be present on ANY page regardless of what viewmodel the REST OF THE PAGE uses.

You can insert the Search Partial View into the Template outside the #RenderBody().
Your partial View might have a a form to an action that returns the ShowResults View.

Related

ASP.net MVC Is it possible to modify a class object in a view?

I am a newbie and creating a website where you can create your own custom quizes. Ive made a database that stores a class object mytests that consists of a name, and a list of questions parameter.
public class MyTests
{
public int ID { get; set; }
public string name { get; set; }
public string description { get; set; }
public List<MyQuestions> AllTestQuestions;
}
//using this object for questions
public class MyQuestions
{
public string QuestionDescription { get; set; }
public string MultipleChoiceCorrect { get; set; }
public string MultipleChoiceB { get; set; }
public string MultipleChoiceC { get; set; }
public string MultipleChoiceD { get; set; }
public string Answerexplanation { get; set; }
}
I'm using the default database code generated by visual studio. I have no problem adding this test object(mytest) to the database, but what I want to do is that on the edit.cshtml view I want to be able to add elements to the question list before returning the object to the database saved.
The problem is I don't know how to edit the model object from the view, or if this is even possible. I could maybe get it to work through a redirect? but I thought that adding the elements directly from the view would be easier. Is it possible to modify the model.object inside a view from the view (putting security concerns aside)?
For example model.title = something;
or
model.list.add()
Is anything like this possible?
If this question is not clear please let me know and I will try to clarify in the comments.
Yes, it is possible to edit the model from within the view.
From within your .cshtml file specify the view model using the #model declaration, then edit the model like so:
#model Namespace.For.MyTests
#Model.name = "Hello World";
<p>#Model.name</p>
Whilst this would work, it's not really what the view is for so I wouldn't recommend it.
The view is about presenting your data, not mutating it - that should be done in the controller, or domain layer. As soon as the user leaves the page then your changes will be lost due to the stateless nature of the web (.NET MVC passes data to the view from the controller, then ends the request).
This should be done at the controller level. You could do it on a view but it's not what the view is for.
Your issue is that if the page is refreshed you will lose you content, so if you do anticipate on the page refreshing you will need a way in which to temporarily hold the information before it being saved.
On a side note, I'd also consider renaming your classes "MyTests" to "MyTest" (singular) and "MyQuestions" to "MyQuestion"... it's just good practice because then you'd have a List of singleton "MyQuestion" in a "MyTest". EntityFramework Codefirst will pluralise the names when the database is created/update.

What's the syntax for Html.Display()?

I've got a model like this:
public class HomeViewModel
{
public IEnumerable<NewsItem> NewsItems { get; set; }
public decimal TotalSales { get; set; }
public IEnumerable<SalesOrderHeader> SalesInformation { get; set; }
}
And in the View I want to do this:
<p>#Html.Display(Model.TotalSales)</p>
But I'm running into this error:
HomeViewModel does not contain a definition for Display"
Which is odd because it should be the Html type that has Display.
What is the correct syntax for what I'm trying to achieve?
It's better to use DisplayFor (Display) fo doing this. If you need to add some display attributes (like only last 2 digits to be shown) you will do this only one time in the ViewModel.
<p>#Html.DisplayFor(model => model.TotalSales)</p>
If you really want to use Display, the MVC is smart enough to render this with your data from Model.
<p>#Html.Display("TotalSales")</p>
Okay it's actually:
<p>#Model.TotalSales</p>
Solved!

How do I prevent hidden fields from interfering with server side validation in MVC?

I have a partial view that displays a number of inputs based on a view model. In some situations, some of those inputs are not rendered by the partial view, but are still decorated with [Required] attributes in the view model. As a result, when the form is posted back to my controller, ModelState.IsValid returns false. Is there a way to bypass this?
You can use Foolproof to validate your fields conditionally. This way, they'll be required only when they need to, as you can see in the example of the link.
private class Person
{
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public bool Married { get; set; }
[RequiredIfTrue("Married")]
public string MaidenName { get; set; }
}
In this example, MaidenName will only change your ModelState.IsValid to false if Married == true
I'd recommend separating your validation from your base model.
public class MyModel
{
public string MyString { get; set; }
public string MyHiddenField { get; set; }
}
public interface IMyModel_ValidateMystringOnly
{
[Required]
string MyString { get; set; }
}
[MetadataType(TypeOf(IMyModel_ValidateMystringOnly))]
public class MyModel_ValidateMystringOnly : MyModel
This allows you to create any number of validation types, and only validate what you want when you want.
public ActionResult ShowMyModel()
{
var model = new MyModel(); // or Respository.GetMyModel() whatever..
View(model);
}
public ActionResult ValidateModel(MyModel_ValidateMystringOnly model)
{
if (ModelState.IsValid)
{
// Hey Validation!
}
// MyModel_ValidateMyStringOnly is a MyModel
// so it can be passed to the same view!
return View("ShowMyModel", model);
}
This is just an example, but should be clear on how-to reuse the same model with or without validation.
I have used method at times where the form changes slightly based on specific DropDown or Radio Button selections.
Inside your Action method before you check ModelState.IsValid you can do something like ModelState.Remove("Object.PropertyName")
Note: The property name should be the same as the ID rendered to the client. Use a "." for any underscores.
If isSomeCondition Then
ModelState.Remove("Property1")
ModelState.Remove("Property2")
End If
If ModelState.IsValid() Then
...
End If
You should always separate your VIEW model from your DOMAIN model. There is a very good reason for this and it has to do with security. When you use your domain models as your view models you are vulnerable to an overposting and/or underposting attacks. You can read more about it on these pages:
http://odetocode.com/blogs/scott/archive/2012/03/12/complete-guide-to-mass-assignment-in-asp-net-mvc.aspx
http://blogs.msdn.com/b/rickandy/archive/2012/03/23/securing-your-asp-net-mvc-4-app-and-the-new-allowanonymous-attribute.aspx
https://hendryluk.wordpress.com/tag/asp-net-mvc/
In short if you don't need a field then it should not be in your view model. You should convert - map your view models to domain models. Although it can be tedious it makes your application much more secure. There are libraries you can use to help you with mapping such as Automapper.
EDIT: Since my original answer, I have come to a conclusion that the easiest way to deal with this type of scenario is to have your view model implement IValidatableObject interface and then write your validation logic inside the Validate method. It does not give you client side validation but it is the most effective and clean way to accomplish custom/scenario based validation without writing your own custom filters.
You can read more about it here: http://weblogs.asp.net/scottgu/class-level-model-validation-with-ef-code-first-and-asp-net-mvc-3

A viewmodel has a behavior/methods compared to a DTO but

People on SO often say:"A ViewModel holds methods that can be executed by the view, properties to indicate how toggle view elements, etc. ..."
When my ViewModel is sent as a WebApi response to the client serialized to JSON, how can this ViewModel execute a method on the client?
This is not clear at all to me.
You can understand viewmodel in at least two ways
instead of passing your business objects to the view (for example MVC Razor view) you pass stripped down objects that contain properties that are needed for this view and nothing else. View creation is easier and you avoid problems when view designer uses fields that are lazy loaded from database (avoid Select N+1 problem and others)
you can create viewmodel that will be used client side (in Javascript). You create it in Javascript as object and thus it can contain methods that view can call. What you are describing (sending JSON objects using WebAPI) is just data that will feed that viewmodel.
For example of this you can look on main page here knockoutjs. You can see TicketsViewModel that contains tickets array. In this example you can see three kinds of tickets hardcoded in viewmodel. But you could get them as JSON from WebAPI like you described. After downloading them just put them in this array.
A DTO (data transfer object) contains data in a consumable format. A ViewModel/ActionModel contains data formatted for the View to consume.
A DTO might look like:
public class OrderDTO
{
public decimal Price { get; set; }
public int Amount { get; set; }
}
While a ViewModel might look like:
public class OrderViewModel
{
public decimal Price { get; set; }
public int Amount { get; set; }
public string PriceBackgroundColor { get; set;}
public Uri ImageUri { get; set; }
}

ASP.net MVC - Binding a form value to a different name

I have a View that has the ability to both create and edit an item that I have separated into partial views:
MainEditView.cshtml
_CreateChildDialog.cshtml
_EditChildDialog.cshtml
I have separate ViewModels for both the Create and Child items:
public class CreateChildViewModel
{
public string ItemText { get; set; }
}
public class EditChildViewModel
{
public string ItemText { get; set; }
}
Since the partial views for the Edit and Create dialog boxes will both be rendered on the same page, I will have a conflict for form id's and names...since they are both called ItemText.
Is it possible to customize the binding of these elements without writing a custom model binder?
I would like to do something like:
public class EditChildViewModel
{
[BindFrom("EditItemText")]
public string ItemText { get; set; }
}
Or does it just make more sense to rename the ViewModel properties to:
public class EditChildViewModel
{
public string EditItemText { get; set; }
}
public class CreateChildViewModel
{
public string CreateItemText { get; set; }
}
EDIT
Based on converstation with Darin I want to make this a little more clear.
My Parent has an Edit action.
When you edit the Parent, you would never create a new child or edit a child when you are calling the ParentController.Edit action.
I have a separate controller for the Child object that has a Create and Edit method:
public class ChildController
{
public ActionResult Edit() {}
public ActionResult Create() {}
}
I am using jQuery calls to asynchronously post to this controller when you edit or create a child. Basically I use a jquery dialog to create/edit a child that will get saved immediately when I click Ok on the dialog. This would happen even before clicking save for the Edit action of the parent.
I would use editor templates. Normally you would pack those two view models into a main view model which will be used by the main view:
public class MyViewModel
{
public CreateChildViewModel Create { get; set; }
public EditChildViewModel Edit { get; set; }
}
and then:
#model MyViewModel
#Html.EditorFor(x => x.Create)
#Html.EditorFor(x => x.Edit)
and I would replace the two partials by their corresponding editor templates (~/Views/Shared/EditorTemplates/CreateChildViewModel.cshtml and ~/Views/Shared/EditorTemplates/EditChildViewModel.cshtml). The editor templates will take of generating proper names and ids of the corresponding input elements.
Personally I tend to prefer editor/display templates instead of partials as they handle better naming of input elements.

Categories