Code regarding EditorTemplates usage ASP.Net MVC - asp.net-mvc

i was reading a post on EditorTemplates
from this url http://stackoverflow.com/questions/4872192/checkboxlist-in-mvc3-0
after seeing their code i just do not understand area like how it would work
view model:
public class MyViewModel
{
public int Id { get; set; }
public bool IsChecked { get; set; }
}
A controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new[]
{
new MyViewModel { Id = 1, IsChecked = false },
new MyViewModel { Id = 2, IsChecked = true },
new MyViewModel { Id = 3, IsChecked = false },
};
return View(model);
}
[HttpPost]
public ActionResult Index(IEnumerable<MyViewModel> model)
{
// TODO: Handle the user selection here
...
}
}
A View
(
~/Views/Home/Index.cshtml
):
#model IEnumerable<AppName.Models.MyViewModel>
#{
ViewBag.Title = "Home Page";
}
#using (Html.BeginForm())
{
#Html.EditorForModel()
<input type="submit" value="OK" />
}
and the corresponding Editor template
(
~/Views/Home/EditorTemplates/MyViewModel.cshtml
):
#model AppName.Models.MyViewModel
#Html.HiddenFor(x => x.Id)
#Html.CheckBoxFor(x => x.IsChecked)
see this code
#using (Html.BeginForm())
{
#Html.EditorForModel()
<input type="submit" value="OK" />
}
1) what this line will do
#Html.EditorForModel() ?
2) if this line would load a view called
MyViewModel from this location
/Views/Home/EditorTemplates/MyViewModel.cshtml
3) then how mvc engine would understand that it has to load view called
MyViewModel from this location /Views/Home/EditorTemplates/
4) i saw people always create
EditorTemplates
folder in shared view but in this case it is created in home folder.....why ?
5) if there are so many other view in this location then how this line
#Html.EditorForModel()
would load this specific view
MyViewModel.cshtml
from this location
/Views/Home/EditorTemplates.
i am new in mvc and learning. so please help me to understand how the above code will work?
also please answer my 5 questions. thanks

Before answer to your specific question, you have to know that asp .net mvc relay heavily on Convention over Configuration.
1) what this line will do
#Html.EditorForModel() ?
It just tell the view to render the model pass in as a whole thing to the EditorFor.
2) if this line would load a view called
MyViewModel from this location
/Views/Home/EditorTemplates/MyViewModel.cshtml 3) then how mvc engine
would understand that it has to load view called
MyViewModel from this location /Views/Home/EditorTemplates/
Mvc knows it by convention. It will look into Views for a template same name to the viewModel type(in this case MyViewModel )
the pattern mvc look at is:
Views{Controller}\EditorTemplates\MyViewModel.cshtml
Views\Shared\EditorTemplates\MyViewModel.cshtml
And if it find it, it will stop looking. Hence the view in Controller will be used even if there is one in the shared.
4) i saw people always create
EditorTemplates folder in shared view but in this case it is created
in home folder.....why ?
If it is in Shared, that means any other controller with same ViweModel type name MyViewModel can use that same view. If it is in home, that means it is only available to "Home" controller specific.
5) if there are so many other view in this location then how this line
#Html.EditorForModel() would load this specific view
MyViewModel.cshtml from this location
/Views/Home/EditorTemplates. i am new in mvc and learning. so please
help me to understand how the above code will work?
That is the convention, as I answered above, there is certain pattern which mvc looks view in, the first match get applied.
Edit
Thank Stephen Muecke for pointing out, I was typing too fast.
The search Pattern for view is:
Views{Controller}\EditorTemplates\MyViewModel.cshtml
Views\Shared\EditorTemplates\MyViewModel.cshtml
So if it find it, it will stop looking. This means if it found in the current controller (in your example is Home), then it stop looking. It only continue to look when it can not find it in the controller specific folder.
Edit 2 - include some reason to use Editor template
the reason for writting editor/display template is for code reuse.
Consider using jquery datepicker on a datetime property.
If you don't use Editor template, then you are going to duplicate code in the view to use jquery datepicker.
If you use editor template, then if some day you want to change jquery datepicker to some other plugin, you only change in the editor template, and no where else. Hence Don't Repate Yourself (DRY) principal.
This also keep same consistency of ui across multiple page when showing same type of input.
The example I gave above is on one property, but if a template is for whole model, that is where EditorForModel comes in, but same idea.
Consider this article on some usage of template
https://www.simple-talk.com/dotnet/asp.net/extending-editor-templates-for-asp.net-mvc/
And below is a more depth detail on template written for asp .net mvc 2. But same thing apply to mvc 4 and 5
http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-1-introduction.html

