MVC 5 Help Understanding ViewModel - c#

Okay, so i have my ViewModel and I understand what the controller does, I'm just having difficulty implementing it. I don't know how to code a controller for the ViewModel, i've tried researching it myself and can't find anything.
Here is my viewModel, how would I go about constructing the controller? Not asking you to do it for me, just how to do it
public class ViewOrderViewModel
{
//From ORDER Table
public int OrderId { get; set; }
public System.DateTime OrderDate { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string PostalCode { get; set; }
public string Country { get; set; }
public string Email { get; set; }
public decimal Total { get; set; }
//from Products
public List<Product> Products { get; set; }
}
UPDATE
public class ViewOrderController : Controller
{
// GET: ViewOrder
public ActionResult ViewOrders()
{
var order = new Order();
var viewModel = GetViewModel(order);
return View(viewModel);
}
public ViewOrderViewModel GetViewModel(Order orderObject)
{
ViewOrderViewModel viewModel = new ViewOrderViewModel();
viewModel.OrderId = orderObject.OrderId;
viewModel.OrderDate = orderObject.OrderDate;
viewModel.FirstName = orderObject.FirstName;
viewModel.LastName = orderObject.LastName;
viewModel.City = orderObject.City;
viewModel.Address = orderObject.Address;
viewModel.Country = orderObject.Country;
viewModel.Email = orderObject.Email;
viewModel.PostalCode = orderObject.PostalCode;
viewModel.Total = orderObject.Total;
return viewModel;
}
}
Still unsure about how to map the List of products in the ViewModel class to the list of products in the db

Typically, your view is going to be "bound" to the ViewModel. It's like saying "Ok, I'm the view for an 'Order' and I only need to worry about the properties that you defined in ViewOrderViewModel".
The controller is not required for that binding to happen. The binding is declared at the top of your view:
Order.cshtml
#Model MyProject.Web.ViewModels.ViewOrderViewModel
<div>
<!-- Html for the view-->
</div>
This allows you to access properties on that model within the view. Razor has some functions that make life easy. For example, if you want to display the OrderId it might look like this:
<span>OrderId: #Model.OrderId </span>
The view doesn't care what values are set for each of those properties, it only cares that the properties exist. Where the controller comes into play is populating those properties with the values you want and then passing the ViewModel to the view:
public ActionResult Order()
{
var viewModel = new ViewOrderViewModel();
// Load data into each property
viewModel.OrderId = 123; // etc..
// Return it to the view. Asp.net knows to return
// it to the Order.cshtml view because the view
// and the controller action share the same name.
return View(viewModel);
}
Edit: In response to your questions in the comments: If you need to populate your ViewModel with values from a different model (such as a database model) you can create a mapper like so:
public ViewOrderViewModel GetViewModel(Order orderObject)
{
ViewOrderViewModel viewModel = new ViewOrderViewModel();
viewModel.OrderId = orderObject.OrderId;
viewModel.FirstName = orderObject.FirstName;
// etc...
return viewModel;
}
and then in your controller you would do something like this:
// var order = new Order()
var viewModel = GetViewModel(order);

Related

C# MVC Display Drop down list from viewmodel in razor

So i have a viewmodel that contains a list of tags List<Tag> tags {get; set;}
public class BlogVM
{
public List<Blog> Blogs { get; set; }
public List<Tag> Tags { get; set; }
}
private EFMTMContext db = new EFMTMContext();
// GET: /Blog/
public ActionResult Index()
{
BlogVM viewModel = new BlogVM();
viewModel.Blogs = db.Blogs.ToList();
viewModel.Tags = db.Tags.ToList();
return View(viewModel);
}
the database of tags is stored into the viewModel.Tags and passed into the view.
The problem I am having is displaying them with the Html.DropDownList. what is displayed is
EFTest.EntityFramwork.Tag, EFTest.EntityFramwork.Tag, EFTest.EntityFramwork.Tag.
(Namespace.Folder.Tag) I was expecting the dropdownlist to populate
Football, Rugby, Tennis etc
#model EFManyToManyTest.ViewModels.BlogVM
#Html.DropDownList("tags", new SelectList(Model.Tags), "---", false)
What is the right way of accessing each of the tags from the viewmodel?
Hopefully someone can help.
Thanks!
Thought I'd add, i can loop through the contents of Model.Tags using,
#foreach (var item in Model.Tags)
{
#item.Name
}
no problem. This works, it's just the dropdownlist that doesnt
This now works using
public class Tag
{
public int **TagID** { get; set; }
public string **Name** { get; set; }
public List<Blog> Blogs { get; set; }
}
#Html.DropDownList("tags", new SelectList(Model.Tags, **"TagID"**, **"Name"**), "---", false)
TagID because that's the ID property in the class definition
You didn't specify value and text fields, take a look at this constructor of the SelectList:
public SelectList(
IEnumerable items,
string dataValueField,
string dataTextField
)
For instance
#Html.DropDownList("tags", new SelectList(Model.Tags, "Id", "Name"), "---", false)

How to Pass Details Model from Controller to View

I am accessing Data in Controller using a WCF Service.
public ActionResult Index()
{
DataRerieveClient _proxy = new DataRerieveClient();
var orderDetails = _proxy.GetProductDetails(null);
return View();
}
Now how to pass the orderdetails from Controller to view and how to access them in View.
Edit:
I have a Model :
public class OrderDetails
{
public int OrderId { get; set; }
public int ProductId { get; set; }
public decimal UnitPrice { get; set; }
public int quanity { get; set; }
public decimal Discount { get; set; }
}
and _proxy.GetProductDetails(null) returns List<ServiceType.OrderDetails>
Do I need a Local Model at all in this scenario?
How do I display the List values in a table in my view?
Edit2:
public class AutoMapperConfig
{
public static void Configure()
{
Mapper.Map(ServiceOrders.OrderDetails, NorthWindMVCWCF.Models.OrderDetails);
}
}
Now getting the Error
'NorthWindMVCWCF.ServiceOrders.OrderDetails' is a 'type', which is not valid in the given context
'NorthWindMVCWCF.Models.OrderDetails' is a 'type', which is not valid in the given context
I prefer to create view models, I would do it as follows:
Create View Models
public class OrderDetailViewModel
{
public int OrderId { get; set; }
public int ProductId { get; set; }
public decimal UnitPrice { get; set; }
public int Quanity { get; set; }
public decimal Discount { get; set; }
}
public class OrderDetailsViewModel
{
public OrderDetailsViewModel()
{
OrderDetails = new List<OrderDetailsViewModel>();
}
public List<OrderDetailsViewModel> OrderDetails { get; set; }
}
Manual projection
You could create an OrderDetails view model and project an instance manually as follows:
var orderDetailsViewModel = new OrderDetailsViewModel();
foreach(var orderdetail in orderDetails)
{
orderDetailsViewModel.Add(new OrderDetailsViewModel { OrderId = orderDetail.OrderId, ProductId = orderDetail.ProductId, UnitPrice = orderDetail.UnitPrice, Quanity = orderDetail.quantity, Discount = orderDetail.Discount });
}
AutoMapper alternative projection
Install AutoMapper, run the following from the package manager console:
Install-Package AutoMapper
Create an AutoMapperConfig.cs in the App_Start folder with the mappings, similar to below:
public static class AutoMapperConfig
{
public static void Configure()
{
Mapper.CreateMap<OrderDetails, OrderDetailViewModel>();
}
}
In your global asax call the configure method:
protected void Application_Start()
{
...
AutoMapperConfig.Configure();
...
}
Then map in your controller:
var orderDetailsViewModel = new OrderDetailsViewModel();
orderDetailsViewModel.OrderDetails = Mapper.Map<List<OrderDetails>, List<OrderDetailsViewModel>>(orderDetails);
I prefer to use the AutoMapper approach as the mapping is defined globally and can be reused within your app.
Returning your view model
Your view model would then be passed back as follows:
return View(orderDetailsViewModel);
Razor output
You access it in your view by adding a model reference at the top:
#model OrderDetailsViewModel
Then output the properties as follows, I've only included OrderId but you can just add the fields the same way:
<table>
<tr>
<th>OrderId</th>
</tr>
#foreach(var orderDetail in Model.OrderDetails)
{
<tr>
<td>#orderDetail.OrderId</td>
</tr>
}
</table>
You have create ViewModel for that.
For example in your model folder create a class:
public class MyViewModel // Whatever name you want to give
{
//My fields which I want to pass to View
publis string Field1{get;set;}
etc
etc
}
public ActionResult Index()
{
DataRerieveClient _proxy = new DataRerieveClient();
var orderDetails = _proxy.GetProductDetails(null);
List<MyViewModel> viewModelList = new List<MyViewModel>();
foreach(var orderDetail in orderDetails)
{
MyViewModel viewModel = new MyViewModel(); //Create an object of your ViewModel
viewModel.Field1 = orderDetails.Field1; //set all feilds like that
viewModelList.Add(viewModel);
}
return View(viewModelList); // Pass View Model to View
}
Note: You have to create View for your ViewModel
Like
#model `List<MyViewModel>`
then use this ViewModel to access properties.
To know more about what is ViewModel, refer to the link below:
http://sampathloku.blogspot.ae/2012/10/how-to-use-viewmodel-with-aspnet-mvc.html
in the Controller class write :
public ActionResult Index()
{
DataRerieveClient _proxy = new DataRerieveClient();
var orderDetails = _proxy.GetProductDetails(null);
return View(orderDetails);
}
i assume your project name is MvcApplication2 and your class name is in Model Folder. so add follow code in top of View (sample.cshtml) :
#model MvcApplication2.Models.OrderDetail
ok , you can use this code to access properties of DataRecieveClient :
<div class="display-label">
#Html.DisplayNameFor(model => model.F1)
</div>
<div class="display-field">
#Html.DisplayFor(model => model.F1)
</div>
Sorry for poor english.!

Using only a few properties from Model in my ViewModel in MVC

I am getting stuck using a ViewModel. Suppose I want to give a logged-in person an Edit Form with only a few of the fields from my Person domain model (maybe I want to custom annotate validations in the ViewModel).
I am getting stuck in two separate places in the controller (I have marked them with "<<< >>>").
When I pass the whole Person object as a property to my ViewModel, I know what to do. I can get my code to only update the name fields, but then I have lost my ability to validate the individual properties in my ViewModel. On the hand if I limit the properties in my ViewModel to only to a few properties, then my code in the GET section where I cann vm.Person doesn't work, since I am not passing the Person.
I scanned many examples on SO, but they were all using AutoMapper. Can I accomplish this without a mapper, and/or how do I write my own? And thanks in advance!
Model:
public class Person()
{
public int PersonId { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string Email { get; set; }
}
ViewModel:
public class LoggedInPersonViewModel()
{
public int PersonId { get; set; }
[Required(ErrorMessage = "Last Name is required")]
public string LastName { get; set; }
public string FirstName { get; set; }
}
Repository:
public Person GetLoggedInPerson()
{
var user = HttpContext.Current.User.Identity;
var userid = user.GetUserId();
return db.People.SingleOrDefault(i => i.UserId == userid);
}
Controller:
public class RegistrationController : Controller
{
//Get Logged in User, Edit Form
public ActionResult UpdateDetails()
{
LoggedInPersonViewModel vm = new LoggedInPersonViewModel();
<<<Do I also need a Person property in my ViewModel>>>
vm.Person = repository.GetLoggedInPerson();
return View(vm);
}
//POST
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult UpdateDetails(LoggedInPersonViewModel loggedinpersonviewmodel)
{
if (ModelState.IsValid)
{
<<<what do i do here? is this correct? Again I cannot use Person if not in my VM.>>>
//Person person = db.People.Find(loggedinpersonviewmodel.PersonId);
//Person.FirstName = loggedinpersonviewmodel.FirstName;
//Person.LastName = loggedinpersonviewmodel.LastName;
//db.Entry(person).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index", "Person");
}
return View(loggedinpersonviewmodel);
}
}
}
Is there a way...or do I have to use AutoMapper for this?
I think you need to Map LoggedInPersonViewModel and Person. Example
public ActionResult UpdateDetails()
{
var person = repository.GetLoggedInPerson();
LoggedInPersonViewModel vm = new LoggedInPersonViewModel();
vm.PersonId = person.PersonId;
//Rest of properties
...
//return view model
return View(vm);
}
I would recommend AutoMapper this type of work. i.e. AutoMapper is a simple little library built to solve a deceptively complex problem - getting rid of code that mapped one object to another.

How to show data from two ViewModels in one View

I would like to fill 2 divs in View with data from 2 ViewModels, but I have a problem.
My 2 viewModels:
public class ChatLogsNameTimeViewModel
{
public UserProfile UserProfile { get; set; }
public string Message { get; set; }
public DateTime Time { get; set; }
}
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public bool IsOnline { get; set; }
public virtual ICollection<ChatLogs> ChatLogs { get; set; }
}
Which means I want to show data from ChatLogsNameTimeViewModel in one div in View and data from UserProfile in other div in View.
This is my ViewModel that uses both viewModels above:
public class ChatLogsUsersViewModel
{
public IEnumerable<ChatLogsNameTimeViewModel> ChatLogs { get; set; }
public IEnumerable<UserProfile> Users { get; set; }
}
And this is my Index() action in controller:
var chatLogs = db.getChatLog().ToList();
var users = dba.getOnlineUsers().ToList();
var view = new ChatLogsUsersViewModel(chatLogs, users);
return View(view);
My problem is that I can not access to ViewModel attributes at all.
When I create foreach loop in view all I can access is this:
Which means I cannot access attributes at all to print them in foreach.
I have this in View:
#model IEnumerable<Chat.Models.ChatLogsUsersViewModel>
I assume that I am not doing something right in my controller. I have methods getChatLog() and getOnlineUsers() implemented in Model, they work alone no problem. I just don't know how to make them work together in one view.
You need to update the type of you view.
You are not passing the view a list of Chat.Models.ChatLogsUsersViewModel, you only have one and this model has two lists.
So update it to:
#model Chat.Models.ChatLogsUsersViewModel
Your model is strongly typed towards the wrong model. As Queti put it, your model should be typed #model Chat.Models.ChatLogsUsersViewModel.
As is, your model is attempting to access a collection of these models. You should find that if you do:
#for each (var x in Model) {
x.
}
x is then a single ChatLogsUsersViewModel and should display its properties in your dev environment. But again, this is not how you want to strongly type your model here.
If I'm not mistaken, this is what you are trying to do, and you can still access your data using a for each loop:
#model Chat.Models.ChatLogsUsersViewModel
for each (var log in Model._chatLogs) {
#<div>#log.Message</div>
}
...
for each (var user in Model._users) {
#<div>#user.UserName</div>
}

