Complete MVC Noob warning.(2 hours learn time)
I've looked at a lot of MVC3 examples online but I havent found a simple example to do what I am trying to do.
What I want to do is two join two models and get some data into a view. The most obvious
public partial class Model1
{
public int ID { get; set; }
public int StudentID { get; set; }
public int CoachID { get; set; }
public String StudentName {get;set;}
}
public partial class Model2
{
public int CoachID { get; set; }
public String CoachName { get; set; }
}
Basically in my view I have to just join Model 1 and model 2 on CoachID and print
StudentName and CoachName in a grid.
How do I do this? How do I create the view model for this ?
How do I iterate through the view and print the joined data?
Can I instead just create a view in the database and directly attach a model and a view to it ?
Sounds simple, but Ive spent the last three hours online completely baffled
Create a StudentCoachViewModel with exactly the properties you need in it to display, nothing more, nothing less.
Populate a list of this viewmodel in your controller and send it to your view. Code sample to follow shortly.
Enumerate that list in your view
public class StudentCoachViewModel
{
public string CoachName { get; set; }
public string StudentName { get;set; }
}
In your controller something along the following lines (just typing this out, haven't checked in compiler)
public ActionResult Index()
{
//code to populate your model1 and model2 already assumed
var viewModels = (from m in model1List
join r in model2List on m.CoachId equals r.CoachId
select new StudentCoachViewModel(){ StudentName=m.StudentName,
CoachName = r.CoachName }).ToList();
return View(viewModels);
}
In your view, something along the lines of (clearly you want to format and use proper layout, table, etc which can be auto generated by visual studio)
#model IEnumerable<StudentCoachViewModel>
//other html content here
#foreach(var viewModel in Model)
{
#Html.DisplayFor(o=>o.CoachName) #Html.DisplayFor(o=>o.StudentName)
}
Now if you are only wanting a single one here rather than a list its even easier
public ActionResult Index(int id)
{
//code to load model1 and model2 already assumed to be in place. also assuming you loaded this data from a database by the id field being passed into this method.
return View(new StudentCoachViewModel(){ StudentName = model1.StudentName, CoachName = model2.CoachName});
}
And the view becomes then simply
#model StudentCoachViewModel
//other html here, h1, divs, etc whatever is in your view as html content.
#Html.EditorForModel()
or if you like to display each one instead of the above one line call:
#Html.LabelFor(o=>o.CoachName)
#Html.EditorFor(o=>o.CoachName)
try this
public partial class Model3{
public Model1 model1{get;set;}
public Model2 model2{get;set;}
}
bind Model3 into view.
View page can accept only one model, so you can't pass two model at the same time,
So you have to Create a another model with all the members of that two model...
then in controller, you should convert those two model to one model and pass it to view with help of new model one
or else you can create a another new model with those two model
Related
I have a controller that uses two classes. One is called IndexModel and the other IndexViewModel.
I pass the IndexViewModel into the IndexModel constructor.
[HttpGet]
public ActionResult Index()
{
var model = new IndexModel(new IndexViewModel());
var vm = model.GetViewModel();
return View("Index", vm);
}
Here is the view model class. Notice that the setter is private.
public class IndexViewModel
{
public IList<SelectListItem> SelectListItems { get; private set; }
public IndexViewModel()
{
this.SelectListItems = new List<SelectListItem>();
}
}
Here is the Model. When GetViewModel is called the SelectListItems list is populated and the view model returned.
public class IndexModel
{
private IndexViewModel vm;
public IndexModel(IndexViewModel IndexViewModel)
{
this.vm = IndexViewModel;
}
public IndexViewModel GetViewModel()
{
this.FillSelectListItems();
return vm;
}
private void FillSelectListItems()
{
// this data is pulled in from another class that queries a database...
var data = ...
foreach (var itm in data)
{
vm.SelectListItems.Add(new SelectListItem()
{
Value = itm.Id.ToString(),
Text = itm.Text,
});
}
}
}
I would appreciate any comments on how this is currently structured, but my main questions are:
Is it bad practice to write a bunch of methods, like FillSelectListItems(), that alter collection data and don't return a result?
Should I make the setter public so I can just return a list from my method and set the view model property that way?
What do you have to gain by making it private? A headache... make it public :)
There aren't any problems using view models in other view models... Imagine having a blog post... BlogPostViewModel... you would expect it to also have comments right? BlogPostViewModel > CommentViewModel
public class BlogPostViewModel
{
public string BlogPostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public List<CommentViewModel> Comments { get; set; }
}
Now when you render that, on your PostsController, at Posts/{id}, the view Posts/Index.cshtml would be rendered, and your comments can be rendered inside a partial view...
// Take this as pseudo code as there's some syntatic errors (can't be asked to open VS)
#for(int i = ... i < Model.Comments.Length... i++){
this.Html.Partial("_CommentsPartial.cshtml, Model.Comments[i])
}
On another note, if you wanted, you could pass your Model to the view as a JSON object as well without with this neat little hack... In your controller action...
this.ViewBag.Json = JsonConvert.SerializeObject<TViewModel>(viewModel);
And in your view just pick it backup...
<script> var json = #this.ViewBag.Json </script>
Hopefully this has provided some insight with regards to the purpose these View Models serve...
I have 2 Models. First one is created by EF and looks like:
public partial class PrinterMapping
{
public string MTPrinterID { get; set; }
public string NTPrinterID { get; set; }
public string Active { get; set; }
}
I created the second one (nothing to do with any database table) and looks like:
public class ExceptionModel
{
public string ExceptionMessage { get; set; }
public ExceptionModel(string exceptionMessage)
{
ExceptionMessage = exceptionMessage;
}
}
In my Index and Create views, the model that is automatically being passed is PrinterMapping. I wish to output ExceptionMessage property of the ExceptionModel model after populating it in a relevant way after saving to the table accessed by PrinterMapping. So in my Create controller, I am doing:
ExceptionModel exModel = new ExceptionModel(message);
where message parameter is a relevant string like "Printer X already exists".
My thoughts were to have a partial view called ExceptionMessageView where my ExceptionModel would be passed on to it and I will display:
#Html.DisplayFor(model => model.ExceptionMessage)
And in my Index and Create views, I will have a line like:
#Html.Partial("~/Views/Home/ExceptionMessageView.cshtml")
Am I over complicating things? This isn't working anyway since I don't fully understand how to pass on the populated ExceptionModel from my Create Controller to the ExceptionMessageView partial view.
Will a kind soul please enlighten?
I would have a complex Viewmodel "PrinterViewModel" that has properties for ExceptionModel and PrinterMapping.
The controller then passes the complete PrinterViewModel to the view.
In the View you would render partials by passing part of the complex Viewmodel to them.
#Html.Partial("ExceptionMessageView",Model.Exception)
For example, I have 3 models with an Id (int), a Name (string) and an active (bool). It's possible to use just one view for these models with the same properties ? A technique like a generic object ? Inheritance ? It's to avoid to write multiple views with the same HTML code.
You could create a ViewModel.
For sample:
public class CustomerViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public bool Active { get; set; }
}
And create another ViewModel with a type like this:
public class CustomersViewModel
{
public CustomerViewModel Customer1 { get; set; }
public CustomerViewModel Customer2 { get; set; }
public CustomerViewModel Customer3 { get; set; }
}
and type your view with this type:
#model CustomersViewModel
Or just use an collection to type your view
#model List<CustomerViewModel>
Take a look at this anwser!
https://stackoverflow.com/a/694179/316799
In a view you can either
specify shared base class for all models and use that.
use dynamic as model
split view in shared (extract into separate view) and specific part. Than call shared sub-view with either existing model (if using base class/dynamic) or simply new model constructed based on data in specific model.
Sample of extracting shared portion with inheritance. Using Html.Partial to render shared portion:
class SharedModel { /* Id,...*/ }
class SpecificModel : SharedModel { /* Special... */ }
SpecificView:
#model SpecificModel
#Html.Partial("SharedView", Model)
<div>#Model.Special</div>
SharedView:
#model SharedModel
<div>#Model.Id</div>
Side note: You can specify view name when returning result by using View if view name does not match action name:
return View("MySharedView", model);
In ASP.NET MVC4 you have the opportunity not to define a model for the view. This means leave the definition of the model empty (don't use #model MyNamespace.MyClass) and then it will automatically use "dynamic" as model.
Greetings
Christian
I'm trying to sort my result page (which is in another view than the filtration page). I have faced this weird issue I do not understand why keeps happening to me.
All the codes provided in very short form, please ask me if you need any other parts of my code for more information.
My Index view(where user filters results):
#model IEnumerable<Cars.Models.CarSearch>
#using (Html.BeginForm("SearchResult", "Home", FormMethod.Post,
new
{
id = "CategoryFormID",
data_modelListAction = #Url.Action("ModelList"),
data_makeListAction = #Url.Action("MakeList"),
data_editionListAction = #Url.Action("EditionList")
}))
{
<label>Make</label>
<select id="MakeID" name="carMake">
<option>All Makes</option>
</select>
}
My SearchResult view:
#model IEnumerable<Cars.Models.Car>
Make
My model:
public class Car
{
public String Make { get; set; } //is my table model
}
public class CarFilter {
public String carMake { get; set; }
}
public class CarSearch {
public CarFilter CarFilter { get; set; }
public byte PageSize { get; set; }
public short PageNumber { get; set; }
public int TotalRows { get; set; }
}
My Controller:
public ActionResult SearchResult(String sortOrder, CarFilter filters)
{
ViewBag.CurrentFilters = filters;
return View();
}
All I'm trying to do is to get carMake from Index post it to controller in CarFilter form (since in my code there are LOTS of fields in the form and I don't want to write them all down) and when user clicks on sort by Make it GET the SearchResult method and it's supposed to set filters = ViewBag.CurrentFilters which is the value user inputted from beginning.
Now the funny part is, when I replace CarFilter filters with String carMake and other places respectively. It works like a charm.
My question:
Why?
How can I do this with CarFilter filters?
UPDATE:
Problem is that filters = ViewBag.CurrentFilters in my SearchResult view does not work with the type CarFilter, because it keeps giving me NULL value when user clicked on the sort by Make.
Second UPDATE:
I tried changing filters = ViewBag.CurrentFilters with CarFilter = ViewBag.CurrentFilters. Now CarFilter filters in my SearchResult(...)method in my controller is not and null object, but ALL the values of the objects in the model class is null (which shouldn't be). I mean the filters object exists but it seems like the values of CarFilter class in my model haven't been passed by ViewBag.CurrentFilters to the view.
when you canged the name it worked because framework found property name and the bind it to what you have within action parameters doesnt work so nicely with objects. My advice is to stick with simple types
Here is similiar case:
How to send model object in Html.RenderAction (MVC3)
Its not a ViewBag problem thants how it works in general. Its the prime reason for using flatted models :/
I'm trying to learn MVC by building a full-featured website. I'm a little stuck when it comes to dealing with forms, and posting data, and models....
BTW: I'm using EF Code-First w/MS SQL CE
Here's the Models in question:
public class Assignment
{
public int AssignmentID { get; set; }
public int? CourseID { get; set; }
public string Name { get; set; }
// etc...
public virtual Course Course { get; set; }
}
public class Course
{
public int CourseID { get; set; }
// etc...
}
I'm loading a partial view that allows the user to add a new assignment
Controller:
public ActionResult Assignments()
{
var assignments = myContext.Assignments.OrderBy(x => x.DueDate);
return View(assignments);
}
[HttpPost]
public ActionResult AddAssignment(Assignment assignment)
{
myContext.Assignments.Add(assignment);
myContext.SaveChanges();
return RedirectToAction("Assignments");
}
// Returns a strongly-typed, partial view (type is Assignment)
public ActionResult AddAssignmentForm()
{
return PartialView();
}
Here's where I'm stuck: I want this form to have a drop down list for the different courses that an assignment could possibly belong to. For example, an assignment called "Chapter 3 Review, Questions 1-77" could belong to course "Pre-Algebra". However, if I use the code below, I have to explicitly declare the SelectListItems. I thought that with the given Assignment model above, I should be able to have the drop down list for Courses automatically generated using MVC awesomeness. What am I doing wrong?
AddAssignment Partial View:
#model MyModels.Assignment
#using(Html.BeginForm("AddAssignment", "Assignments"))
{
// Can't I create a drop down list without explicitly
// setting all of the SelectListItems?
#Html.DropDownListFor(x => x.Course, ....
}
Basically you are confusing/mixing your business model and your UI model.
The quick fix here is to add the data for the dropdown list to the ViewBag (a dynamic object).
Alternatively you could create a class AssignmentModel that contains the relevant Assignment properties and the List.
And No, this is not well supported in the templates.
You do realize you'll need some error handling in the Post method(s)?