Related

ASP.NET MVC: Adding partial views dynamically and having them reflected in the view model

Situation: I have an ASP.NET Razor page that is constructed of partials.
Some of these are in loops iterating over collections that are members of the main page's view model. These iterated partials use the collections' member types as their own view model.
So the main page Razor looks something like this:
#model MyProject.MainViewModel
#*Other stuff...*#
#foreach (Subtype v in Model.SubViewModel)
{
Html.RenderPartial("MyPartial", v);
}
And the partial looks like this:
#model Subtype
#Html.HiddenFor(t=>t.ParentViewModelID)
#Html.EditorFor(t=>t.Field1)
#Html.EditorFor(t=>t.Field2)
This all works well on initial page load, but I'm wondering how I should handle dynamically adding these partials via AJAX.
Suppose the user presses a button that's ultimately intended to add to one of these view model collections. I can AJAX back to the server, render the appropriate partial there, and return it to the client:
[System.Web.Mvc.HttpGet]
public ActionResult AddSubtype(int mainViewModelId)
{
var model = new Sub { ParentViewModelID=mainViewModelId};
return PartialView("MyPartial", model);
}
And then I use a bit of script to stick this into the DOM where it belongs:
success: (data) => { $('#subtypeHanger').append(data);},
This seems to work, except for one big part: when I evntually submit my form, I'm not seeing the data associated with this dynamically added partial in my view model.
I have been trying to debug this, but I'm wondering in broad terms whether this is possible- or alternately, how much MVC is supposed to abstract over such things. Obviously I can do things with JavaScript to force this to work. Is there an ASP.NET MVC pattern for this I should emulate, though?
EDIT: I'll mention some things I've tried since I posted this:
Managing the "name" and "id" attributes of the partial views' edit controls manually, so that they end up looking like they do when I nest these controls in the main view instead of a partial (i.e. longer, with more text and an index).
Using the main view model class for the partials. This makes me move the loops into the partial. The "name" and "id" property look like what I was going for in #1 above, but this design greatly complicates the process of returning the partial from my AJAX calls.
Using "for" loops instead of "foreach." By itself, this doesn't achieve what I want.
All this said, if #1 or #2 is the best pattern for what I'm doing, perhaps I can make that work.
"Situation: I have an ASP.NET Razor page that is constructed of partials."
This is called "Master Details"
check
Master Details CRUD with MVC 5 | The ASP.NET Forums
The trick is naming your Fields
#Html.HiddenFor(t=>t.ParentViewModelID ,new { htmlAttributes = new { #Name = "[" + i + "].ParentViewModelID" } })
#Html.EditorFor(t=>t.Field1 ,new { htmlAttributes = new { #Name = "[" + i + "].Field1" } })
#Html.EditorFor(t=>t.Field2 , new { htmlAttributes = new { #Name = "[" + i + "].Field2" } })
so it will be
<input type="text" name="[0].Field1" />
<input type="text" name="[0].Field2" />
<input type="text" name="[1].Field1" />
<input type="text" name="[1].Field2" />
and better(for ASP.NET mvc 5 Model Binding) if you use
public ActionResult Edit(MainViewModel master, List<Subtype> details)
if you provided the Full model i will provide a full working example

Cannot populate dropdownlist in ASP.NET MVC 5 from Entity Framework

