UIHint does not work with IList? - asp.net-mvc-3

I have a property in my ViewMode:
[UIHint("FileUpload")]
public IList<string> Images { get; set; }
In view Create.cshtml
#html.ValidationSummary(true)
#html.EditorForModel()
In the folder Shared/EditorTemplates/FileUpload.cshtml
<h3>Test</h3>
But the field is not displayed. Simply, nothing happens!
I did the same test with another type of field and it worked:
[UIHint("FileUpload")]
public string Test { get; set; }
What could be wrong?
How do you solve this problem?
If I manually add the code below in my Create.cshtml view, it works!
#Html.EditorFor(m => m.Images)
I do not know what to do.

Yes, UIHint doesn't work with lists. You will need a loop inside the corresponding editor template (~/Views/Shared/EditorTemplates/FileUpload.cshtml). The UIHint template is passed the model which in this case is a IList<string>.
#model IList<string>
#foreach (var item in Model)
{
...
}

Related

Razor view dropdown list for a model class in MVC3

I have two model class in MVC3 one for Services which have those properties
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Image { get; set; }
public int ChildOf { get; set; }
It also have a DB table by Entityframework
Another model is Quata which have those properties
public int ID { get; set; }
public string Sender_Name { get; set; }
public string Description { get; set; }
.....
......
public Services Service_ID { get; set; }
It also have a DB table by Entityframework
I want to create a Razor(C#) view (for Quata) where user can send a quata by fill a html form but where i wanna show a dropdown list with Services ID as dropdown value and Services Name as dropdown text which is also come dynamically from the Services DB table .
My question is how i should create that dynamic dropdown list by #Html.DropDownListFor ? and send the selected data from that dropdown list to a Controller ?
Try this
Controller:
public ActionResult Create()
{
var Services = new Services();
Services.Load(); //load services..
ViewBag.ID = new SelectList(Services.ToList(), "Id", "Name");
return View();
}
[HttpPost]
public ActionResult Create(Quata Quata)
{
//save the data
}
A strong Typed View: (Using Razor)
#model Quata
#using (Html.BeginForm()) {
<fieldset>
<legend>Quata</legend>
<div>
#Html.LabelFor(model => model.Service_ID.ID, "Service")
</div>
<div>
#Html.DropDownList("ID", String.Empty)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
take a look at #Html.DropDownListFor
So say your viewmodel has a list of said Services.
Something that may work for you is the following (you may not need a for loop here, editor is supposed to eliminate that, but I had some weird binding issues).
In your top level view which points at your viewmodel (#model Quata, assuming Quata is your viewmodel) have this code :
#For i = 0 To Model.DropdownListInput.Count - 1
Dim iterator = i
#Html.EditorFor(Function(x) x.DropdownListInput(iterator), "EnumInput")
Next
In your Editor Template (create a subfolder under the view folder this dropdownlist will be in called editor templates and name the template whatever you desire, mine was EnumInput).
In your editor template, which should point at your model for Services (#model Services) have something like the following code (with substitutions for your appropriate variable names):
#<div class="editor-label">
#Html.LabelFor(Function(v) v.value, Model.DisplayName)
</div>
#<div class="editor-field">
#Html.DropDownListFor(Function(v) v.value, New SelectList(Model.ParamEnums, "ValueForScript", "EnumValue"), "--Please Select A Value--")
#Html.ValidationMessageFor(Function(v) v.value)
</div>
Replace the list with your list and the lambda values with yours (#Html.DropDownListFor(x => x.id, New SelectList(x.ServiceList, "ID", "Name"), "--Please Select A Value--") or something like that.
Note that this code is in VB, but it should provide a rough guide.

Inconvenient render partial view MVC 3

I have a partial view of work with a viewmodel, but I have a inconvenience when rendering a partial view in the main view
I get the following message:
The model item passed into the dictionary is of type ‘RolMVC3.Models.USER’ but this dictionary requires a model item of type ‘RolMVC3.Areas. Distributor.Models. LocationViewModel ‘
View Model(LocationViewModel), the ViewModel is within an Area
namespace RolMVC3.Areas.Distributor.Models
{
public class LocationViewModel
{
[Required]
public decimal IdDepartment { get; set; }
[Required]
public string NameCity { get; set; }
[Required]
public string NameNeighborhood { get; set; }
}
}
}
Partial View(_LocationEdit):
#model RolMVC3.Areas.Distributor.Models.LocationViewModel
.....
.....
<div class="editor-label">
#Html.LabelFor(model => model.IdDepartment)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.IdDepartment, new SelectList(ViewBag.Department as System.Collections.IEnumerable, "IdDepartment", "NameDepartment", ViewBag.selectedDepartment), "--- Select ---", new { id = "Department"})
#Html.ValidationMessageFor(model => model.Idepartment)
</div>
.....
.....
Main View(Edit):
#model RolMVC3.Models.USER
#{
ViewBag.Title = "Edit User";
}
....
....
#{Html.RenderPartial("_LocationEdit");}
....
....
How I can resolve this?
Blessings
In your Main View(Edit) pass the LocationViewModel , because partial view requires this model and you are not passing it in #Html.RenderPartial
#model RolMVC3.Models.USER
#{
ViewBag.Title = "Edit User";
}
....
....
#{Html.RenderPartial("_LocationEdit",YourLocationViewMODEL);}
....
....
When you call your RenderPartial you need to pass in a model type of LocationViewModel. It doesn't look like you have this in your edit view right now, so you need to either a) add it to the viewbag from your controller and pass it via your RenderPartial call or b) change the model type in your edit view (you may need a wrapper that holds both the user and locationviewmodel information)
[Edit]
The source code would looks something like the following:
// ViewModel
namespace RolMVC3.Areas.Distributor.Models
{
public class EditPageViewModel
{
public LocationViewModel LocationViewModel {get;set;]
public USER User { get; set; }
}
}
// Edit View
#model RolMVC3.Areas.Distributor.Models.EditPageViewModel
#{
ViewBag.Title = "Edit User";
}
....
....
#Html.RenderPartial("_LocationEdit",Model.LocationViewModel);
....
....