Is it possible to validate an MVC-3 model without validating "sub-models"?

I have a class that requires another class to be specified, but I don't want the MVC ModelState validator to check whether the secondary model is valid. Is this possible?
Here's a brief overview:
My entities look something like this:
public class WidgetType
{
public long Id { get; private set; }
[Required]
public string Name { get; set; }
...
}
public class Widget
{
public long Id { get; private set; }
[Required]
public string Name { get; set; }
[Required]
public WidgetType WidgetType { get; set; }
...
}
I have them encapsulated in a WidgetViewModel class that I'm passing to/from the View like this:
public class WidgetViewModel
{
public Widget Widget { get; set; }
public ICollection<WidgetType> WidgetTypes
{
get
{
return _repository.GetWidgets();
}
}
...
}
My view looks something like this:
...
#Html.DropDownListFor( m => m.Widget.WidgetType.Id, new SelectList( new EquipmentViewModel().EquipmentTypes, "Id", "Name" ) )
...
All of this works except for validation. ModelState.IsValid is always false because "Widget.WidgetType.Name" is required. I need the user to select a WidgetType, but I don't want ModelState to be validated deeper than "Widget.WidgetType.Id" (which should be all that Widget needs for its foreign key?).
Is there a better way to do this? I feel like there should be some way to validate without recursively inspecting deeper into the properties, but I can't find it. What am I missing...?
public class WidgetViewModel
{
[Required]
public string Name { get; set; }
[Required]
public WidgetType WidgetTypeId { get; set; }
public SelectList WidgetTypes
{
get
{
//This should be popuplated in your controller or factory not in the view model
retun new SelectList{ _repository.GetWidgets(),"Id","Name");
}
}
}
In your view
#Html.DropDownListFor( m => m.WidgetTypeId, Model.WidgetTypes)
And in your controller
public ActionResult Create(WidgetViewModel model)
{
Widget widget = new Widget{
Name = model.Name,
WidgetType = yourManager.GetWidgetTypeByID(model.WigetTypeId);
};
yourManager.Create(widget);
//...
}
If all you need in your view is the WidgetID then you don't need to include the entire Widget in the WidgetViewModel. Just have property called WidgetID. View model classes should have only the data the is necessary for the view.
In the controller action method that is called when you submit the form, you can use the WidgetID to fetch the Widget object from the database if it is needed.
http://blog.stevensanderson.com/2010/02/19/partial-validation-in-aspnet-mvc-2/ gives an example of partial validation

Categories

Resources