I'm trying to populate a dropdown list in ASP.NET MVC 5, but with no success.
Controller:
[AllowAnonymous]
[HttpGet]
public ActionResult PartialRegistrationRoles()
{
using (DBModels db = new DBModels())
{
List<Role> allRoles = db.Roles.ToList();
var roles = new SelectList(db.Roles.ToList(),"RoleName");
ViewData["DBRoles"] = roles.Items;
}
return View();
}
Partial view:
#using System.Collections
#using WorkFinder.Models
#model WorkFinder.Models.Role
<div class="form-group">
#Html.DropDownList("Roles", new SelectList((IEnumerable)
ViewData["DBRoles"]),"Roleid","RoleName")
</div>
Actual dropdown:
The drop down is just retrieving some SYSTEM.DATA.ENTITY...
Error when browsing the main view, however, the dropdown shows in partial view:
Error in main view
Can anyone please help me understand what I'm doing wrong?
Thanks!
First, take a look at the overload methods for DropDownList and SelectList. Most likely you misplaced a bracket and intended to do something like:
#Html.DropDownList("Roles", new SelectList((IEnumerable)ViewData["DBRoles"],"Roleid","RoleName"))
In terms of calling this as a partial view, please show the relevant code (the main view) and I will update the answer.

How do I pass a model to a partial view and load a specific record?

I am still relatively new to MVC and am finding every new concept to be a struggle, so please forgive me if this is an overly simple concept or the question has been asked many times before (I tried to find other examples).
I have several modals that can be called from my shared layout using jQuery's "dialog." Each modal is simply a DIV with a partial view attached to it like this:
<div id="JoinDialog" title="Join the Contractor Network" style="display: none;">
#Html.Partial("_JoinPartial")
</div>
And is called like this:
$(".ClickToJoin").click(function () {
$(function () {
$("#JoinDialog").dialog({ width: "auto", height: "auto"});
});
});
I have added a "Profile" modal to the layout in which I would like to insert the user's data into the INPUT values. To do that, I presume that I will need to pass in a model, and load the data I want via the controller. Since I currently have this partial view in the "Shared" folder, I assume I will also need to move it to one of my view folders where I can attach it to a controller?
Any nudge in the right direction would be appreciated.
Since I currently have this partial view in the "Shared" folder, I
assume I will also need to move it to one of my view folders where I
can attach it to a controller?
No there is no need for you to move the partial view to the controller folder. You can use the partial view from the shared folder itself (View Engine also looks at Shared folder to find a matching view). Here goes the sample example -
Lets say you have a model like this -
public class MyModel
{
public string Name { get; set; }
}
And then you have an action to return the partial view from the shared folder -
public ActionResult GetPartial()
{
MyModel model = new MyModel();
model.Name = "Rami";
return PartialView("TestPartial", model);
}
Then have the partial view in the Shared folder like this -
#model YouModelNamespace.MyModel
<div>#Model.Name</div>
Then on the actual page, you can have following code -
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
#{
Html.RenderAction("GetPartial");
}
That will display the result from the partial view on the page as shown in below screenshot.
When you have to render a View (or a Partial View), asp.net mvc has some default orders to find it. First asp.net mvc will search the respective views' folder of the controller you are executing and if it was not found, asp.net mvc search on the Shared folder. So, if you have a view called _JoinPartial on the Views/Product (for sample) folder and shared folder, it will priorize the View folder. Sometimes you get a exception that views was not found, in there message you can see all places where asp.net mvc find it, for sample:
In your case, the controller could return a Partial View
public ActionResult GetJoinPartial()
{
return PartialView("_JoinPartial");
}
Since you have on the View folder, it will use it, instead it will use the partialView on the Shared folder.

Model with List - approaches to add new item to the list from a Razor view

