How to add a list<T> to view with a single model - asp.net-mvc-4

Getting an error while trying to add a grid to my detail page. The error is:
The model item passed into the dictionary is of type 'GridMvc.Html.HtmlGrid1[MyApp.Models.RecipientActivityMetadata]', but this dictionary requires a model item of type 'System.Collections.Generic.List1[MyApp.Models.RecipientActivityMetadata]'.
MVC4 View is a combination of a detail page and a list. I am using a viewmodel that looks like this:
public class FormViewModel()
{
public RecipientMetadata Recipient { get; set; }
public StudentActivityMetadata StudentActivity { get; set; }
public List<RecipientActivityMetadata> RecipientActivites { get; set; }
}
The view top is:
#model MyApp.Models.ViewModels.FormViewModel
and it renders a partial view which contains the list:
#Html.Partial("_grid", Model.RecipientActivites)
and the partial looks like this:
#using GridMvc.Html
#model List<MyApp.Models.RecipientActivityMetadata>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<div>
#Html.Grid(Model).Columns(columns =>
{
columns.Add(c => c.ActCount).Titled("Activity Num");
columns.Add(c => c.ActivityType).Titled("Activity Type");
columns.Add(c => c.FundCode).Titled("FundCode");
columns.Add(c => c.Hours).Titled("Hours");
}).WithPaging(10)
</div>

From Comment to Answer
According to the documentation provided by Grid.Mvc, #Html.Grid uses a partial view _Grid.cshtml. Because your partial view also has same name, the solution is to use a different name for your partial view.

Related

Implementation of Kendo Listview control in MVC

I was trying to implement Listview control of Kendo UI for MVC. I am trying to bind the list view with my model but I am getting this error :
"CS1977: Cannot use a lambda expression as an argument to a dynamically dispatched operation without first casting it to a delegate or expression tree type"
I have checked some other questions on stackoverflow with the same error but I am unable to know the cause for this error as this is kendo Syntax and there is nothing wrong with my code as far as I know.
The error is in this line::.DataSource(ds => ds
View Page:
#{
ViewBag.Title = "Courses";
}
#using Kendo.Mvc.UI
<h2>Courses</h2>
Back
<div class="bodywrap">
<div class="CommonClass">
#( Html.Kendo().ListView<K_SampleProject.Models.CourseModel>(Model)
.Name("listView")
.TagName("div")
.ClientTemplateId("template")
.DataSource(ds => ds
.Model(model =>
{
//The unique identifier (primary key) of the model is the ProductID property
model.Id(p => p.ProductID);
// Declare a model field and optionally specify its default value (used when a new model instance is created)
model.Field(p => p.ProductName).DefaultValue("N/A");
// Declare a model field and make it readonly
model.Field(p => p.UnitPrice).Editable(false);
})
)
.Pageable()
)
</div>
</div>
<script type="text/x-kendo-tmpl" id="template">
<div class="product">
<img src="#Url.Content("~/content/web/foods/")${ProductID}.jpg" alt="${ProductName} image" />
<h3>${ProductName}</h3>
<dl>
<dt>Price:</dt>
<dd>${kendo.toString(UnitPrice, "c")}</dd>
</dl>
</div>
</script>
Model
namespace K_SampleProject.Models
{
public class CourseModel
{
public List<tbl_Courses> CourseList { get; set; }
public string ProductID { get; set; }
public string ProductName { get; set; }
public string UnitPrice { get; set; }
}
}
Controller
public ActionResult Courses()
{
CourseModel Model = new CourseModel();
RegistrationService ObjService = new RegistrationService();
Model.CourseList = ObjService.GetCourses();
return View(Model);
}
The main error in your code is that you passing single CourseModel class to the list, when it expects the List of CourseModel.
So, your Controller should looks like:
public ActionResult Courses()
{
List<CourseModel> result;
CourseModel Model = new CourseModel();
RegistrationService ObjService = new RegistrationService();
Model.CourseList = ObjService.GetCourses();
result.Add(Model);
return View(result);
}
I also advise:
Add #model List<CourseModel> in top of the View
If it is a PartialView (not main view like index) change return for: return PartialView(result);

How to Use Two Same Model in a View? [MVC4]

