ASP.NET MVC - Nested Editor Templates Not Working - asp.net-mvc

I have two editor templates:
UploadFiles.cshtml:
#model HttpPostedFileBase[]
#Html.EditorFor(m => Model, "UploadFile", new { multiple = true })
UploadFile.cshtml:
#model HttpPostedFileBase
#Html.TextBox("", Model, new { type = "file", multiple = Convert.ToBoolean(ViewBag.Multiple) ? "multiple" : "" })
<!-- Additional code here i don't wish to repeat in both controls -->
Notice how UploadFiles.cshtml template accepts an array and then calls the UploadFile.cshtml template and passes in multiple = true via the view data.
The problem i have is if i say:
#Html.EditorFor(m => Model.Files, "UploadFiles")
It doesn't render anything.
However if i say:
#Html.EditorFor(m => Model.File, "UploadFile")
It renders correctly.
I'd appreciate if someone could show me how this can be achieved.
Thanks

In UploadFiles.cshtml, you're passing Model, which is an array, to UploadFile.cshtml, which does not take an array. Did you mean to wrap that line in a foreach?
foreach (var file in Model)
{
#Html.EditorFor(x => file, "UploadFile", new { multiple = true })
}

Related

Asp.Net Razor View Passing Expression To Partial

I find myself writing this a whole lot in my views:
<div class="form-group">
#Html.LabelFor(x => x.City)
#Html.EditorFor(x => x.City)
#Html.ValidationMessageFor(x => x.City)
</div>
I'd really like to put this in a Partial _Field.cshtml, something like this:
#model //what model type???
<div class="form-group">
#Html.LabelFor(Model)
#Html.EditorFor(Model)
#Html.ValidationMessageFor(Model)
</div>
That could then be called by:
#Html.Partial("_Field", x => x.City)
What would the #model type in my partial be if I wanted to accomplish something like this?
UPDATE This works, but I'd rather use a partial for ease of changing the template:
public static MvcHtmlString Field<TModel, TItem>(this HtmlHelper<TModel> html, Expression<Func<TModel, TItem>> expr)
{
var h = "<div class='form-group'>";
h += $"{html.LabelFor(expr)}{html.EditorFor(expr)}{html.ValidationMessageFor(expr)}";
h += "</div>";
return MvcHtmlString.Create(h);
}
That's not possible. However, what you want is very similar to editor templates. Essentially, you just create a view in Views/Shared/EditorTemplates named after one of the following conventions:
A system or custom type (String.cshtml, Int32.cshtml, MyAwesomeClass.cshtml, etc.)
One of the members of the DataType enum (EmailAddress.cshtml, Html.cshtml, PhoneNumber.cshtml, etc.). You would then apply the appropriate DataType attributes to your properties:
[DataType(DataType.EmailAdress)]
public string Email { get; set; }
Any thing you want, in conjunction with the UIHint attribute:
[UIHint("Foo")]
public string Foo { get; set; }
Which would then correspond to a Foo.cshtml editor template
In your views, then, you simply use Html.EditorFor:
#Html.EditorFor(x => x.City)
Then, for example, you could have Views/Shared/EditorTemplates/String.cshtml as:
<div class="form-group">
#Html.Label("", new { #class = "control-label" })
#Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue, new { #class = "form-control" })
#Html.ValidationMessage("")
</div>
(The empty quotes are placeholders. Razor will automatically fill in the appropriate property name, thankfully.)
Calling EditorFor, then, will print all of this, rather than just the default text input. You can take this much further, as well. I have some articles on my blog that goes into greater detail, if you're interested.
UPDATE
It's worth mentioning a few features of EditorFor:
You can pass a template directly to the call, meaning you can customize what template is used on the fly and per instance:
#Html.EditorFor(x => x.City, "MyCustomEditorTemplate")
You can pass additionalViewData. The members of this anonymous object are added to the ViewData dynamic dictionary. Potentially, you could use this to branch within your editor template to cover additional scenarios. For example:
#Html.EditorFor(x => x.City, new { formGroup = false })
Then in your editor template:
#{ var formGroup = ViewData["formGroup"] as bool? ?? true; }
#if (formGroup)
{
<!-- Bootstrap form group -->
}
else
{
<!-- Just the input -->
}

Post a form with multiple partial views