Editor Template Not working in MVC3

I am trying out the Editor Template in MVC 3
My model class is
public class BookViewModel
{
public int Id { get; set; }
public string Name { get; set; }
[DataType(DataType.Text)]
public string Author { get; set; }
}
I have create a partial view for Editor template and put that in a EditorTemplates folder with name Text.cshtml. following is the partial view
#inherits System.Web.Mvc.WebViewPage<string>
<p> Write the name of author</p> #Html.TextBox(Model)
and I used #Html.EditorFor in the view page
<p> Name : #Html.EditorFor(model => model.Name)</p>
<p> Author</p> #Html.EditorFor(model => model.Author)
But when I run the program what I see is only an empty TextBox. I should see a TextBox filled with Author Name right?
What am I missing here?
Your editor template should be:
#model String
<p> Write the name of author</p> #Html.TextBox("name of the textbox", Model)
The first parameter of the #Html.TextBox() helper can be an empty string as well, but its not recommended

Losing data in models and collections inside the ViewModel on postback

I am using a viewmodel that contains a few other models(entities) for each partial view.
I am rendering a partial view by passing the entity which is inside the ViewModel. My partial view has a few fields and some buttons. On click of button (which is inside my partial view) the form is being posted back with the data in a sub entity, whereas my viewmodel is always posted back as null...
I need the data to be present in my viewmodel on post back.
All views are strongly typed:
Code:
public class OrdersVM
{
public FiltersVM filterCriteria { get; set; }
public IEnumerable<MeterInventory> meters { get; set; }
public string assignTo { get; set; }
public IEnumerable<SelectListItem> AssigneeOptions { get; set; }
}
public partial class Meters
{
public int MTRNO { get; set; }
public string LOCName { get; set; }
}
public class FiltersVM
{
public string Center { get; set; }
public DateTime? DueDate { get; set; }
}
View Code
#model OrdersVM
#{
ViewBag.Title = "Orders";
}
#using (Html.BeginForm())
{
<div>
#Html.Partial("~/Views/Base/Filters.cshtml", Model.filterCriteria)
</div>
#foreach (var item in Model.meters)
{
<table>
<tr>
<td>
#Html.Encode(item.LOCNAME)
</td>
</tr>
</table>
}
}
Controller code
[HttpPost]
public ActionResult Index(OrdersVM orders, FiltersVM filters)
{
//orders is null
//filters has values
}
Thanks Olivehour. I am using the partial view "Filters.cshtml". and am rendering the same.
Below is the code for partial view :
#model ViewModels.FiltersVM <fieldset>
<legend>Order Assignment</legend>
<table id="tbl1" class="tableforcontrols">
<tr>
<td>
<div class="editor-label">
#Html.LabelFor(model => model.LDC)
</div>
</td>
<td>
<div class="editor-field">
<input type="submit" value="Search" id="btnSearch" name="button" />
</div>
</td>
<td>
<div class="editor-field">
<input type="submit" class="cancel" value="Reset" id="btnReset" name="button" />
</div>
</td>
</tr>
</table> </fieldset>
I tried with single argument "OrdersVM" (parent view model) but no luck.
[HttpPost]
public ActionResult Index(OrdersVM orders)
but if I pass the parent viewmodel to the partial view it was holding the data in OrdersVM.filterCriteria but not for properties (IEnumerable meters, string assignTo and Enumerable AssigneeOptions)
#Html.Partial("~/Views/Base/Filters.cshtml", Model)
I am new to MVC. Please let me know if any one finds the solution.
Thanks in advance.
It looks like you have a couple of problems here. One probable reason why the orders arg is null in your action method is because it doesn't look like you are rendering any input elements. You just have #Html.Encode(item.LOCNAME).
In order for the default model binder to construct an instance of OrdersVM and pass it to the action method, it needs to have input from the HTTP POST. You need something more like #Html.TextBoxFor(m => item.LOCNAME).
The second problem I think is that you have 2 arguments in the action method. Since the OrdersVM already has a FiltersVM property, you should just be able to have a single OrdersVM argument to the action method. During the HTTP POST, you can just access FiltersVM properties from OrdersVM.filterCriteria. This will lead to your 3rd challenge, though, since the meters property on OrdersVM is an IEnumerable collection.
To solve this, first have a couple reads of this article about model binding collections. It's old, but it still applies in MVC3. Read it and really wrap your head around it.
If you don't like using integers to index your collection fields, there is an HTML helper written by Steve Sanderson that allows you to index collection inputs using GUID's. We use this all the time, but it can be tricky -- mainly, you should always put the collection item in a partial view. For now, you might just be better off using integer-based indexing as outlined in the Haacked article.
It sounds like you are comming from Webforms. To transition to MVC you need to remove the thought of PostBack. This is concept that doesn't really exist on the web but Webforms faked it for us.
In MVC you usually start with a GET request like /edit/{someId}. From here you load the data for the viewmodel from the database and render the view. Now let's say that all data in the viewmodel is editable so each property have it's own input field. The user edits some data and saves the form. This issues a POST to the server.
Assume we have this POST method
[HttpPost]
public ActionResult Edit(MyViewModel model)
In this case you have all the data you need modelbinded because all data existed in the form.
You could do this and get the same view rendered because all data was databinded.
[HttpPost]
public ActionResult Edit(MyViewModel model){
return View(model);
}
Now let's pretend you have a dropdown in your form. Then you would have these two properties in your viewmodel.
public int CarId { get; set; }
public IEnumerable<SelectListItem> CarOptions {get; set; }
When you post the form this time the CarId will be populated in the ViewModel but not CarOptions because they are not a part of the form data. What you do if you would want to return the same view again is to reload the missing parts.
[HttpPost]
public ActionResult Edit(MyViewModel model){
model.CarOptions = LoadCarOptions();
return View(model);
}
It's certainly possible to modelbind that too if you put it in a hidden field. But it's easier and probably more effective to reload it from server/database again. This is the normal approach taken when working with MVC.