I'm trying to create a status update page where I want a user to insert status message in Index page and also, I want to show all inserted all status messages in the same Index page.
This is my Model code:
public class Statuses
{
[Key]
public int StatusID { get; set; }
[DataType(DataType.MultilineText)]
[Required]
public string message { get; set; }
}
public class StatusContext : DbContext
{
public DbSet<Statuses> Status { get; set; }
}
And, I used #Html.EditorFor(model => model.message) in the Index.cshtml page.
To show the editor, I used the following model in View.
#model LearnStart.Models.Statuses
However, to show all the status messages below the Multiline TextArea, I think I'm supposed to use the below one.
#model IEnumerable<LearnStart.Models.Statuses>
How to use both model in same view so that I can display both the text area (to insert the status message) and to list all available status messages below it?
First, you should not be passing your entities directly to your view. The recommended best practice is to use View Models, which are models tailored specifically to your view.
Second, when using a view model you can now do this, since it's not tied to your data model entities:
public class MyActionViewModel {
public List<StatusesViewModel> StatusList {get;set;}
public StatusesViewModel CreatedStatus {get;set}
}
Then in your view:
#model MyActionViewModel
#Html.EditorFor(x => x.CreatedStatus)
.............................................
#Html.DisplayFor(x => x.StatusList)
Then you can create two templates, an EditorTemplate and a DisplayTempate:
In ~/Views/Shared/EditorTemplates/StatusesViewModel.cshtml
#model StatusesViewModel
#using(Html.BeginForm()) {
#Html.EditorFor(x => x.Message)
<input type="submit" value="Create Status" />
}
In ~/Views/Shared/DisplayTemplates/StatusesViewModel.cshtml
#model StatusesViewModel
<div>
<span>#Model.Message</span>
</div>
The thing that's nice about using the templates is that they will automatically iterate over your collection.. no foreach or for statement is used. A single EditorFor works on the entire collection, then renders the template based on the type, which in this case translates to StatusViewModel.cshtml
Easy way is to put a list inside Viewbag and show list in View as shown :-
Controller :
Public Actionresult Myaction()
{
.........
Viewbag.data = //bind list of messages here
return View();
}
View :
#model LearnStart.Models.Statuses
.........
.........
.........
#if(Viewbag.data != null){
<table>
#foreach(var item in Viewbag.data)
{
<tr><td>#item.message</td></tr>
}
</table>
}

MVC4 - Partial View Model binding during Submit

I have view model which has another child model to render the partial view (below).
public class ExamResultsFormViewModel
{
public PreliminaryInformationViewModel PreliminaryInformation { get; set; }
public string MemberID { get; set; }
public string MemberName { get; set; }
public int PatientID { get; set; }
public string ConfirmationID { get; set; }
public bool IsEditable { get; set; }
#region Select Lists
public SelectList ProviderOptions { get; set; }
#endregion
}
public class PreliminaryInformationViewModel
{
public string ProviderName { get; set; }
public string ProviderID { get; set; }
public string ServiceLocation { get; set; }
}
This PreliminaryInformationViewModel view model also used as a child models in another view model since this preliminary information can be updated at different pages.
So I created this preliminary information as a separate partial and to include in other pages.
#{Html.RenderPartial("_PreliminaryInformation", Model.PreliminaryInformation);}
Inside the partial
#model Web.Models.Preliminary.PreliminaryInformationViewModel
<div>
#Html.TextBoxFor(x => x.DateOfService })
</div>
But the problem is during submit this preliminary model is always null due to the reason HTML name attribute is always is rendered as
but when I pass the parent model to the partial as below.
#model Web.Models.Exam.ExamResultsFormViewModel
<div>
#Html.TextBoxFor(x => x.PreliminaryInformation.DateOfService })
</div>
Now the HTML element is generated as
<input type = 'text' name='PreliminaryInformation.DateOfService.DateOfService' id='PreliminaryInformation.DateOfService'>
and it binds properly during the submit.
I understand MVC bind the element value based on the name attribute value, but the second implementation would need me to create a multiple partial for each page, which I don't like.
So far I couldn't find a solution to work with the first implementation, is there way I can make preliminary information model value bind during submit with the first implementation.
I know its a bit late but it might help to someone
If you have complex model, you can still pass it into partial using:
#Html.Partial("_YourPartialName", Model.Contact, new ViewDataDictionary()
{
TemplateInfo = new TemplateInfo()
{
HtmlFieldPrefix = "Contact"
}
})
where I have defined model with property "Contact". Now what HtmlFieldPrefix do is add the property binding for each model "so the model binder can find the parent model"
There is a blog post about it: http://www.cpodesign.com/blog/bind-partial-view-model-binding-during-submit/
.NET Core 2 binding
In .NET Core 2 and MVC the answer above will not work, the property is no longer settable.
How ever the solution is very similar.
#{ Html.ViewData.TemplateInfo.HtmlFieldPrefix = "Contact"; }
#await Html.PartialAsync("_YourPartialName", Model.Contact)
after you can submit your model, it will bind again.
Hope that helps
You can add the HtmlFieldPrefix to the top of your partial view:
#{
ViewData.TemplateInfo.HtmlFieldPrefix = "Contact";
}
This is the same approach as that described by #cpoDesign but it means you can keep the prefix in your partial view if you need to do that.
You need to create an editor template for PreliminaryInformationViewModel to replace the partial view, then call with Html.EditorFor( m => m.PreliminaryInformation ). Reference this solution. Creating the template should be as simple as moving your partial view to the Views/Shared/EditorTemplates directory. Html.EditorFor(...) will automatically use this template based on the type you're passing in as the model (in this case, PreliminaryInformationViewModel)
I also ran into this problem. I will explain my solution using your code. I started with this:
#{
Html.RenderPartial("_PreliminaryInformation", Model.PreliminaryInformation);
}
The action corresponding to the http post was looking for the parent model. The http post was submitting the form correctly but there was no reference in the child partial to the parent partial. The submitted values from the child partial were ignored and the corresponding child property remained null.
I created an interface, IPreliminaryInfoCapable, which contained a definition for the child type, like so:
public interface IPreliminaryInfoCapable
{
PreliminaryInformationViewModel PreliminaryInformation { get; set; }
}
I made my parent model implement this interface. My partial view then uses the interface at the top as the model:
#model IPreliminaryInfoCapable
Finally, my parent view can use the following code to pass itself to the child partial:
#{
Html.RenderPartial("ChildPartial", Model);
}
Then the child partial can use the child object, like so:
#model IPreliminaryInfoCapable
...
#Html.LabelFor(m => m.PreliminaryInformation.ProviderName)
etc.
All of this properly fills the parent model upon http post to the corresponding action.
Quick Tip: when calling your EditorFor method, you can set the name of the template as a parameter of the Html.EditorFor method. Alternatively, naming conventions can be your friend; just make sure your editor template filename is exactly the same name as the model property type
i.e. model property type 'CustomerViewModel' => 'CustomerViewModel.cshtml' editor template.
Please make the below changes to your partial page. so it will come with your Parent model
//Parent Page
#{Html.RenderPartial("_PreliminaryInformation", Model.PreliminaryInformation);}
//Partial Page
#model Web.Models.Preliminary.PreliminaryInformationViewModel
#using (Html.BeginCollectionItem("PreliminaryInformation", item.RowId, true))
{
<div>
#Html.TextBoxFor(x => x.DateOfService })
</div>
}
For .net core 2 and mvc, use do like below:
#{
Html.ViewData.TemplateInfo.HtmlFieldPrefix = "Contact";
}
#await Html.PartialAsync("_YourPartialViewName", Model.Contact)

