I am having problem reading this razor syntax that comes from an existing template.
<div class="#(HasText(columnclass) ? columnclass == "myColumns-1" || columnclass == "myColumns-2" ? "col-md-12" : columnclass == "myColumns-12" ? "col-md-6" : "col-md-12" : "col-md-12")">
Is the above the same as:
#if(HasText(columnclass))
{
if(columnclass == "myColumns-1" || columnclass == "myColumns2")
{
<div class="col-md-12"></div>
}
else if (columnclass == "myColumns-12")
{
<div class="col-md-6"></div>
}
else
{
<div class="col-md-12"></div>
}
}
else
{
<div class="col-md-12"></div>
}
Can the first complex razor syntax instead be used in a html.extension or even a func<>?
You can write a simple Extension -
public static class DivExtensions
{
public static MvcHtmlString Div(this HtmlHelper html, string input)
{
if(String.IsNullOrWhiteSpace(input))
return new MvcHtmlString("<div class=\"col-md-12\"></div>");
if (input == "myColumns-12")
return new MvcHtmlString("<div class=\"col-md-6\"></div>");
return new MvcHtmlString("<div class=\"col-md-12\"></div>");
}
}
usage -
#using MVC.NameSpaceOfExtension
#Html.Div("myColumns-6")
NOTE: I cannot guarantee the logic in my extension method. Please do test it on your side and if there is a problem in logic, then please replace it with your logic.
Related
<div class="form-gridcontrol">
<label>Notes</label>
#Html.CustomTextArea(m => m.Notes)
</div>
In ASP.NET MVC , I have created a custom textarea and inputing/displaying data from the database using a Model.Above is the code where you can see the Notes are getting assigned to #Html.CustomTextArea.
I have a situation where , I need to display a text "Not Applicable" if there is no value in "m.Notes"
How I should right the logic in the above code? Please guide.
There are multiple possible ways for this. One of the way is that you can populate in the controller action from where it is loaded like:
public ActionResult YourActionMethod()
{
............
............
if(String.IsNullOrEmpty(model.Notes))
model.Notes = "Not Applicable";
return View(model);
}
Another way can be to introdcue the backing field on your property and write in it's getter:
private String _notes;
public String Notes
{
get
{
return String.IsNullOrEmpty(_notes) ? "Not Applicable" : _notes;
}
set
{
_notes = value;
}
}
You can try this:
#if (Model.Notes != null)
{
#Html.CustomTextArea(m => m.Notes)
}
else
{
#Html.CustomTextArea( m => m.Notes, new { #Value = "Not Applicable"})
}
edit:this is not working with textarea
else
{
#Html.CustomTextArea(m => m.Notes, new {id="mytextarea"})
<script>
$("#mytextarea").text("Not Applicable")
</script>
}
I got a trick for you :)
I need to create a custom #Html.Partial() method.
Use Case
I have a .cshtml page where in I have multple sections like below
<!-- EDUCATION -->
#Html.Partial("Templates/Create/Modules/Education")
<!-- JOBS -->
#Html.Partial("Templates/Create/Modules/Jobs")
I want to be able to create a custom .Partial() method. Something on the likes of this
#Html.CustomPartial("Templates/Create/Modules/Jobs", "jobs", "edit")
where in the last two parameters are module id and action type id respectively. Using these values I will make a decision in my CustomPartial what I need to show in the output.
I am not sure how to go about this one. Please advice.
Or if someone can point me to the source code of the Html.Partial that too would be very helpful.
You can already do this using the overload of #Html.Partial() that accepts a ViewDataDictionary
#Html.Partial("Templates/Create/Modules/Jobs", new ViewDataDictionary { { "module", someValue }, {"edit", anotherValue }})
Then in the partial
#if(ViewData["module"] == someValue)
{
// do something
}
else
{
// do something else
}
And if your still interested, here is the source code
Here is what worked for me
public static class CustomHtmlHelpers
{
public static MvcHtmlString RenderModule(this HtmlHelper helper,
string partialViewName,
string moduleName,
string actionType)
{
var isAccessAllowed = _someService.someMethod(userId, moduleName, actionType);
if (isAccessAllowed)
{
return helper.Partial(partialViewName);
}
else
{
return MvcHtmlString.Empty;
}
}
}
#Html.Partial("../Partial_views/_Menu_creation", new ViewDataDictionary { { "Type",
"Menu" }, { "Menu", "Dimensions" }, { "Active", "Yes" }, { "Icon", "icon-1" }, {
"data-hide", "" } })
In partial view
#if (ViewData["Type"] == "Menu")
{
#if (ViewData["Active"] == "Yes")
{
<a data-check='#ViewData["Menu"]' role="button" class="active-menu">
<b class='#ViewData["Icon"]'></b>
<span>#ViewData["Menu"]</span>
</a>
}
else
{
}
}
#if (ViewData["Type"] == "Heading")
{
}
This is working
If I have an integer value that I want to display on a page, I can do that a number of ways:
<span>#Html.DisplayFor(modelItem => item.UserId)</span>
<span>#item.UserId</span>
But what is the best way to convert that to displaying the value IF UserId != 0. But if UserId == 0, display an empty string. Is there a way to do it right in Razor syntax or do I need to head to code?
Create an extension method for int:
public static class IntExtensions
{
public static string EmptyIfZero(this int value)
{
if(value == 0)
return string.Empty;
return value.ToString();
}
}
... and then in your Razor view, do:
<span>#item.UserId.EmptyIfZero()</span>
<span>#((item.UserID == 0) ? "" : #item.UserID.ToString())</span>
OR
<span>#if(item.UserID == 0) { <span></span> }
else { #Html.DisplayFor(m => item.UserID); }
</span>
I think you could do this with one if condition
<span>#if(item.UserID != 0) { #Html.DisplayFor(m => item.UserID); } //the browser would render empty string by itself
To render content without putting the redundant (as you said) <span>, use the #: - MVC3 Razor: Displaying html within code blocks and #: for displaying content
<span>
#if(item.UserID == 0) { } //this is redundant too
else { #Html.DisplayFor(m => item.UserID);
}
</span>
Note that I have moved the } to next line. ASP.Net MVC did not accept it
<span>
#if(item.UserID == 0) { #:Some content with no html tag wrapper
}
else { #Html.DisplayFor(m => item.UserID);
}
</span>
You can do this:
<span>#(item.UserId != 0 ? item.UserId.ToString() : string.Empty)</span>
I think the best and most reusable would be to create a template.
So you create e.g. UserId.vbhtml in ~/Views/Shared/DisplayTemplates
with the following content:
in the template you build all the logic, which would be something like (I am typing without checking and I usually code in VB, so there may be mistakes):
#model int
#((Model == 0) ? "" : #Model)
And then in the code you use:
#Html.DisplayFor(modelItem => item.UserId, "UserId")
You can also put UIHint attribute over UserId in your class: [UIHint("UserId")]
and then you do not need to provide the template name.
This is another way to display integer or blank when model value is 0.
Model:
[Display(Name = "User Id")]
public int UserId { get; set; }
View:
<span>
#Html.DisplayNameFor(m => m.UserId)
</span>
<span>
#Html.TextBoxFor(m => m.UserId, new { #Value = (Model.UserId > 0 ? Model.UserId.ToString() : string.Empty) })
</span>
I did some more global solution, display blank for:
- integer: 0
- decimal: 0.0
- string: "0x0" or "0x0x0"
Extension class:
public static class DataTypesExtension
{
static string[] Keywords = { "0", "0.0", "0.00", "0.000", "0.0000", "0,0", "0,00", "0,000", "0,0000", "0x0", "0x0x0" };
public static string BlankIfZero(this object n)
{
string ret = string.Empty;
if (n == null)
return ret;
string sn = n.ToString().Replace(" ", "");
if (!DataTypesExtension.Keywords.Contains(sn))
ret = n.ToString();
return ret;
}
}
Display template:
#model object
#using MyProject.Extensions;
#{
string val = Model.BlankIfZero();
#val;
}
And in View Model:
[UIHint("BlankIfZero")]
public int? MyIntProperty { get; set; }
[UIHint("BlankIfZero")]
public decimal? MyDecimalProperty { get; set; }
[UIHint("BlankIfZero")]
public string MyStringProperty { get; set; }
On my case, i found this to be easier:
$(document).ready(function () {
$("#myField").val("");
}
Not sure if this is wahat you were looking for, but worked fine for me.
I have the following:
#if ((#Model.SeqId != 0) & (Model.SeqId != 1))
{
<text>
window.location.href = "www.stackoverflow.com";
</text>
}
I don't know much about razor. Is there something I could do to make it simpler?
Yes, you can. Suffice to define a property on your view model
public bool ShouldRedirectToSO
{
get
{
return (SeqId != 0 && SeqId != 1);
}
}
and then:
<script type="text/javascript">
#if (Model.ShouldRedirectToSO)
{
#:window.location.href = 'http://www.stackoverflow.com';
}
</script>
or if you intend to redirect immediately on page load if the condition is met you could also do this directly from the controller:
public ActionResult Foo()
{
var model = ...
if (model.ShouldRedirectToSO)
{
return Redirect("http://www.stackoverflow.com");
}
return View(model);
}
Can SeqId ever be less than 0? If not, you could do
#if (Model.SeqId > 1)
{
<text>
window.location.href = "www.stackoverflow.com";
</text>
}
Also, you don't need to # the Model in a code block. You probably want to use && instead of & as this fails as soon as the first test is false, saves a few CPU cycles.
http://msdn.microsoft.com/en-us/library/2a723cdk(v=vs.71).aspx
My Model contains a property named Title, and in my Create view I set the page title using ViewBag.Title.
This creates the following problem: the form generated by Html.Editor will display the text from ViewBag.Title, instead of the model's Title value.
The only workaround I have found is first calling Html.Editor, and then setting the View.Title.
Does anyone have a better solution?
Edit 1: I am using MVC 3.
Edit 2: This is my DisplayTemplates/Object.cshtml:
#model dynamic
#using Iconum.VS10CS040.Library.Web.MVC3.Helpers
#if (ViewData.TemplateInfo.TemplateDepth > 1) {
<span class="editor-object simple">#ViewData.ModelMetadata.SimpleDisplayText</span>
} else {
foreach (var prop in ViewData.ModelMetadata.Properties.Where(
pm =>
pm.ShowForEdit
&& !ViewData.TemplateInfo.Visited(pm)
&& pm.ModelType != typeof(System.Data.EntityState)
&& !pm.IsComplexType
)
)
{
if (prop.HideSurroundingHtml) {
<text>#Html.Editor(prop.PropertyName)</text>
} else {
string css = "";
if (prop.Model != null && prop.Model.GetType() != null)
{
css += " " + prop.Model.GetType().ToString().ToLower().Replace('.', '-');
}
if (prop.DataTypeName != null)
{
css += " " + prop.DataTypeName.ToLower();
}
if (prop.IsRequired && prop.ModelType.FullName != "System.Boolean")
{
css += " required";
}
<div class="editor-container #css">
<div class="editor-label">
#if (!String.IsNullOrEmpty(Html.Label(prop.PropertyName).ToHtmlString()))
{
// Use LabelWithForThatMatchesTheIdOfTheInput instead of Label because of a bug (fixed in MVC 3)
#Html.LabelWithForThatMatchesTheIdOfTheInput(prop.PropertyName)
}
#if (prop.IsRequired && prop.ModelType.FullName != "System.Boolean")
{
#Html.Raw(" <span class=\"required\">*<span>");
}
</div>
<div class="editor-field">
#* This the line that causes my problem *#
#Html.Editor(prop.PropertyName)
#Html.ValidationMessage(prop.PropertyName)
</div>
</div>
}
} //foreach
// Loop though all items in the Model with an TemplateHint (UIHint)
foreach (var prop in ViewData.ModelMetadata.Properties.Where(
pm => pm.ShowForEdit
&& !ViewData.TemplateInfo.Visited(pm)
&& pm.ModelType != typeof(System.Data.EntityState)
&& !pm.IsComplexType
&& pm.TemplateHint != null
&& (
pm.TemplateHint == "jWYSIWYG0093"
||
pm.TemplateHint == "jQueryUIDatepicker"
||
pm.TemplateHint == "CKEditor"
)
)
)
{
// TODO: check for duplicate js file includes
#Html.Editor(prop.PropertyName, prop.TemplateHint + "-Script")
}
}
I would recommend using EditorFor instead of Editor.
Html.EditorFor(x => x.Title)
instead of:
Html.Editor("Title")
This way not only that the view takes advantage of your view model but it behaves as expected in this case.
Example with ASP.NET MVC 3.0 RTM (Razor):
Model:
public class MyViewModel
{
public string Title { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Title = "ViewBag title";
ViewData["Title"] = "ViewData title";
var model = new MyViewModel
{
Title = "Model title"
};
return View(model);
}
}
View:
#model AppName.Models.MyViewModel
#{
ViewBag.Title = "Home Page";
}
#Html.EditorFor(x => x.Title)
#{
ViewBag.Title = "Some other title";
}
So no matter how much we try to abuse here the editor template uses the correct model title (which is not the case if we used Html.Editor("Title")).
As suggested by the other answers, using EditorFor instead of Editor seems to work around the problem. However, using EditorFor requires knowledge of the model type and property type at compile-time, which isn't the case for Object.cshtml.
You can still do this by building up and calling the correct generically-constructed EditorFor method using reflection. The code to do this is really messy, so here are some re-usable extension methods to do it for you.
Use them like this in Object.cshtml where prop is an instance of ModelMetadata like in the question:
#Html.DisplayFor(prop)
#Html.LabelFor(prop)
#Html.EditorFor(prop)
#Html.ValidationMessageFor(prop)
Here are the extension methods:
using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Web.Mvc;
using System.Web.Mvc.Html;
using System.Web.Routing;
namespace ASP
{
public static class NonStronglyTypedStronglyTypedHtmlHelpers
{
public static MvcHtmlString DisplayFor<TModel>(this HtmlHelper<TModel> html, ModelMetadata prop)
{
return StronglyTypedHelper(html, h => h.DisplayFor, prop);
}
public static MvcHtmlString EditorFor<TModel>(this HtmlHelper<TModel> html, ModelMetadata prop)
{
return StronglyTypedHelper(html, h => h.EditorFor, prop);
}
public static MvcHtmlString LabelFor<TModel>(this HtmlHelper<TModel> html, ModelMetadata prop)
{
return StronglyTypedHelper(html, h => h.LabelFor, prop);
}
public static MvcHtmlString ValidationMessageFor<TModel>(this HtmlHelper<TModel> html, ModelMetadata prop)
{
return StronglyTypedHelper(html, h => h.ValidationMessageFor, prop);
}
private static MvcHtmlString StronglyTypedHelper(HtmlHelper html, Func<HtmlHelper<object>, GenericHelper<object>> accessMethod, ModelMetadata prop)
{
var constructedMethod = MakeStronglyTypedHelper(html, accessMethod, prop);
var genericPropertyExpression = MakePropertyExpression(prop);
var typedHtmlHelper = MakeStronglyTypedHtmlHelper(html, prop.ContainerType);
return (MvcHtmlString)constructedMethod.Invoke(null, new object[] { typedHtmlHelper, genericPropertyExpression });
}
private static MethodInfo MakeStronglyTypedHelper(HtmlHelper html, Func<HtmlHelper<object>, GenericHelper<object>> accessMethod, ModelMetadata prop)
{
var objectTypeHelper = new HtmlHelper<object>(html.ViewContext, html.ViewDataContainer, html.RouteCollection);
var runMethod = accessMethod(objectTypeHelper);
var constructedMehtod = runMethod.Method;
var genericHelperDefinition = constructedMehtod.GetGenericMethodDefinition();
return genericHelperDefinition.MakeGenericMethod(prop.ContainerType, prop.ModelType);
}
private static object MakeStronglyTypedHtmlHelper(HtmlHelper html, Type type)
{
var genericTypeDefinition = typeof(HtmlHelper<>);
var constructedType = genericTypeDefinition.MakeGenericType(type);
var constructor = constructedType.GetConstructor(new[] { typeof(ViewContext), typeof(IViewDataContainer), typeof(RouteCollection) });
return constructor.Invoke(new object[] { html.ViewContext, html.ViewDataContainer, html.RouteCollection });
}
private static LambdaExpression MakePropertyExpression(ModelMetadata prop)
{
var propertyInfo = prop.ContainerType.GetProperty(prop.PropertyName);
var expressionParameter = Expression.Parameter(prop.ContainerType);
var propertyExpression = Expression.MakeMemberAccess(expressionParameter, propertyInfo);
return Expression.Lambda(propertyExpression, expressionParameter);
}
private delegate MvcHtmlString GenericHelper<TModel>(Expression<Func<TModel, object>> expression);
}
}
I found partial solution myself.
Just use:
#Html.EditorForModel()
instead of:
#foreach (var property in Model.GetMetadata().Properties)
{
<div class="editor-label">
#Html.Label(property.PropertyName)
</div>
<div class="editor-field">
#Html.Editor(property.PropertyName)
#Html.ValidationMessage(property.PropertyName)
</div>
}
Html.EditorForModel() method return same results, but without described problem.
I solve same problem. Use this syntax instead Html.Editor
#(Html.EditorFor(p => property.Model))