I have a model with various properties but the one of interest is a List of another type of Model.
For example:
public class User
{
public string Name { get; set; }
public string Description { get; set; }
public IEnumerable<UserInterest> Interests { get; set; }
}
I then use an Editor Template within my view to render out a view for each item of the model items.
#Html.EditorFor(x => x.Interests)
The EditorFor template looks something like:
#model Interest
<div>
#Html.HiddenFor(x => x.Id)
#Html.TextBoxFor(x => x.InterestText)
#Html.CheckBoxFor(x => x.Delete)
....
</div>
Something very similar to the accepted answer here: Model Containing List of Models (MVC-3, Razor)
My question is - how would you from the client-side (jQuery) create a new item within the property without going back to the server. I currently have a rough way of doing it whereby I post the data back to my controller which returns the model back with a new blank item within the Interests property.
This seems to be overkill making a HTTP request and not very elegent. I was thinking of using jQuery .Clone() but not entirely sure on what I'd need to do in terms of naming the elements and clearing existing values.
So does anybody have any suggestions. I'm hoping to get more opinions and different approaches.
You can simply create the Textbox and checkbox on the fly and add that to the DOM. When saving it, Use jQuery ajax to post that data ( new record data) to an action method and save it there. Return a status back (Succcess /Falied) from your action method to your client side code ( your callback function of jQuery ajax/post) and check it there. If it is success, Show a success message to the user and append the new item to the existing list.
Sample jSFiddle : http://jsfiddle.net/carwB/2/
If you want to return some complex data ( ex : All new records with its id etc..) along with the status, you may return JSON from your action method.
EDIT : To keep your Model binding works with the newly added dynamic elements, you need to follow the naming convention of the elements.
The trick is to keep the id property value of the html element in this format.
CollectionName_ItemIndex__PropertyName
and name property value in this format
CollectionName[ItemIndex].PropertyName
I created a sample working program and explained it how it works Here based on your requirements.
In such situations I prefer to use client templating. You send data to server with ajax and then receive JsonResult. Look at JsRender this is javascript lib without jQuery dependency.
1.Create two partial view one is for list item and second one is creation
2.First partail view should be inside the div which has id 'divMdeolList'
3.and Creation view will have the code like that
#using (Ajax.BeginForm("SubmitData", new AjaxOptions { UpdateTargetId = "divMdeolList" }))
{
#Html.TextBoxFor(x => x.InterestText)
<p>
<input type="submit" value="Create" />
</p>
}
4. And then create a ActionResult type action on controller that will render the partialview
public ActionResult SubmitData(YourModel model)
{
//Do : save the record
return PartialView("FirstPartailView", model);
}
This will update the View without postback

MVC: Binding dynamic select list back to ViewModel on Post

I have a MVC 3 project, and is stuck on the binding of the view back to the model on Post.
Here is my Model:
public class DBViewModel
{
public ProductionDatabasesViewModel PDBViewModel { get; set; }
public IList<string> SelectedNames { get; set; }
public DBViewModel()
{
SelectedNames = new List<string>();
PDBViewModel = new ProductionDatabasesViewModel();
}
}
My view: (cut down version)
#model Web.Models.DBViewModel
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
#Html.ListBoxFor(x => x.SelectedNames, new SelectList(Model.SelectedNames))
<input id="button-submit" name="SubmitButton" type="submit" value="Submit" />
</fieldset>
}
Snippet of the Controller:
[HttpPost]
public ActionResult Database(DBViewModel model)
{
var users = model.SelectedNames; //<---- empty, the count is 0 always.
}
In the view there is the ability to select users from a autocomplete box and then jQuery script that will add the selected names to the list in the view above. This all works, so I can search for the users, and then click the add button to add the users to the list. But I run into problems when clicking the Submit button. The users that were added in the view (selectlist in the view), is not bound to the Model. So on post the model.SelectedNames is empty. I have also tried Request["SelectedNames"] and Request.Form["SelectedNames"] and they are all null.
I am very experienced in normal webforms, but still learning MVC, so any help would be much appreciated.
[UPDATE]
I will update the question further tomorrow my time, but it appears that the items that is in the list will be bound to the viewmodel if I select them. I will figure this out tomorrow. But thanks a lot so far for all the comments, help and suggestions.
Does this Does the Model Binder in ASP.NET MVC Beta Support List<T>? answer your question? The string list is a simpler version.
Have to say this cut off version of your code works as it should. SelectedNames list is successfully filled from listbox after submit. You can check it yourself. Though I have made several changes:
removed all references to ProductionDatabasesViewModel;
changed return type of POST version of Database method to void;
added GET version of Database method
:
public ActionResult Database()
{
return View(new DBViewModel { SelectedNames = { "a", "b", "c" } });
}
So I believe you have a bug somethere else, not in these parts.
Ok, I have found what the issue is. When you have a multi select element in your html that you want to bind the values in the box back to the model, you have to select the items first. Very annoying, but that's the way it seems to want it.
So if you have a select box like:
<select id="selected-ad-users-list" name="SelectedNames" multiple="multiple" size="5"
style="width: 100%">
</select>
And you add items to it, from anywhere really, in my case I had another list/or textbox where they typed a name in, and then I used jQuery to populate this select list. What I needed to do was to add a bit of jQuery script to spin through my list and select all items. So once the items are selected, it will bind them to the model.
Here is a good article on this: select all or some items in a select box
I really hope this helps someone.

Resources