How could I make my View Model - c#

I have a partial view that contains my form input(textboxes). I have 2 other partial views that use this same form. One for adding a product and one for editing a product.
This form uses a view model(Lets call it CoreViewModel). Now editing product has a couple more fields then adding a product.
I am wondering how can I add these extra fields without them showing up on an add product form?
I cannot add these extra fields to the edit product view they must be in the CoreViewModel otherwise I think styling it will be a nightmare.
I was thinking of having maybe a base class and then for the editing. I would send it a view model that inherits this base class.
Check in the view if the View Model is of this inherited class and not a base class and if it is not a base class render the code.
This way I am not sticking the edit specific code into my CoreViewModel that both the add view and the edit view have access.
I hope this sort of makes sense.
Thanks
Edit
Using Muhammad Adeel Zahid code as I base I think I got it to work
public class CreateViewModel
{
......
......
}
public class EditViewModel:CreateViewModel{
public string AdditionalProperty1{get;set;}
public string AdditionalProperty2{get;set;}
}
Controller
EditViewModel viewModel = new EditViewModel();
// add all properties need
// cast it to base
return PartialView("MyEditView", (CreateViewModel)viewModel);
View 1
#Model CreateViewModel
#using (Html.BeginForm())
{
#Html.Partial("Form", Model)
}
Form View
#Model CreateViewModel
// all properties from CreateView are in here
// try and do a safe case back to an EditViewModel
#{EditViewModel edit = Model as EditViewModel ;}
// if edit is null then must be using this form to create. If it is not null then it is an edit
#if (edit != null)
{ // pass in the view model and in this view all specific controls for the edit view will be generated. You will also have intellisense.
#Html.Partial("EditView",edit)
}
When you post it back to your Edit action result just take in the EditViewModel and cast it back to your base. Then you will have all the properties as it seems to work

I have often read people advising against such things. They often urge having viewmodel per view (even for edit and create view of same entity for that matter). Again, it all comes down to what you are doing. You may have different data annotations on edit and create view for different validation needs but if they are the same, we probably have a point to use same viewmodel for create and edit.
To solve your scenario I can't figure out couple of options. First, keep a boolean property in your view model telling you if it is and edit or create and conditionally render you properties on view
public class MyViewModel
{
public string P1{get;set;}
....
public boolean Editing{get;set;}
}
Set Editing property to false in Create ActionResult and to true in Edit ActionReult. This is the simplest method. The second one is little dirtier, but you will feel like using the technology. You can use dynamic behavior of c# 4.0. have your page inherit from dynamic in iherits directive of the page (I use aspx view engine). Then have a create ViewModel:
public class CreateViewModel
{
......
......
}
and one Edit ViewModel
public class EditViewModel:CreateViewModel{
public string AdditionalProperty1{get;set;}
public string AdditionalProperty2{get;set;}
}
and in your view you can do something like:
<%:if(Model.GetType().Name.ToString() == "EditViewModel"){%>
<%:Html.Textbox("AdditionalProperty1")%>
<%:Html.Textbox("AdditionalProperty1")%>
<%}>
There is a price to pay with dynamic. You lose intellisense and you can't use strongly typed helpers (at least in asp.net MVC 2).

Related

