Following Brad Wilson's excellent series on using and customizing editor templates, I tried adding an Object.cshtml to the Shared\EditorTemplates folder. The template renders, but the [HiddenInput(DisplayValue = false)] on a model property doesn't render a hidden <input type="hidden" ... /> as expected. Using [HiddenInput(DisplayValue = true)] renders both the hidden and visible elements as expected.
I have verified that the default template for Object works fine and renders the hidden inputs. It's only a problem when building a custom template based on Brad's series above.
Looks like something has changed. Inspecting the MVC 3 source, I found that prop.HideSurroundingHtml is used to determine when to print the surrounding HTML, not to print only the hidden element. The following template allows several levels of rendering an editor for an object graph:
#if (ViewData.TemplateInfo.TemplateDepth > 2)
{
#(ViewData.ModelMetadata.Model != null ?
ViewData.ModelMetadata.SimpleDisplayText :
ViewData.ModelMetadata.NullDisplayText)
}
else
{
foreach (var prop in ViewData.ModelMetadata.Properties.Where(pm => pm.ShowForEdit && !ViewData.TemplateInfo.Visited(pm)))
{
if (!prop.HideSurroundingHtml)
{
if (!String.IsNullOrEmpty(Html.Label(prop.PropertyName).ToHtmlString()))
{
<div class="editor-label">#Html.Label(prop.PropertyName)</div>
}
#Html.Raw("<div class=\"editor-field\">")
}
#Html.Editor(prop.PropertyName)
if (!prop.HideSurroundingHtml)
{
#Html.ValidationMessage(prop.PropertyName, "*")
#Html.Raw("</div>")
}
}
}
I tidied my version of that up a bit for anyone who cares:
#foreach (var modelMetadata in ViewData.ModelMetadata.Properties)
{
if (modelMetadata.HideSurroundingHtml == false)
{
if (!string.IsNullOrEmpty(Html.Label(modelMetadata.PropertyName).ToHtmlString()))
{
<div class="editor-label">
#Html.Label(modelMetadata.PropertyName)
</div>
}
<div class="editor-field">
#Html.Editor(modelMetadata.PropertyName)
</div>
}
}
Related
I have an MVC 5 project and we are creating radio buttons from database records.
It works fine below but there has to be a way to NOT have the if check in there and either append the checked after the fact or even before.
As shown below, the only difference between the 2 radio button creations is: new { #checked = "checked" }
#{
foreach (var item in Model.PaintColor)
{
if (item.DefaultChoice == true)
{
#Html.RadioButtonFor(m => m.pledge.PaintColorId,
item.ColorId.ToString(),
new { #checked = "checked" }
)
}
else
{
#Html.RadioButtonFor(m => m.pledge.PaintColorId,
item.ColorId.ToString()
)
}
#Html.Label("Color" + item.ColorId, item.ColorDesc)
}
<br />
}
See comments from Stephen regarding fixing this. Basically setting it as checked as not required as since it checks the value that it is being bound to.
When the view model is initially created I set the field value to the Default value and the radio button is set as checked.
foreach (var item in Model.PaintColor)
{
var idVal = string.Format("PaintColor_{0}", item.ColorId);
<div>
<label>
#Html.RadioButtonFor(m => m.pledge.PaintColorId,
item.ColorId,
new { id = string.Format("Rb{0}", idVal) }
)
<span>#item.Description</span>
</label>
We are working on Kendo MVC UI, where we are sending the data from one view to another view, all the data(testbox, dropdown) are getting passed to the next view except the attachments(pdf,xlsx).
Below is the code which in the controller which we have written to capture from view and save the data and pass the same data to the another view and bind the data to the kendo controls(upload control also)
public ActionResult SaveData(System.Web.Mvc.FormCollection form, IEnumerable<HttpPostedFileBase> files) // insert operation
{
//*************************//
if (form != null)
{
string ddluserexceptioncategory = Convert.ToString(form["txtexceptioncategory"], CultureInfo.InvariantCulture);
if (!string.IsNullOrEmpty(ddluserexceptioncategory))
{
ddluserexceptioncategory = ddluserexceptioncategory.Trim();
}
if (ddluserexceptioncategory == "User Management")
{
//Bind the data to the class object(_clsObj)
if (files != null)
{
TempData["FileName"] = files;
_clsObj.Files = files;
}
TempData["SecondViewData"] = _clsObj;
return RedirectToAction("ExceptionType", "Home", new { id = 0, regionId = _clsObj.RegionId, status1 = "New,In Progress", keyword1 = string.Empty });
}
}
string regions = "", statusValue = "";
if (form != null)
{
regions = form["hiddenregionselected"] + "";
statusValue = form["hiddenstatusselected"] + "";
}
return RedirectToAction("homepage", "Home", new { region = regions, status = statusValue });
}
Below is the code which we bind the request to the second
#if (TempData["FileName"] != null)
{
IEnumerable<HttpPostedFileBase> firstFile = (IEnumerable<HttpPostedFileBase>)TempData["FileName"];
<div class="k-dropzone">
<div class="k-button k-upload-button">
<input name="files" type="file" data-role="upload" multiple="multiple" autocomplete="off" tabindex="-1" class="valid" style="display: none;">
<input id="files" name="files" type="file" data-role="upload" multiple="multiple" autocomplete="off">
<ul id="files1" class="k-upload-files k-reset">
#foreach (var file in firstFile)
{
string filename= Path.GetFileName(file.FileName);
<li class="k-file" data-uid="7aa03676-4dac-468e-b34a-99ac44d23040">
<span class="k-icon k-success">uploaded</span>
<span class="k-filename" title="#filename">#filename</span>
<strong class="k-upload-status">
<span class="k-icon k-delete"></span>
</strong>
</li>
}
</ul>
</div>
</div>
<script>
jQuery(function()
{jQuery("#files").kendoUpload(
{"select":uploadselect,
"localization":{"select":"Browse file",
"headerStatusUploading":"uploading..",
"headerStatusUploaded":"uploded.."},
"async":{"saveUrl":"/Home/Save",
"autoUpload":false,"removeUrl":
"/Home/Remove"}});});
</script>
}
else
{
#(Html.Kendo().Upload().Name("files").Async(a => a.Save("Save", "Home").Remove("Remove", "Home").AutoUpload(false)).Multiple(true).Messages(m =>
{
m.Select("Browse file");
}).Events(events => events.Select("uploadselect")))
}
Any suggestions or help is much appreciated.
My guess is that the issue is coming from using TempData to get this data from your markup to your controller or vice versa.
As you are likely aware, anything you put into TempData is discarded after the next request completes (Using Tempdata in ASP.NET MVC - Best practice, http://www.codeproject.com/Articles/476967/What-is-ViewData-ViewBag-and-TempData-MVC-Option).
I would suggest to try using the ViewBag to prove this theory. If it proves out you might think about passing this data as part of a complex object instead of using MVC's data dictionaries.
I am having difficulties trying to render a checkBox on my Partial View. Basically, what I want is to render the checkBox based on a value extracted from the database. Please see my code below:
<div class="editor-label">
#Html.LabelFor(model => model.Active)
</div>
<div class="editor-field">
#{if (Model.Active == 'Y')
{ Html.CheckBox("Active", true); }
else
{ Html.CheckBox("Active", true); }
}
</div>
In this code block, I am checking for the value inside the Active field from my model and renders the isChecked property of the checkBox to true or false based on that value.
I have debugged this code and used a breakpoint. I have a value of 'Y' from my database and it did went through the if statement. However, when the form pops up, the checkBox didn't render.
Can someone please help me? Thanks!
I think your main problem may be because you have #{ instead of just # before the if.. try remove that.
also, to make things easier.. create a new property on your view model:
public bool IsActive
{
get { return Active == "Y"; }
}
then on your view, use Html.CheckBoxFor(m=> m.IsActive)
I have a Page.cshtml similar to the following (that does not work):
#{
Layout = "../Shared/Layouts/_Layout.cshtml";
var mycollection = (ViewBag.TheCollection as IQueryable<MyCollectionType>);
}
<h2>#ViewBag.Title</h2>
content here
#if (mycollection != null && mycollection.Count() > 0)
{
#section ContentRight
{
<h2>
Stuff
</h2>
<ul class="stuff">
#foreach (MyCollectionType item in mycollection )
{
<li class="stuff-item">#item.Name</li>
}
</ul>
}
}
As I said, this does not work. I want to not define the section if there's nothing in the collection. Is there any way to have something like this work? If not, what are my other options? I'm very new to this Razor ViewEngine.
Edit
In my layout i have:
#if(IsSectionDefined("ContentRight"))
{
<div class="right">
RenderSection("ContentRight")
</div>
}
what i don't want is the div to output when the section is empty.
I ended up doing something a little hacky to get it working how I needed it.
on my page i have:
#{
Layout = "../Shared/Layouts/_Layout.cshtml";
var mycollection = (ViewBag.TheCollection as IQueryable<MyCollectionType>);
ViewBag.ShowContentRight = mycollection != null && mycollection.Count() > 0;
}
then in my layout i have:
#if(IsSectionDefined("ContentRight") && (ViewBag.ShowContentRight == null ||ViewBag.ShowContentRight == true))
{
<div class="right">
RenderSection("ContentRight")
</div>
}
else if(IsSectionDefined("ContentRight"))
{
RenderSection("ContentRight")
}
If the section is defined it has to be rendered, but if there's no content i dont want the <div>s
If there's a better way i'd like to know.
The renderer is expecting the method to be called sometime in the layout file. You can spoof the renderer and use "global" conditionals (think login).
#{
ViewBag.content = RenderBody();
}
#if (Request.IsAuthenticated) {
#ViewBag.content;
}
else {
#Html.Partial("_LoginPartial")
}
Extension method with private static readonly field info for perf:
private static readonly FieldInfo RenderedSectionsFieldInfo = typeof(WebPageBase).GetField("_renderedSections", BindingFlags.Instance | BindingFlags.NonPublic);
public static void EnsureSectionsAreRegisteredAsRendered(this WebPageBase webPageBase, params string[] sectionNames)
{
var renderedSections = RenderedSectionsFieldInfo.GetValue(webPageBase) as HashSet<string>;
if (renderedSections == null)
{
throw new WebCoreException("Could not get hashset from private field _renderedSections from WebPageBase");
}
foreach (var sectionName in sectionNames)
{
if (!renderedSections.Contains(sectionName))
{
renderedSections.Add(sectionName);
}
}
}
In your cshtml:
#{ this.EnsureSectionsAreRegisteredAsRendered("SectionName1", " SectionName2", "…"); }
Yes, yes, yes.... I know.... bad reflection! Use at your own risk :)
I use the following method in my view base class (from this excellent blog post http://haacked.com/archive/2011/03/05/defining-default-content-for-a-razor-layout-section.aspx/):
public HelperResult RenderSection(string name, Func<dynamic, HelperResult> defaultContents)
{
if (IsSectionDefined(name))
{
return RenderSection(name);
}
return defaultContents(null);
}
If you don't have a view base class, I recommend one because it lets you add all sorts of little extra functionality to your views. Just create a class with the following signature: public abstract class MyViewPage<T> : WebViewPage<T> and then set it in your web.config:
<system.web.webPages.razor>
<pages pageBaseType="MyViewPage">
...
</pages>
</system.web.webPages.razor>
You can wrap your whole section in an if statement with IsSectionDefined
Layout.cshtml:
#if (IsSectionDefined("ContentRight"))
{
<div>
#RenderSection(name: "ContentRight", required: false)
</div>
}
Your cshtml page:
#section ContentRight
{
#if (mycollection != null && mycollection.Count() > 0)
{
<h2>
Stuff
</h2>
<ul class="stuff">
#foreach (MyCollectionType item in mycollection )
{
<li class="stuff-item">#item.Name</li>
}
</ul>
}
}
Okay, maybe I'm missing something, but I can't figure this out. Using ASP.NET MVC 3, Razor views.
I have a model object like this:
public class MyModel
{
public HttpPostedFileBase File { get; set; }
public string Title { get;set; }
public string Description { get; set; }
}
When, in a strongly typed view, I call #Html.EditorForModel(), it only generates the Title and Description form fields.
I created the file: Views\Shared\EditorTemplates\HttpPostedFileBase.cshtml, with dummy content, but it still doesn't get rendered.
Is it possible to get EditorForModel to generate file input fields?
I managed to get it working by creating a custom Object.cshtml editor template:
#foreach (var prop in ViewData.ModelMetadata.Properties.Where(pm => pm.ShowForEdit && !ViewData.TemplateInfo.Visited(pm)))
{
if (prop.HideSurroundingHtml)
{
#Html.Editor(prop.PropertyName)
}
else
{
<div class="editor-container">
<div class="editor-label">
#Html.Label(prop.PropertyName, prop.DisplayName)
</div>
<div class="editor-field">
#Html.Editor(prop.PropertyName, prop.TemplateHint)
#Html.ValidationMessage(prop.PropertyName, "*")
</div>
</div>
}
}
Basically it calls Html.Editor() for each property of the model. I don't know if it's a good solution, but it works for now.
I have investigated a similar problem - the editor for complex properties is not output. Your case may be different but the workaround that I found (creating an editor for your model - Model.ascx) should work for you too.