I'm currently trying to post a form composed of two strongly typed views. This question is similar but it doesn't have an answer:
MVC 3 Razor Form Post w/ Multiple Strongly Typed Partial Views Not Binding
When I submit form the model submitted to the controller is always null. I've spent a couple of hours trying to get this to work. This seems like it should be simple. Am I missing something here? I don't need to do ajax just need to be able to post to the controller and render a new page.
Thanks
Here's my view code:
<div>
#using (Html.BeginForm("TransactionReport", "Reports", FormMethod.Post, new {id="report_request"}))
{
ViewContext.FormContext.ValidationSummaryId = "valSumId";
#Html.ValidationSummary(false, "Please fix these error(s) and try again.", new Dictionary<string, object> { { "id", "valSumId" } });
#Html.Partial("_ReportOptions", Model.ReportOptions);
#Html.Partial("_TransactionSearchFields", new ViewDataDictionary(viewData) { Model = Model.SearchCriteria });
}
Here's the code in the controller:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult TransactionReport(TransactionReportRequest reportRequest)
{
var reportInfo = new List<TransactionReportItem>();
if (ModelState.IsValid)
{
var reportData = _reportDataService.GetReportData(Search.MapToDomainSearchCriteria(reportRequest.SearchCriteria));
if (reportData!=null)
{
reportInfo = reportData.ToList();
}
return View(reportInfo);
}
return View(reportInfo);
}
The partial views themselves are pretty irrelevant since all they are doing is biding and displaying their models.
Partials are not the way to go here. You are looking for EditorTemplates, these are made for what you want. This case, your properties will be nicely bound to your model (that you will submit).
Your main View will have this form (note that you only have to use EditorFor instead of Partial; in this case, you probably will need to put that viewData parameter in the ViewBag or so):
#using (Html.BeginForm("TransactionReport", "Reports", FormMethod.Post, new {id="report_request"}))
{
ViewContext.FormContext.ValidationSummaryId = "valSumId";
#Html.ValidationSummary(false, "Please fix these error(s) and try again.", new Dictionary<string, object> { { "id", "valSumId" } });
#Html.EditorFor(model => model.ReportOptions);
#Html.EditorFor(model = Model.SearchCriteria });
}
Now you only have to drag your partials to the folder ~/Shared/EditorTemplates/ and rename them to match the model name they are the editor templates for.
In the ~/Shared/EditorTemplates/ folder, make a new "view", example "SearchCriteria.cshtml". Inside, put as "model" the type of class you which to create an editor template for. Example (example class has properties Name and OtherCriteria):
#model MyNamespace.SearchCriteria
<ul>
<!-- Note that I also use EditorFor for the properties; this way you can "nest" editor templates or create custom editor templates for system types (like DateTime or String or ...). -->
<li>#Html.LabelFor(m => m.Name): #Html.EditorFor(m => m.Name)</li>
<li>#Html.LabelFor(m => OtherCriteria): #Html.EditorFor(m => m.OtherCriteria</li>
</ul>
Some good reading about them:
https://www.exceptionnotfound.net/asp-net-mvc-demystified-display-and-editor-templates/
https://www.hanselman.com/blog/ASPNETMVCDisplayTemplateAndEditorTemplatesForEntityFrameworkDbGeographySpatialTypes.aspx
You should add prefix to the PartialView's fields. That will let binding data correctly.
So instead:
#Html.Partial("_ReportOptions", Model.ReportOptions);
Use:
#Html.Partial("_ReportOptions", Model.ReportOptions, new ViewDataDictionary { TemplateInfo = new TemplateInfo { HtmlFieldPrefix = "ReportOptions" }})
I agree with #Styxxy and #Tony, Editor Templates are the better solution. However, your problem is that that you are feeding a sub-model to the partial views. Thus, when the partial view renders it doesn't know that it's part of a larger model and does not generate the correct name attributes.
If you insist on using Partials rather than Editor Templates, then I suggest only passing the Model to the partials, then having each partial do Model.Whatever.Foo and it will generate the correct name attributes for binding.
Try using EditorTemplates instead of Partials http://coding-in.net/asp-net-mvc-3-how-to-use-editortemplates/.
#Html.Partial("_ReportOptions", Model.Contact, new ViewDataDictionary()
{
TemplateInfo = new TemplateInfo()
{
HtmlFieldPrefix = "Contact"
}
})
)
#Html.Partial("_TransactionSearchFields", Model.SearchCriteria, new
ViewDataDictionary()
{
TemplateInfo = new TemplateInfo()
{
HtmlFieldPrefix = "SearchCriteria"
}
})

ModelMetadata.Watermark and MVC View Models