What's the best way to return List of data from controller [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 5 years ago.
Improve this question
I have a List<MyModel> that I am returning from controller to view
Shall I create a new class to hold this List and return object of the new class or I can return the List itself..
which one is a better programming practice ??
It depends on what your exact requirements are, consider the below two scenarios.
Using MVC webapi with any js framework like angular/jquery or returning partial view.
You just want to bind the list to a grid and that grid will be updated with new data from same controller action. In this scenario its better to return the list instead of view model, this will ensure that only required data is sent back to view.
``
[HttpPost]
public List<string> Index(string txtJsonIn)
{
return new List<string>();
}
Using MVC without any js/ajax calls
In this case the view will be loaded with values from ViewModel everytime, so if you just keep the List as your return value from action, it will be hard to add new property in future.
class MyViewModel
{
List<string> MyList { get; set; } //Your list that you need right now
string PropertyThatCanBeAddedInFuture { get; set; }
}
[HttpPost]
public MyViewModel Index(string txtJsonIn)
{
return new MyViewModel();
}
You can write the view as below
#foreach (string str in Model.MyList)
{
<tr>#str</tr>
}
Also using viewModels is always a good way to have loose coupled code, don't directly use any model from database, instead use Automapper to map your viewmodel to models from database.
In a partial view I have often used an IEnumerable where there is repeating logic
#model IEnumerable<Result>
#foreach (Result res in Model)
{
<div data-id="#res.ID">
<p>
<!-- Code goes here -->
</p>
</div>
}
View-model is just a conceptual term in MVC's presentation layer. Technically it's just a concrete class.
So real question here whether to use View-model/class or directly pass List.
Now back to the point - Wrapping 'only' list in another class will yield nothing unless you have few other members to be wrapped and passed to view. If there's something more than a List that your view needs, create a class (call it View-model in MVC) to wrap all that stuff and pass it to view.
There is really no reason to create a view model other than personal preference if you just have that one property UNLESS there is any chance that you will be adding more properties in the future.
However it is best practice to utilize interfaces whenever possible to keep your code decoupled. That said you should switch it to IEnumerable instead of list.
I always create a viewmodel, to pass data to my view, populate it in your controller and then pass it to your view. This is a clean way of working in MVC.
TIP : Make a new folder ViewModels in your Models folder.
The same way with Entities, never directly use your entities , use viewmodels that provide just the things you need in your views. Also take a look at Data Annotations , these can come in handy when using a viewmodel for form validation etc...
you can just create a viewmodel like Jelman said and in it just declare a list of your model like this by creating another class file in your model folder that is going to contain your list of model
namespace projectname.Models
{
public class ModelNameViewModel
{
public List<ListOfModel> { get; set; }
}
}
or you could simply Change the declaration on the top of your view to have a list of your model like this if there is no other field you need to add to the model
#model List<YourModelName>
While you can return that model to the view, chances are that not all data will be used in the view. Therefore we create view model classes which only hold data relevant to the view and we use a mapper (take a look at AutoMapper) to map between the two objects.
While this adds another class to manage, it keeps the domain model nice and clean and without any "view specific" properties (such as lists used in drop-downs etc.)
What you end up returning will end up being whatever you need in the view, as you might want to have some other data (or metadata) with that collection, in which case an object wrapper will be used.
Hope this helps.
It is more correct to create a new model containing a list. If tomorrow you need to add a new property, you can easily add it to this model without changing viewmodel in your view.
Compare:
You have a model with a list of objects:
class MyModel
{
public List<int> Comments {get;set;}
}
Your view looks like this:
#model MyModel
foreach(var comment in Model.Comments)
...
and when you need to add new property, you an easily do this:
class MyModel
{
public string Title {get;set;}
public List<int> Comments {get;set;}
}
your view will looks like
#model MyModel
...
Model.Title
...
foreach(var comment in Model.Comments)
...
So you do not need to change the model type in the controller and in the view, you simply add new properties to the created model

How to create a complex view model in ASP.NET MVC?

