Untill now in C# MVC3 i only had to use one controller which used only one model.
But now i think i need access to two models in my controller. I'm wondering if this is allowed in the MVC pattern?
I have a ProfileController. The ProfileController shows the profile info of a user obviously. But there's also some other data that i wish to show, like the groups a user created. For that i also have a GroupsModel.
How am i supposed to get both data from one controller?
How am i supposed to get both data from one controller?
By using a view model:
public class MyViewModel
{
public ProfileModel Profile { get; set; }
public GroupsModel Groups { get; set; }
}
and then passing this view model to the view:
public ActionResult ShowProfile()
{
var model = new MyViewModel();
model.Profile = ...
model.Groups = ...
return View(model);
}
And now your view will be strongly typed to the view model:
#model MyViewModel
and you can display information:
#Html.DisplayFor(x => x.Profile.SomeProperty)
Assuming that you aren't putting too much into a single view/controller, why not compose a simple view model which has the data you need?
public class ProfileInfo
{
public Person Person
{
get;
set;
}
public List<Group> Groups
{
get;
set;
}
}
Related
I am quite new to C# and asp.net mvc and I have been trying to find an answer to my question here and in other foras but have not succeeded. However if this question has been asked and answered before then I apologize and could you please link to that post.
I am trying to create a treeview where I pass a list of ViewModels to my View.
I use code-first approach and have added a migration between my model and the database.
My problem is how to pass a list of objects of the ViewModel to the View?
I can pass a list of objects of the model from the controller but how to pass a list of objects of the ViewModel? Is that possible?
Maybe there is a simple solution to this that I have not found yet and maybe I am doing this all wrong. Anyway I really need some help.
public class MyClass
{
public int Id { get; set; }
public int ParentId { get; set; }
public string Name { get; set; }
}
public class MyClassViewModel
{
public MyClass Parent { get; set; }
public List<MyClass> Children
{
get { return GetChildren(); }
}
public List<MyClass> GetChildren()
{
var listOfChildren = new List<MyClass>().Where(c => c.ParentId ==
Parent.Id).ToList();
return listOfChildren;
}
}
public ActionResult CreateTree()
{
var viewModel = new MyClassViewModel();
//This only returns one instance of an object, how to return a list?
return View("CreateTree", viewModel);
}
It is very simple, you just pass the list of your view model to the view:
[HttpGet()]
public IActionResult CreateTree()
{
List<MyViewModel> viewModelList = MyViewModel.GetList();
return View("CreateTree", viewModelList);
}
On the view, you set the expected type for the model via the #model directive.
#model List<MyViewModel>
#{
//stuff
}
Now you can just iterate through your list of viewmodel.
#foreach (MyViewModel item in Model)
{
//stuff
}
You could create a class, containing a list of your ViewModels
public class ModelWithListOfViewModels
{
public List<ViewModel> ViewModels { get; set; }
}
Add your ViewModels to the list in the controller
public ModelWithListOfViewModels model { get; set; }
[HttpGet()]
public ActionResult FillList()
{
model = new ModelWithListOfViewModels();
//Here you fill the list with the ViewModels you want to pass to the View
model.ViewModels.Add(/** ViewModel here **/);
return View("CreateTree", model);
}
Then simply loop over the list to get your model in the View
#model ModelWithListOfViewModels
#foreach (var vm in Model.ViewModels)
{
//use vm to get access to a ViewModel added in the list
}
I have a ParentView that has few fields of its own. This ParentView renders a single PartialView out of 3 different PartialViews based on dropdown selected in the ParentView. So there is PartialViewA, PartialViewB and PartialViewC. At a time only one will be rendered. Each has its own Table ie. model class.
Eg: PartialViewAModel:ParentViewModel .. and so on.
One of the ways I could think of collecting data is by using FormCollection.
So if dropdownvalue is A I would know what keys I have to pick and where to store.
But is there a more elegant way for binding the data? Had there been only one view and model I could have simply used Modelclass in the binding. Eg: ublic
[HttpPost]
ActionResult Create(CustomerClass cust)
If you specify a Parent View Model that has child Partial View Models, like this:
public class ParentViewModel
{
public ParentViewModel()
{
PartialViewModel1 = new PartialViewModel1();
PartialViewModel2 = new PartialViewModel2();
PartialViewModel3 = new PartialViewModel3();
}
public string PartialViewType { get; set; } /* Value to determine which view to show */
public PartialViewModel1 PartialViewModel1 { get; set; }
public PartialViewModel2 PartialViewModel2 { get; set; }
public PartialViewModel3 PartialViewModel3 { get; set; }
}
Where the Partial View Models, for example PartialViewModel1, have the properties unique to that view model, like this:
public class PartialViewModel1
{
public string Property1_1 { get; set; }
public string Property1_2 { get; set; }
public string Property1_3 { get; set; }
}
You can specify your Parent View so that it has a form which contains the partial views, which can be toggled on the client side with a bit of JavaScript (which I haven't included but should be simple enough :) ):
#model Models.ParentViewModel
#using (Html.BeginForm("Update", "Home", FormMethod.Post))
{
#Html.TextBoxFor(x => x.PartialViewType) /* Change this to a drop down */
#Html.Partial("PartialView1")
#Html.Partial("PartialView2")
#Html.Partial("PartialView3")
<input type="submit" value="Submit" />
}
The Partial Views look like this, e.g. for PartialView1:
#model Models.ParentViewModel
<h3>Partial View 1</h3>
<p>#Html.TextBoxFor(x => x.PartialViewModel1.Property1_1)</p>
<p>#Html.TextBoxFor(x => x.PartialViewModel1.Property1_2)</p>
<p>#Html.TextBoxFor(x => x.PartialViewModel1.Property1_3)</p>
So now you can submit to the Update action on your controller by passing through the ParentViewModel:
[HttpPost]
public ActionResult Update(ParentViewModel model)
{
// Do whatever processing required.
// You can switch on model.PartialViewType to process the appropriate PartialView fields
return View("Index", model);
}
When you submit, the model should then contain whatever has been submitted in the appropriate Partial View Model properties.
Hope this helps!
From my understanding, a view model represents only the data that I would like to display in a particular view and can consist of properties from multiple domain models. I also understand that view models act as a data-binder between my domain models and views.
With that said, I have the following model:
public class Image
{
public int ImageID { get; set; }
public string ImageCaption { get; set; }
public int NumOfLikes { get; set; }
public string ImageFilePath { get; set; }
}
And a ProfileViewModels class with the the following view models:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
namespace Splend.Models
{
public class ImageUploadViewModel
{
}
public class EditUserProfileViewModel
{
}
public class ViewUserProfileViewModel
{
}
public class ViewImagesViewModel
{
}
}
Firstly, I assumed that this is the correct way of creating view models based on AccountViewModels generated by ASP.NET Identity. Secondly, I have not scaffolded any items from these two models. So I have no views or controllers yet.
Questions
How would my view models bind data between my domain model and views?
How would I map my view models to my domain models?
How does Entity Framework know that my view model properties are referenced from my domain model and that I am not creating new model properties, i.e. is
there a particular view model and property naming convention that I
have to use to tell Entity Framework that ImageUploadViewModel is a view model?
I wouldn't recommend a one-size-fits-all approach to creating and managing view models cause it gets complicated quickly, especially if you come to assign validation to them.
It seems like you're trying to anticipate the view models that you need before you create your views?
Generally I would recommend having a 1:1 relationship between your view models and your views and ensure that your view models contain the least amount of fields to hold only the information that is required to display said view.
I have assumed that you have a users that have multiple Images and the following view models:
public class ProfileViewModel
{
public string UserName { get; set; }
public string Password { get; set; }
public string FullName { get; set; }
public List<ImageViewModel> UserImages { get; set; }
}
// View model to represent user images
public class ImageViewModel
{
public int ImageID { get; set; }
public string ImageCaption { get; set; }
public int NumOfLikes { get; set; }
public string ImageFilePath { get; set; }
}
Here's how to map to your view model. Simply create a new instance of your view model class(es) , assign the values to them and pass them to the view:
[HttpGet]
public ActionResult EditUser()
{
//
// LOAD INFO ABOUT THE USER (Using a service layer, but you're probably just using Entity Framework)
//
// Get the user ID?
var userId = this.User.Identity.GetUserId();
// Get the user domain models from the database?
var userDomainModel = this.UserService.GetUserInfo(userId);
// Get the user's image domain models from the database?
var userImagesDomainModelList = this.UserImageService.GetImagesForUser(userId);
//
// THE VIEW MODEL MAPPING STARTS HERE...
//
// Create an instance of your view model and assign basic info
var responseModel = ProfileViewModel()
{
public string UserName = userDomainModel.UserName;
public string Password = userDomainModel.Password;
public string FullName = userDomainModel.FullName;
}
// Initialise list for images
responseModel.UserImages = new List<ImageViewModel>();
// Loop though each image domain model and assign them to the main view model
foreach (var imageDomainModel in userImagesDomainModelList)
{
// Initialise image view model
var imageViewModel = new ImageViewModel()
{
ImageID = imageDomainModel.ImageID,
ImageCaption = imageDomainModel.ImageCaption,
NumOfLikes = imageDomainModel.NumOfLikes,
ImageFilePath = imageDomainModel.ImageFilePath
};
// Add the image view model to the main view model
responseModel.UserImages.Add(imageViewModel);
}
// Pass the view model back to the view
return View(responseModel);
}
Just map the incoming values that have been posted to your domain models in the same way:
[HttpPost]
public ActionResult UpdateImage(UpdateImageViewModel requestResponseModel)
{
// Get the user ID?
var userId = this.User.Identity.GetUserId();
// Map incoming view model values to domain model
var imageDomainModel = new Image()
{
ImageID = requestResponseModel.ImageId,
ImageCaption = requestResponseModel.ImageCaption,
NumOfLikes = requestResponseModel.NumOfLikes,
ImageFilePath = requestResponseModel.ImageFilePath,
}
try
{
// Send the domain model up to your service layer ready to be saved
this.UserImageService.UpdateImage(userId, imageDomainModel);
ViewBag.SuccessMessage = "Your image was updated";
}
catch (Exception)
{
// Critical error saving user image
}
return View(requestResponseModel);
}
At no point will you try to attach your View Models to your Entity Framework context. If you did, then it would reject it. It knows which models it can accept by the IDbSet<object> properties that are defined within the definition of your DbContext:
public class YourDbContext : DbContext
{
public YourDbContext() : ("YourDbConnectionStringName")
{
}
// Definitions of valid Entity Framework objects
public virtual IDbSet<User> Users { get; set; }
public virtual IDbSet<Image> Images { get; set; }
}
var dbContext = new YourDbContext();
var imageViewModel = new ImageViewModel();
var imageDomainModel = new Image();
// Adding object defined within your db context is fine...
dbContext.Set<Image>().Add(imageDomainModel); // OK
dbContext.Set<Image>().Add(imageViewModel); // Not OK, will result in exception
I personally don't sent Domain Models up to my service layer, I use DTOs (Data Transfer Objects). But that's a whole other story...
Please see this post from me that explains how I lay out my MVC applications: https://stackoverflow.com/a/32890875/894792
I'm having trouble passing a viewmodel into a view. I have two views: a Search view and a GeneralForm view. Search passes search results into GeneralForm's viewmodel.
Say the GeneralForm is a complex viewmodel that holds two other viewmodels:
public class GeneralFormViewModel
{
public GeneralInfoViewModel GeneralInfo { get; set; }
public NotesViewModel Notes { get; set; }
}
public class GeneralInfoViewModel
{
[Required(ErrorMessage = "Please enter the person's name.")]
[DisplayName("Name:")]
public string Name { get; set; }
[Required(ErrorMessage = "Please enter the person's ID.")]
[DisplayName("ID:")]
public int ID { get; set; }
}
public class NotesViewModel
{ // etc.
(I set up this way in order to use multiple #Html.BeginForms on my GeneralForm view. In this way, I hope to POST and validate small sections of the entire general form, one at a time, using KnockoutJS and AJAX.)
[HttpPost]
public ActionResult Search(SearchViewModel vm)
{
var query = // do some searching
var viewmodel = new GeneralFormViewModel()
{
GeneralInfo = new GeneralInformationViewModel
{
ID = query.id,
Name = query.name
}
};
return RedirectToAction("GeneralForm", viewmodel);
}
At this point, viewmodel.GeneralInfo is not null, and the viewmodel is passed to the GeneralForm controller.
[HttpGet]
public ActionResult GeneralForm(GeneralFormViewModel model)
{
return View(model);
}
Now model.GeneralInfo is null. What conventions of MVC am I breaking by doing this, and how can I get the GeneralForm view to render the data acquired via the Search controller to the GeneralForm view?
Problem is You can't send data with a RedirectAction.
you're doing a 301 redirection and that goes back to the client.
Store it in a TempData or Session ...
See the following post:
passing model and parameter with RedirectToAction
I am having trouble understanding if this is possible or not. I have a stored procedures and a view model classes and one controller. Is it possible that I could add another view model class with a different stored procedure to use one controller and display the the information from both stored procedures into one view?
This is my current Model:
public class People
{
public class Jobs
{
public int jobID { get; set; }
public string jobName { get; set; }
}
public class Names
{
public int userId{get;set}
public string userName {get; set;}
}
}
Yes, this is totally possible. Simply create a view model that contains the properties that would be populated by your stored procedures and then pass that to your view:
public class PeopleViewModel
{
public List<Job> Jobs { get; set; }
public List<Name> Names { get; set; }
}
Then the action method:
public ActionResult Index()
{
var model = new PeopleViewModel();
model.Jobs = // ... fetch from database
model.Names = // ... fetch from database
return View(model);
}
Update per comments
As you've stated Jobs and Names are collections, the model code you've shown won't solve your problem. It sounds like this is what you really want:
public class Job // Notice I've removed the pluralisation
{
public int jobID { get; set; }
public string jobName { get; set; }
}
public class Name // Notice I've removed the pluralisation
{
public int userId { get; set; }
public string userName { get; set; }
}
Now, your People class can contain lists of those:
public class People
{
public List<Job> Jobs { get; set; }
public List<Name> Names { get; set; }
}
Now you can wrap this in a view model:
public class PeopleViewModel
{
public People People { get; set; }
}
Then you would populate it from your action:
public ActionResult Index()
{
var model = new PeopleViewModel();
model.People = // ... fetch from database
return View(model);
}
The thing is, if Jobs and Names are unrelated, there's no point in wrapping them in a People class first. You'd simply do the following (as discussed above):
public class PeopleViewModel
{
public List<Job> Jobs { get; set; }
public List<Name> Names { get; set; }
}
Think of a view model as a way of representing just a slice of the data you want to display in your view, nothing more, nothing less. All the code above does is define what data you'd like to see in that view. As the properties on the view model are public, you can populate them in your controller's action using whatever method you currently use.
Second Update
I think it would probably be helpful to show you a full example as to how fits together for your views. Firstly, I'll show you the quick and dirty way of accessing the data in your view model and then afterwards I'll show you how to do the same with DisplayTemplates.
Quick and Dirty Approach
So first up, let's assume we have an action called Index like the following:
public ActionResult Index()
{
var model = new PeopleViewModel();
model.Jobs = // ... fetch from database
model.Names = // ... fetch from database
return View(model);
}
Here's the Index view:
#model PeopleViewModel
#foreach (var job in Model.Jobs)
{
#Html.DisplayFor(m => job.JobId)
#Html.DisplayFor(m => job.JobName)
}
#foreach (var name in Model.Names)
{
#Html.DisplayFor(m => name.UserId)
#Html.DisplayFor(m => name.UserName)
}
Notice how the view expects an instance of PeopleViewModel and it's simply looping over each individual item in each collection, printing the contents. However, whilst this is fine for very simple views, mixing logic with your HTML becomes a maintenance headache with more complicated views and larger projects. With that in mind, we can make use of DisplayTemplates instead.
Second Approach - Using Templates
The action in your controller remains the same. However, we need to make some other changes. What we're working towards is turning the Index view into this:
#model PeopleViewModel
#Html.DisplayFor(m => m.Jobs)
#Html.DisplayFor(m => m.Names)
By calling Html.DisplayFor in this way, it will try to locate a DisplayTemplate which corresponds to the data type it's being passed. In this case, the helper is smart enough to know that we're passing collections to it and so it will look for a template that matches the data type the collection contains. That means we need to make templates for the types Job and Name. To do that, follow these steps:
Create a DisplayTemplates folder inside your view's current folder (e.g. if your view is Home\Index.cshtml, create the folder Home\DisplayTemplates).
Create a strongly-typed view in that directory with the name that matches your model (i.e. in this case you would make two views, which would be called Job.cshtml and Name.cshtml, respectively).
Now you can move all of the display logic into those two templates. So they would look like the following:
Job.cshtml
#model Job
#Html.DisplayFor(m => m.JobId)
#Html.DisplayFor(m => m.JobName)
Name.cshtml
#model Name
#Html.DisplayFor(m => m.UserId)
#Html.DisplayFor(m => m.UserName)
Hopefully that clears things up a little.