I'm struggling to display a watermark (so called placeholder) on my MVC3 form inputs.
There is already few posts down here talking including the quite focused one here:
Html5 Placeholders with .NET MVC 3 Razor EditorFor extension?
In these posts, advice is made to create tweaked html.TextBox templates.
In my case, my asset is that I should not need any editor template as I'm tweaking them inline.
Better than long talks, here is the relevant part of the actual code:
~/Models/myModel.cs
namespace myProject.Models {
public class myFormModel {
...
[Display(Name = "firstFieldName", Prompt = "firstFieldPrompt")]
public string firstFieldValue { get; set; }
...
}
}
~/Controllers/myFormSurfaceController.cs
namespace myProject.Controllers {
public class myFormSurfaceController : SurfaceController {
...
[ChildActionOnly]
public PartialViewResult myForm()
{
return PartialView("myPartialView", new myFormModel());
}
...
[HttpPost, ValidateAntiForgeryToken]
public ActionResult handleMyFormSubmit(myFormModel model) {...}
...
}
}
~/Views/myProject/Partial/myPartialView.cshtml
#model myFormModel
#{
using (Html.BeginUmbracoForm("handleMyFormSubmit", "myFormSurface", null, new Dictionary<string, object> { { "class", "myFormStyle" }, { "id", "myFormId" } }))
{
...
#Html.TextBoxFor(x => x.firstFieldValue, new { #class = "myInputStyle", #placeholder = ViewData.ModelMetadata.Watermark })
...
}
}
Result is that the placeholder html tag is showing up correctly on my rendered webpage but is empty though Name tag is filled up correctly, even without DisplayName decoration set on my view model's property.
http://localhost/testpage
...
<input type="text" value="" placeholder="" name="firstFieldName" id="firstFieldName" class="myInputStyle">
...
What am I missing here ? I did try indeed to create both editor templates (MultilineText and String) in the correct folder (~/Views/Shared/EditorTemplates/) but I assume they are never called as I'm using "Html.TextBoxFor" and not "Html.TextBox"...
Other thing, if I remove "#placeholder = ViewData.ModelMetadata.Watermark" from the #Html.TextBoxFor call, I don't have any "placeholder" displayed on the rendered webpage. Which is good, this part of the call is definitively fine.
Thanks in advance for any help on that point...
Nicolas.
Edit:
What about if I create more variable in my model.
For instance:
public string firstFieldPrompt { get { return "bla"; } set { } }
and then
#Html.TextBoxFor(x => x.firstFieldValue, new { #class = "myInputStyle", #placeholder = x => x.FirstFieldPrompt })
?
I realise this is an oldie, but you can use the ModelMetadata.FromLambdaExpression() method from within your view (without using templates), i.e.
#Html.TextBoxFor(x => x.firstFieldValue,
new {
#class = "myInputStyle",
#placeholder = ModelMetadata.FromLambdaExpression(x => x.firstFieldValue, ViewData).Watermark
})
Hope this helps someone :-)
The reason you get an empty watermark is that in your case (i.e. not using templates) ViewData actually refers to myFormModel (not myFormModel.firstFieldValue); you are essentially retrieving the watermark of your view model. Since models can't have watermarks ([Display] can't be applied to classes) ViewData.ModelMetadata.Watermark will always be empty for views.
As far as I can see, your only option here (if you don't want to use templates) is doing the watermark inline:
#Html.TextBoxFor(x => x.firstFieldValue, new { #class = "myInputStyle", placeholder = "Your watermark text here" })
By the way, if want to use templates, you need to use the templated helpers #Html.EditorFor() and #Html.DisplayFor(). #Html.TextBoxFor() is just the strongly-typed version of #Html.TextBox(). It is not templated.

EditorFor ignores tabindex. How do you set a tabindex?

The use of tabindex seems to only work for htmlhelpers like Textboxfor and not EditorFor
For example;
<%: Html.TextBoxFor(model => Model.MyItem, new { #tabindex = "3" })%>
Produces a tabindex value.
However, if you use;
<%: Html.EditorFor(model => Model.MyItem, new { #tabindex = "3" })%>
Then the result is that the control is created as expected, but the tabindex is missing.
So...... How is it possible to set the tabindex for a given EditorFor control?
The main problem I was having is that I needed to create a EditorFor type mechanism in order to format the decimal like a currency (our system has multiple currencies so "C" would not have been appropriate), get a tab index working AND allow the system to maintain the standard validation.
I've managed to achieve that using the following. By creating my own custom editor control.
Create a file (mine is called decimal.ascx) within the Views/Shared/EditorTemplates directory of your project.
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<decimal?>" %>
<% int intTabindex = 0;
decimal myVal = 0;
string strModelValue = "";
if (Model != null)
{
myVal = (decimal)Model;
strModelValue = myVal.ToString("#.00");
}
else
strModelValue = "";
if (ViewData["tabindex"] != null)
{
intTabindex = (int)ViewData["tabindex"];
}
%>
<%: Html.TextBox("", strModelValue, new { #tabindex = intTabindex })%>
Essentially, this code just overrides what would normally be presented in a "decimal" EditorFor method with the;
<%: Html.TextBox("", Model.ToString("#.00"), new { #tabindex = intTabindex }) %>
template.
My calling code now reads;
<%: Html.EditorFor(model => Model.MyItem, new { tabindex = 5 })%>
The result is the following code on the page.
<input id="Model_MyItem" name="Model.MyItem" tabindex="5" type="text" value="12.33" />
Which is exactly what I required.
Whilst this is only true for my particular circumstances, I would encourage anybody looking to solve this issue to attempt a custom control first for the task as it might save you a considerable amount of time.
If would of course be possible in the code to create a specific type of control required and adjust the results around that.
For example; we could simple add another item in the call to determine the text format.
new {tabindex = 12, numberformat=2}
Then simply create a handler for all the formats.
Since the EditorFor is just a template for a DataType it's only expecting a datatype as it's Model. There are a couple of ways of going about this I am guessing. You could possibly add the tabindex to an anonymous object that will be merged into the ViewData for the EditorTemplate like so.
Code in your View:
Html.EditorFor(m => m.Username, "test", new { tabindex = 3, style = "width: 400px;" })
EditorForModel Template Check for ViewData:
<%: Html.TextBoxFor(m => m.Username, ViewData)%>
This should render an text input with a tabindex of 3 and style="width: 400px;"
Happy coding.
Edited:
Here is exactly the markup I have inside of my test page:
<%: Html.EditorFor(m => m.DollarsAmount, "NullableDecimal", new { tabindex = 99 }) %>
I'm telling the EditorFor template to select the "NullableDecimal" EditorTemplate that I have created. (You could place a UiHint attribute on the property inside of the model also to tell it what editortemplate to use)
"NullableDecimal" EditorTemplate located in ~/Views/Shared/EditorTemplates:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<decimal?>" %>
<%: Html.TextBox(string.Empty, (Model.HasValue ? Model.Value.ToString("#.00") : null), ViewData) %>
What's more extensible about my implementation is the extra ViewData that I pass in via my anonymous object is merged into the ViewData dictionary to be used by the EditorTemplate. So if you don't pass any ViewData in to the EditorTemplate then it wont render your text inputs tabindex to 0 as your implementation currently will do. Plus your implementation will only account for tabindexes and not for any other input attributes. i.e. maxlength or style

How to retrieve value from DropDownListFor html helper in ASP.NET MVC2?

I know there was few similar questions here about DropDownListFor, but neither helped me...
I use Entity Framework as ORM in my project. There's EF model called "Stete". Stete has Foreign on EF model called "Drustva"
Now I'm trying to make a form for editing the data, for Stete model. I managed to display everything, including Stete.Drustva.Naziv property, but I can't get this last property in my handler method [HttpPost]. It always return 0, no matter what I select in drop down list.
Here's the code:
DrustvaController:
public static IEnumerable<SelectListItem> DrustvaToSelectListItemsById(this KnjigaStetnikaEntities pEntities, int Id)
{
IEnumerable<Drustva> drustva = (from d in pEntities.Drustva
select d).ToList();
return drustva.OrderBy(drustvo => drustvo.Naziv).Select(drustvo => new SelectListItem
{
Text = drustvo.Naziv,
Value = drustvo.Id.ToString(),
Selected = (drustvo.Id == Id)? true : false
});
}
SteteController:
private IEnumerable<SelectListItem> privremenaListaDrustava(int Id)
{
using (var ctx = new KnjigaStetnikaEntities())
{
return ctx.DrustvaToSelectListItemsById(Id);
}
}
public ActionResult IzmijeniPodatkeStete(Int32 pBrojStete)
{
PretragaStetaModel psm = new PretragaStetaModel();
ViewData["drustva"] = privremenaListaDrustava(psm.VratiStetuPoBrojuStete(pBrojStete).Drustva.Id);
ViewData.Model = new Models.Stete();
return View("EditView", (Stete.Models.Stete)psm.GetSteta(pBrojStete));
}
EditView:
<div class="editor-label">
<%: Html.Label("Društvo") %>
</div>
<div class="editor-field">
<%: Html.DropDownListFor(m => m.Drustva.Naziv, ViewData["drustva"] as IEnumerable<SelectListItem>) %>
<%: Html.ValidationMessageFor(model => model.FKDrustvo) %>
</div>
I am sorry for not translating names of the objects into english, but they hardly have appropriate translation. If necessary, I can try creating similar example...
Did you include Html.BeginForm or Ajax.BeginForm in your view markup? That is a common oversight that can cause the behavior you are referring to. I can't tell from the code you pasted in your question.Cheers.

Resources