Creating and updating a complex view model is what I struggle with most of the time when I'm working on a web project.
For instance, I got a PageViewModel that takes a background image URL and a page title to pass to _Layout.cshtml, so
#model WebApplication.ViewModels.PageViewModel
<body background-image=#Model.BackgroundImageUrl>
...
</body
As for every other view model I now got 2 options:
Derive from PageViewModel
Create a property on PageViewModel to hold the specific view model (composition over inheritance etc.)
For obvious reasons I'm more inclined towards option 2. In any case, for GET actions I now need to initialise all the properties from PageViewModel as well as the properties from the view model I actually need for a particular action, e.g.
public PageViewModel
{
public string BackgroundImageUrl { get; set; }
public ContactViewModel Contact { get; set; }
}
is created like
public IActionResult Contact(int contactId)
{
...
var viewmodel = new PageViewModel
{
BackgroundImageUrl = ...,
ContactViewModel = new
{
...
}
}
}
which to me is a recipe for disaster.
Things get even worse on POST, because ideally I will post only those fields that are relevant to the action in question, that is
public IActionResult Contact(ContactViewModel viewmodel)
{
if (ModelState.IsValid)
{
... (this is the easy case)
return RedirectToAction(...)
}
... (now we have a problem)
}
If anything goes wrong in the POST action I need to rebuild the entire view model graph, which is not too bad for the above example but in a real world application this gets extremely messy very fast (just think of populating drop down lists with data from a store). Controller actions are supposed to be lean, aren't they? It doesn't feel right to make the view models responsible for retrieving their own data either, so probably I should come up with a view model factory. That in turn will produce a plethora of factories, one for each view model which, again, will get messy.
I'm wondering if there is a better approach.
One possibility to consider is to use a child action responsible for some portion of the layout.
So for example you could have the following child action which will be responsible for populating the background image URL:
[ChildActionOnly]
public ActionResult BackgroundImage()
{
var model = new MyViewModel
{
BackgroundImageUrl = ...
};
return PartialView(model);
}
and then have a corresponding partial view:
#model MyViewModel
<img src="#Model.BackgroundImageUrl" alt="" />
which can be included in your main Layout (which in this case doesn't need a view model because the different portions of it will be assembled from the child actions):
<body>
#Html.Action("BackgroundImage", "SomeController")
...
</body>
Using this approach the main action that is responsible for rendering the view doesn't need to know and assemble a complex view model. It will focus only on its specific view model.

use properties and metadata on base class in different namespace

I have a separate Visual Studio project where I keep my data model (EF6). So, my entities are in namespace Name1 (created by EF6 database first, but simplified below, for this example):
namespace Name1
{
public class Person
{
public string FName {get; set;}
public string LName {get; set;}
}
}
Now, I have created a new MVC 5 project, which references the data Visual Studio project so that I can access the entities. In my MVC project, I want to add some metadata to the entity like this:
namespace NameMvc
{
[MetadataType(typeof(PersonMetaData))]
public class Person : Name1.Person
{
}
public class PersonMetaData
{
[Display(Name = "Firstname")]
public string FName;
}
}
In my controller I want to get all the persons, so I have an Action like this:
using Name1;
using NameMvc;
-- controller class code
public ActionResult Index()
{
var persons = db.Person.ToList();
return View(persons);
}
-- controller class code
And in my view I try to access that via:
#model IEnumerable<NameMvc.Person>
Now, when I run the code I get an error:
The model item passed into the dictionary is of type 'System.Data.Entity.Infrastructure.DbQuery`1[Name1.Person]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable`1[Name.MvcPerson]'.
I probably did something wrong in the action, because db.Person comes from the Name1 namespace.
I want to use the metadata in my View so that when I do something like this #Html.DisplayNameFor(model => model.FName), it displays "Firstname".
You are getting an error rendering your view because you are returning IEnumerable<Name1.Person> from your action, but your view is expecting IEnumerable<NameMvc.Person>. When you are using a strongly-typed model in your view, it must match the model returned from the action.
There are two possible solutions for this error:
Change your view to use IEnumerable<Name1.Person> as its model, or
Change your action to return an IEnumerable<NameMvc.Person> as the model.
When you use MetadataType to add metadata to your model, the most common way is to use the fact that the original model class is generated as a partial class. You create another "part" to the partial class and add the MetadataType attribute to it.
However, partial classes cannot be used across assemblies. So this means that if you move your model to its own assembly, then you cannot add a partial class to your MVC project to add the metadata.
To solve this, you can do one of the following:
Add your metadata in your model's assembly. To do this, you use the partial class solution in your model's assembly. The problem with this is that you're now mixing view logic with data logic.
Create a new class in your MVC project which is a pseudo-copy of your data model to act as a view model. You add your metadata to that. Your action will return this view model and your view will use that. In your action, you copy the data from your data model to your view model.
I prefer option #2 for a couple of reasons:
It solves the problem you're facing, and
How data is presented to the user is often different than how I want it represented in my databases. This mapping allows me to handle that nicely.
The drawback to option #2 is the repetitive copying of data. However you could use tools like AutoMapper to simplify the data copying.

General approach for displaying a message on any page

I have a fairly simple (to explain) requirement for my ASP.NET MVC web application:
On any page, be able to display a message based on an event that happened on a preceding page. Messages may include dynamic content, such as information entered by the user in a form submission.
Similar to this...
The approach must work in POST-REDIRECT-GET scenarios.
If the user has two browser tabs (which share session state) open on the application, then any message should only be displayed in the tab in which the related event happened.
For example:
After submitting a feedback form, a message thanking the user (by name)
When logging in, a message telling the user the date/time when they last logged in to the application
My first thought was to use a strongly-typed view with an appropriate model (with a string property) for this. However this would mean changing every single existing non-strongly-typed view to use this model, and updating all existing models to inherit from this new model.
This seems like overkill.
An alternative is to store the message in TempData:
// In my controller
TempData["Message"] = "Some kind of message";
// In my shared master view
#if (TempData["Message"] != null)
{
<div class="message">#TempData["Message"]</div>
}
Using TempData is covered in a bit more detail in this blog posting. Interestingly, the author also suggests using a custom HTTP header as an alternative. An interesting idea, but does not work with POST-REDIRECT-GET.
Is using TempData the preferred option, or is there a "better" alternative? (e.g. something more strongly typed)
My first thought was to use a strongly-typed view with an appropriate model (with a string property) for this. However this would mean changing every single existing non-strongly-typed view to use this model, and updating all existing models to inherit from this new model.
This is the only consistent, reusable, testable solution to do this that I can imagine, despite the extra work it may cause.
It is best practice to use ViewModels to communicate between View and Controllers. You can have a base View Model and all other View Models derived from that as below:
public class BaseVM
{
public string Message{ get; set;}
}
public class CreateViewModel: BaseVM
{
public string CustoomerName{ get; set;}
}
You can populate the Message property while returning the model to the controller as below:
public ActionResult Step2()
{
//Some Logic
step2Model.Message = "Yes, Success..!!";
return View(step2Model);
}
After that, on each view page, you can check if that property has something in it.You can do so as below:
#if(!string.IsNullOrEmpty(Model.Message))
{
//Show message
}
EDIT:
OP is well aware of this approach, but still keeping this answer as it has a snippet to show how to do this in code. Secondly, when it comes to use ViewModels, I agree with following comment by CodeCaster in his answer.
This is the only consistent, reusable, testable solution to do this that I can imagine, despite the extra work it may cause.
You could make a convention in your project that a message to display would always go into ViewBag.InfoMessage dynamic variable. So, in your layout file you would display it if it's passed into the view from a controller.
More strict way would be to create a base model class with InfoMessage property and derive all other models / viewmodels from that base.
To persist the message through POST-REDIRECT-GET scenario, use a session variable which you'd clear once the value is sent to the view - to avoid displaying on more than one page.
Here some code:
public class BaseViewModel
{
public string InfoMessage { get; set; }
}
public class SpecificViewModel : BaseViewModel
{
// other model properties
}
In your controller then:
SpecificViewModel vm = new SpecificViewModel();
vm.InfoMessage = Session["InfoMessage"] as string;
Session["InfoMessage"] = null;
// other code
return View(vm);
Of course your view will have strongly typed model: SpecificViewModel.

ASP.NET MVC - posting back only a half of typed view's model?

Let's say you're developing your own stackoverflow in ASP.NET MVC :-)
You'd like to have a functionality to answer questions. So, the view should contain original question details (title, text, etc) and it should also contain the form to submit the answer. In order to render the view, you should provide both original question object and new empty answer object. You then want only answer to be posted back.
How would you define the type for this view? Would you implement a view model specific for this view that would contain both the question and an empty answer? What's the common approach here?
Ideas I have are:
Using a special view model object that contains both question and answer. View is bound to this object. When rendering the view, question details are used. When posting back, the object I get at controller only has answer-related fields populated.
Using ViewBag to pass question object. View is bound to answer model.
?
(no AJAX please, just the very basic scenario)
Your question doesn't make much sense. Your question would not be posted back because only form controls are posted, such as inputs, button values, etc.. Your question is just html text, and does not get posted.
You're going to return the question, and any answers as your model to the page. At the bottom, you're going to want a form that submits back an AnswerModel with a given question ID.
You might create a default AnswerModel that gets passed down when you render the view, but you're not going to work (or worry about) question and answer 'objects' just models. You might create a ThreadViewModel that contains a question and a list of current answers.
What I do is create four Interfaces:
public IQuestionDetail
{
string QuestionText { get; }
// Question Details
}
public IQuestionView
{
IQuestionDetail QuestionDetail { get; }
}
public IAnswerDetail
{
int/guid QuestionID { get; }
string AnwerText { get; }
// Anwer Details
}
public IAnswerView
{
IAnswerDetail AnswerDetail { get; }
}
Then I derive 4 classes
public QuestionDetail : IQuestionDetail
{ //implemented }
public AnswerDetail : IAnswerDetail
{ //implemented }
public QuestionViewModel : IQuestionView, IAnswerView
{ //implemented }
public AnswerModel : IAnswerView
{ //implemented }
A single view (Question/Detail.cs)
#model Models.QuestionViewModel
#html.Partial("partial-questionDetails", Model)
#html.Partial("partial-answerDetails", Model)
Two Partial Views
//partial-questionDetails.cshtml
#model Models.IQuestionView
#Html.DisplayFor(Model.QuestionDetail)
//partial-answerDetails.cshtml
#model Models.IAnswerView
#Html.BeginForm()
{
Html.EditorFor(Model.AnswerDetail)
}
QuestionController
public ActionResult Detail(IAnswerView AnswerModel)
{
// AnswerModel is only populated with the Answer Fields
// Do Stuff with AnswerModel
QuestionViewModel viewModel = new QuestionViewModel()
View(viewModel);
}
When building forms in ASP.NET MVC, I usually create two separate classes, one for populating the view (the ViewModel), and another for receiving data on submit. The ViewModel (usually read-only) may contain additional information required to populate the view, but the data object only has properties (with public setters) for submitting the form.
These classes will have several properties in common, so that the model binding in ASP.NET MVC will wire up properly. For simple forms, I don't always extract separate interfaces for each view, but if you want to guarantee that the properties match, then interfaces is an easy way to do it.
To do this, you would create a single interface, whose properties represent the form fields. Then, have each of your classes implement the interface, which will give you a compile-time guarantee that the form fields exist on both classes.

Categories

Resources