Nested Display Templates In Razor

I'm currently getting to grips with ASP.Net MVC 3 and the Razor templating engine, but I've come across an issue that I can't quite get to grips with - so over to the StackOverflow community to help!
Let's say that I have a View Model hierarchy that looks like this:
public class Foo {
public string FooTitle { get; set; }
[UIHint("BarList")]
public IList<Bar> BarList { get; set; }
}
public class Bar {
public string BarTitle { get; set; }
}
all fairly simple I'm sure you'll agree. To go with this View Model I have the following:
~/Views/Home/Index.cshtml
#model Foo
<h1>#Model.FooTitle</h1>
#Html.DisplayFor(m => m.BarList)
~/Views/Home/DisplayTemplates/BarList.cshtml
#model IEnumerable<Bar>
<div class="bar-list">
#Html.DisplayForModel()
</div>
~/Views/Home/DisplayTemplates/Bar.cshtml
#model Bar
<p>#Model.BarTitle</p>
I would expect to find the contents of Bar.cshtml to be displayed when I execute my View, but the rendering doesn't seem to nest further that BarList.cshtml
What am I doing wrong here?
You don't need the intermediary BarList.cshtml template nor UIHint if you follow the conventions:
public class Foo {
public string FooTitle { get; set; }
public IList<Bar> BarList { get; set; }
}
public class Bar {
public string BarTitle { get; set; }
}
View (~/Views/Home/Index.cshtml):
#model Foo
<h1>#Model.FooTitle</h1>
<div class="bar-list">
#Html.DisplayFor(m => m.BarList)
</div>
Display template (~/Views/Home/DisplayTemplates/Bar.cshtml):
#model Bar
<p>#Model.BarTitle</p>
and the Bar.cshtml template will automatically be rendered for each element of the BarList collection.
I doubt you still have this issue but this is the correct solution
~/Views/Home/DisplayTemplates/BarList.cshtml
#model IEnumerable<Bar>
<div class="bar-list">
#foreach (var bar in Model) {
#Html.DisplayFor(c => bar)
}
</div>
Move all these Display Templates to:
~/Views/Shared/DisplayTemplates/
Edit: What about iterating through each bar?
#model IEnumerable<Bar>
<div class="bar-list">
#foreach (var bar in Model) {
#Html.DisplayForModel(bar)
}
</div>

Resources