Create ViewModel for Navigation

I have an MVC 4 application with several views. I.e. Products, Recipes, Distrubutors & Stores.
Each view is based around a model.
Let's keep it simple and say that all my controllers pass a similar view-model that looks something like my Product action:
public ActionResult Index()
{
return View(db.Ingredients.ToList());
}
Ok so this is fine, no problems. But now that all of my pages work I want to change my navigation (which has dropdowns for each view) to load the items in that model.
So I would have a navigation with 4 Buttons (Products, Recipes, Distrubutors & Stores).
When you roll over each button (let's say we roll over the products button) then a dropdown would have the Products listed.
To do this I need to create some type of ViewModel that has all 4 of those models combined. Obviously I can't just cut out a PartialView for each navigation element and use
#model IEnumerable<GranSabanaUS.Models.Products>
And repeat out the Products for that dropdown, because then that navigation would only work in the Product View and nowhere else.
(After the solution)
AND YES ROWAN You are correct in the type of nav I am creating, see here:
Introduction
I'm going to be making a few assumptions because I don't have all the information.
I suspect you want to create something like this:
Separating views
When you run into the issue of "How do I put everything into a single controller/viewmodel" it's possible that it's doing too much and needs to be divided up.
Don't treat your a final page as one big view - divide the views up into smaller views so they are doing 'one thing'.
For example, the navigation is just one part of your layout. You could go even further to say that each dropdown menu is a single view that are part of the navigation, and so on.
Navigation overview
Suppose you have a _Layout.cshtml that looks like this:
<body>
<div class="navbar">
<ul class="nav">
<li>Products</li>
<li>Recipes</li>
</ul>
</div>
#RenderBody()
</body>
As you can see we have a simple navigation system and then the main body is rendered. The problem that we face is: How do we extract this navigation out and give it the models it needs to render everything?
Extracting the navigation
Let's extract the navigation into it's own view. Grab the navigation HTML and paste it into a new view called __Navigation.cshtml_ and put it under ~/Views/Partials.
_Navigation.cshtml
<div class="navbar">
<ul class="nav">
<li>Products</li>
<li>Recipes</li>
</ul>
</div>
Create a new controller called PartialsController. Create a new action to call our navigation.
PartialsController.cs
[ChildActionOnly]
public class PartialsController : Controller
{
public ActionResult Navigation()
{
return PartialView("_Navigation");
}
}
Update our Layout to call the navigation.
_Layout.cshtml
<body>
#Html.Action("Navigation", "Partials")
#RenderBody()
</body>
Now our navigation is separated out into its own partial view. It's more independent and modular and now it's much easier to give it model data to work with.
Injecting model data
Suppose we have a few models such as the ones you mentioned.
public class Product { //... }
public class Recipe { //... }
Let's create a view-model:
NavigationViewModel.cs
public class NavigationViewModel
{
public IEnumerable<Product> Products { get; set; }
public IEnumerable<Recipe> Recipes { get; set; }
}
Let's fix up our action:
PartialsController.cs
public ActionResult Navigation()
{
NavigationViewModel viewModel;
viewModel = new NavigationViewModel();
viewModel.Products = db.Products;
viewModel.Recipes = db.Recipes;
return PartialView("_Navigation", viewModel);
}
Finally, update our view:
_Navigation.cshtml
#model NavigationViewModel
<div class="navbar">
<ul class="nav">
#foreach (Product product in Model.Products)
{
#<li>product.Name</li>
}
#foreach (Recipe recipe in Model.Recipes)
{
#<li>recipe.Name</li>
}
</ul>
</div>
public class MenuContents
{
public IEnumerable<Products> AllProducts { get; set; }
public IEnumerable<Recepies> AllRecepies { get; set; }
public IEnumerable<Distributors> AllDistributors { get; set; }
public IEnumerable<Stores> AllStores { get; set; }
private XXXDb db = new XXXUSDb();
public void PopulateModel()
{
AllProducts = db.Products.ToList();
AllRecepies = db.Recepies.ToList();
AllDistributors = db.Distributors.ToList();
AllStores = db.Stores.ToList();
}
}
Then in your controller
public ActionResult PartialWhatever()
{
MenuContents model = new MenuContents();
model.PopulateModel();
return PartialView("PartialViewName", model);
}
Then in your partial view
#Model MenuContents
... do whatever here

ASP.NET MVC 4 - EditorTemplate for nested collections

I have the following model classes (classes simplifies for the purpose of this question):
public class Lesson
{
public Guid Id {get;set;}
public string Name {get;set;}
public List<ExerciseForPupil> Exercises {get;set;}
}
public class ExerciseForPupil
{
public Guid Id {get;set;}
public string Name {get;set;}
public List<ExerciseItemForPupil> ExerciseItems {get;set;}
}
public class ExerciseItemForPupil
{
public Guid Id {get;set;}
public string Content {get;set;}
public string UserValue {get;set;}
}
Now, I want users to be able to fille "UserValue" value for each exercise in the lesson.
Let's say there are 5 exercises for the lesson.
I render these exercises as follows
#Html.EditorFor(x=>x.Lesson.Exercises)
Which renders an EditorTemplate which looks as follows:
#model MyNamespace.ExerciseForPupil
#using (Html.BeginForm("ScoreExercise", "SharedLesson", FormMethod.Post))
{
#Html.Hidden("Id", Model.Id)
#if (Model.ExerciseItems != null)
{
foreach (var exerciseItem in Model.ExerciseItems)
{
#Html.EditorFor(x => exerciseItem, "ExerciseItemForPupil")
}
}
<input type="submit" value="Submit"/>
}
I also have EditorTemplate for "ExerciseItemForPupil":
#model MyNamespace.ExerciseItemForPupil
#Html.HiddenFor(model => model.Id)
#Html.TextBoxFor(model => model.UserValue)
Problem:
As can be seen there will be multiple forms on the page. My "ScoreExercise" action is as follows:
public ActionResult ScoreExercise(ExerciseForPupil exercise)
{
//exercise.ExerciseItems is NULL
}
But my nested collection on the second level (ExerciseItems) is null.
What am I doing wrong?
UPDATE
I've changed the code according to #MysterMan advices:
I call editor template for Exercises as follows:
#Html.EditorFor(x => x.Lesson.Exercises)
and inside this EditorTemplate I call Editor Template for my ExerciseItems
#Html.EditorFor(x=>x.ExerciseItems)
this renders the following markup for UserValue property:
<input id="Lesson_Exercises_0__ExerciseItems_1__UserValue" name="Lesson.Exercises[0].ExerciseItems[1].UserValue" type="text" value="">
but it does not work also
Don't use the foreach. EditorTemplates already iterate over collections if you pass it a collection.
#model MyNamespace.ExerciseForPupil
#using (Html.BeginForm("ScoreExercise", "SharedLesson"))
{
#Html.HiddenFor(x => x.Id)
#Html.EditorFor(x => x.ExerciseItemsForPupil)
<input type="submit" value="Submit"/>
}
A few things to note. You don't have to pass the template name, as the template name is already the same name as the item type. You don't have to use the Post formmethod, as that's the default. There is no name of the property for List so I just assumed it was the plural.
Your last class is also illegal, you would not specify it as a List like that.