Can I make the razor syntax simpler - asp.net-mvc

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

Related

Implement null check logic in Mvc View with a model

<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 :)

razor cshtml javascript not reachable

I am trying to run javascript based on TempData that i pass from the controller.
But using the code below with razor, it neve reach to second if statement. I have a feeling that i am not using the proper razor syntax.
#if (TempData["status"] != null)
{
<script type="text/javascript">
var status = "#TempData["status"]";
if (status == "customerAdded") {
swal("1!", "good", "success")
}
else {
}
</script>
}
You can convert TempData to String or else to check data value or you can use console.log("-"); inside your javascript to check your code run or not
You need to use Json.Encode and #Html.Raw on your C# data to make it compatible with your scripts.
#if (TempData["status"] != null)
{
<script type="text/javascript">
var status = '#Html.Raw(Json.Encode(TempData["status"]))'; // this is the change
if (status == "customerAdded") {
swal("1!", "good", "success")
}
else {
}
</script>
}

Difficulty reading this razor syntax nested if else

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.

Cascading dropdown changing view and model ASP.NET and Razor

I am newbie in ASP.NET MVC 3 and Razor. I want to make multiple forms in one view. All forms (in red rectangle) will show depend on what I choose in "Jenis Registrasi" dropdown (red arrow). Sometimes few forms need different model to load.
How the good implementation for this?
Sorry for unrepresentative title and question. Thanks for guiding me. :D
I kinda threw this together, but its similar to what I do in a lot of ajax situations. I'd certainly re factor out some of the logic. Maybe add blockUi into the JavaScript.
In your client you will have something like
$('RegistrasiDropDown').change(function () {
$.get('#Url.Action("GetExtraFormFields")', { id: $('#RegistrasiDropDown').val() }, function (data) {
if (data.success == false) {
//Handle Error
});
} else {
$('#ExtraFormSection').html(data);
}
})
});
In Your controller you will have something like (in c#), but the concept will be the same in VB
public ActionResult GetExtraFormFields(string id)
{
try
{
var registrationItem= GetRegistrationItemById(id);
if (condition1 == true) //Replace your own logic here.
{
var model = new ModelType1 {
Prop1 = "foo";
}
return PartialView("_PartialView1", model)
}
else if ()//// and so on
}
catch (Exception exception)
{
return return Json(new { success = false, message = exception.msg }, JsonRequestBehavior.AllowGet);
}
}

ASP.NET MVC Validation form with AngularJS

I'm with a project in MVC 4 and AngularJS (+ twitter bootstrap). I usually use in my MVC projects "jQuery.Validate", "DataAnnotations" and "Razor". Then I enable these keys in my web.config to validate properties of model on the client:
<add key="ClientValidationEnabled" value="true" />
<add key="UnobtrusiveJavaScriptEnabled" value="true" />
For example if I have in my model this:
[Required]
[Display(Name = "Your name")]
public string Name { get; set; }
With this Cshtml:
#Html.LabelFor(model => model.Name)
#Html.TextBoxFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
The html result would:
<label for="Name">Your name</label>
<input data-val="true" data-val-required="The field Your name is required." id="Name" name="Name" type="text" value="" />
<span class="field-validation-valid" data-valmsg-for="Name" data-valmsg-replace="true"></span>
But now when I use AngularJS, I want to render maybe like this:
<label for="Name">Your name</label>
<input type="text" ng-model="Name" id="Name" name="Name" required />
<div ng-show="form.Name.$invalid">
<span ng-show="form.Name.$error.required">The field Your name is required</span>
</div>
I do not know if there are any helper or "Data Annotation" to resolve this. I understand that AngularJS has many more features like:
<div ng-show="form.uEmail.$dirty && form.uEmail.$invalid">Invalid:
<span ng-show="form.uEmail.$error.required">Tell us your email.</span>
<span ng-show="form.uEmail.$error.email">This is not a valid email.</span>
</div>
Well, specifically. I need some helper or "Data Annotation" to resolve the attributes (Data Annotation) for display on the client with AngularJS.
If it still does not exist, perhaps it is time to do, like RazorForAngularJS
Edit
I think perhaps the best way to work with ASP.NET MVC and AngularJS is do it (front-end) by hand (writing all the HTML by hand)
As someone that's authored an ASP.Net/Angular website, I can tell you that you're going to be way better off stepping away from using Razor to render your HTML where you can.
In my projects I've set up one razor view to render my main page (I'm using a single page app written in Angular), then I have a folder of straight .html files that I use as my templates for Angular.
The rest is done in ASP.Net Web API calls in my case, but you can also use MVC action with JSON results.
As soon as I switched to this architecture, things went a lot more smoothly for me, development wise.
I agree with blesh idea about stepping away from razor, but you can create some tools for creating pages more rapid. IMHO it is better to use razor features where they needed instead of removing it from out toolset.
BTW have a look at ngval. It brings data annotations to client side as angularjs validators. It has an html helper and an angular module. I have to mention that project is in early development stages.
I wrote a directive to smooth out the transition from MVC to AngularJs. The markup looks like:
<validated-input name="username" display="User Name" ng-model="model.username" required>
Which behaves identically to Razor conventions, including delaying validation until after a field is modified. Over time, I've found maintaining my markup pretty intuitive and simple.
My article on the subject
Plinkr
I think there are probably half a dozen ways to do what you want. Probably the easiest is to use an Angular directive that recognizes jquery.validation markup.
Here is such a project: https://github.com/mdekrey/unobtrusive-angular-validation
And here is another: https://github.com/danicomas/angular-jquery-validate
I haven't tried either because personally, I solved this problem by writing code to make MVC output angular validation attributes instead of jquery.validation.unobtrusive attributes.
A 3rd option is to rely only on server side validation. Though this is obviously slower, it may be your only option sometimes for more complex validation scenarios. In this case, you just have to write javascript to parse the ModelStateDictionary object that Web API controllers usually return. There are some examples out there on how to do that and integrate it into AngularJS's native validation model.
Here is some incomplete code to parse the ModelStateDictionary:
````
angular.module('app')
.directive('joshServerValidate', ['$http', function ($http) {
return {
require: 'ngModel',
link: function (scope, ele, attrs, c) {
console.info('wiring up ' + attrs.ngModel + ' to controller ' + c.$name);
scope.$watch('modelState', function () {
if (scope.modelState == null) return;
var modelStateKey = attrs.joshServerValidate || attrs.ngModel;
modelStateKey = modelStateKey.replace(attrs.joshServerValidatePrefix, '');
modelStateKey = modelStateKey.replace('$index', scope.$index);
modelStateKey = modelStateKey.replace('model.', '');
console.info('validation for ' + modelStateKey);
if (scope.modelState[modelStateKey]) {
c.$setValidity('server', false);
c.$error.server = scope.modelState[modelStateKey];
} else {
c.$setValidity('server', true);
}
});
}
};
}]);
````
I'm rather disappointed with the other answers provided here. "Don't do it" isn't such a great suggestion when you're trying to validate something a little more difficult than an email address.
I solved this in a slightly different way. I modified my MVC application to response to the application/json content type via a filter and a custom view engine which injects a Json serializer razor template into the view locations to search.
This was done to allow skinning of our website with jQuery UI, Bootstrap & Json responses for the same controllers/actions.
Here is a sample json result:
{
"sid": "33b336e5-733a-435d-ad11-a79fdc1e25df",
"form": {
"id": 293021,
"disableValidation": false,
"phone": null,
"zipCode": "60610",
"firstName": null,
"lastName": null,
"address": null,
"unit": null,
"state": "IL",
"email": null,
"yearsAtAddress": null,
"monthsAtAddress": null,
"howHeard": null
},
"errors": [
"The first name is required",
"The last name is required",
"Please enter a phone number",
"Please enter an email address"
],
"viewdata": {
"cities": [
{
"selected": false,
"text": "CHICAGO",
"value": "CHICAGO"
}
],
"counties": [
{
"selected": false,
"text": "COOK"
}
]
}
}
The filter is used to translate redirect results into a json object which passes the next url onto the calling program:
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
// if the request was application.json and the response is not json, return the current data session.
if (filterContext.HttpContext.Request.ContentType.StartsWith("application/json") &&
!(filterContext.Result is JsonResult || filterContext.Result is ContentResult))
{
if (!(filterContext.Controller is BaseController controller)) return;
string url = filterContext.HttpContext.Request.RawUrl ?? "";
if (filterContext.Result is RedirectResult redirectResult)
{
// It was a RedirectResult => we need to calculate the url
url = UrlHelper.GenerateContentUrl(redirectResult.Url, filterContext.HttpContext);
}
else if (filterContext.Result is RedirectToRouteResult routeResult)
{
// It was a RedirectToRouteResult => we need to calculate
// the target url
url = UrlHelper.GenerateUrl(routeResult.RouteName, null, null, routeResult.RouteValues, RouteTable.Routes,
filterContext.RequestContext, false);
}
else
{
return;
}
var absolute = url;
var currentUri = filterContext.HttpContext.Request.Url;
if (url != null && currentUri != null && url.StartsWith("/"))
{
absolute = currentUri.Scheme + "://" + currentUri.Host + url;
}
var data = new {
nextUrl = absolute,
uid = controller.UniqueSessionId(),
errors = GetFlashMessage(filterContext.HttpContext.Session)
};
var settings = new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
Formatting = Formatting.Indented,
NullValueHandling = NullValueHandling.Ignore
};
filterContext.Result = new ContentResult
{
ContentType = "application/json",
Content = JsonConvert.SerializeObject(data,settings)
};
}
Here is the Views\Json\Serializer.cshml, with using statements excluded for brevity and security of our codebase. This does three attempts at returning a response. The first is to read the original View{controller}{action}.cshtml, parsing out the html helpers and placing those into forms and fields. The second attempt looks for and elements from our built-in blogging system (PostContent below) and failing that we just use the Model.
#model dynamic
#{
Response.ContentType = "application/json";
Layout = "";
var session = new Object(); // removed for security purposes
var messages = ViewBag.Messages as List<string>() ?? new List<string>();
var className = "";
if (!ViewData.ModelState.IsValid)
{
messages.AddRange(ViewData.ModelState.Values.SelectMany(val => val.Errors).Select(error => error.ErrorMessage));
}
dynamic result;
string serial;
try
{
Type tModel = Model == null ? typeof(Object) : Model.GetType();
dynamic form = new ExpandoObject();
dynamic fields = new ExpandoObject();
var controller = ViewContext.RouteData.Values["controller"] as string ?? "";
var action = ViewContext.RouteData.Values["action"] as string;
var viewPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Views", controller, action + ".cshtml");
if (File.Exists(viewPath))
{
string contents = File.ReadAllText(viewPath);
var extracted = false;
var patterns = new[]
{
#"#Html\.\w+For\(\w+ => \w+\.(.*?)[,\)]",
#"#Html\.(\w+)For\(\w+ => \w+\.([\w\.]+)[, ]*(\(SelectList\))*(ViewBag\.\w+)*[^\)]*",
"name=\"(.*?)\""
};
for (var i = 0; i < 3 && !extracted; i++)
{
switch (i)
{
case 0:
form = contents.ExtractFields(patterns[0], Model as object, out extracted);
fields = contents.ExtractElements(patterns[1], Model as object, out extracted, ViewData);
break;
case 1:
form = Model as mvcApp.Models.Blog == null ? null : (Model.PostContent as string).ExtractFields(patterns[2], Model as object, out extracted);
break;
default:
form = Model;
break;
}
}
}
else if (Model == null)
{
// nothing to do here - safeModel will serialize to an empty object
}
else if (Model is IEnumerable)
{
form = new List<object>();
foreach (var element in ((IEnumerable) Model).AsQueryable()
.Cast<dynamic>())
{
form.Add(CustomExtensions.SafeClone(element));
}
} else {
form = Activator.CreateInstance(tModel);
CustomExtensions.CloneMatching(form, Model);
}
// remove any data models from the viewbag to prevent
// recursive serialization
foreach (var key in ViewData.Keys.ToArray())
{
var value = ViewData[key];
if (value is IEnumerable)
{
var enumerator = (value as IEnumerable).GetEnumerator();
value = enumerator.MoveNext() ? enumerator.Current : null;
}
if (value != null)
{
var vtype = value.GetType();
if (vtype.Namespace != null && (vtype.Namespace == "System.Data.Entity.DynamicProxies" || vtype.Namespace.EndsWith("Models")))
{
ViewData[key] = null;
}
}
}
result = new
{
uid = session.UniqueId,
form,
fields,
errors = messages.Count == 0 ? null : messages,
viewdata = ViewBag
};
var setting = new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.None,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
ContractResolver = new CamelCasePropertyNamesContractResolver(),
Formatting = Formatting.Indented
};
if (form is IEnumerable)
{
setting.NullValueHandling = NullValueHandling.Ignore;
}
serial = JsonConvert.SerializeObject(result, setting);
}
catch (Exception e)
{
result = new {
uid = session.UniqueId,
error = e.Message.Split('|')
};
serial = JsonConvert.SerializeObject(result);
}
#Html.Raw(serial)
}
For the clone methods see Best way to clone properties of disparate objects
public static dynamic ExtractFields(this string html, string pattern, object model, out bool extracted)
{
if (html == null || model == null)
{
extracted = false;
return null;
}
dynamic safeModel = new ExpandoObject();
var safeDict = (IDictionary<string, Object>)safeModel;
var matches = new Regex(pattern).Matches(html);
extracted = matches.Count > 0;
if ( extracted )
{
foreach (Match match in matches)
{
var name = match.Groups[1].Value;
var value = CustomExtensions.ValueForKey(model, name);
var segments = name.Split('.');
var obj = safeDict;
for (var i = 0; i < segments.Length; i++)
{
name = segments[i];
if (i == segments.Length - 1)
{
if (obj.ContainsKey(name))
{
obj[name] = value;
}
else
{
obj.Add(name, value);
}
continue;
}
if (!obj.ContainsKey(name))
{
obj.Add(name, new ExpandoObject());
}
obj = (IDictionary<string, Object>)obj[name];
}
}
}
return safeModel;
}
And here is an implementation of key value coding to make dealing with property chains a bit easier:
/// <summary>
/// This borrows KeyValueCoding from Objective-C and makes working with long chains of properties more convenient.
/// KeyValueCoding is null tolerant, and will stop if any element in the chain returns null instead of throwing a NullReferenceException.
/// Additionally, the following Linq methods are supported: First, Last, Sum & Average.
/// <br/>
/// KeyValueCoding flattens nested enumerable types, but will only aggregate the last element: "children.grandchildren.first" will return
/// the first grandchild for each child. If you want to return a single grandchild, use "first.children.grandchildren". The same applies to
/// Sum and Average.
/// </summary>
/// <param name="source">any object</param>
/// <param name="keyPath">the path to a descendant property or method "child.grandchild.greatgrandchild".</param>
/// <param name="throwErrors">optional - defaults to supressing errors</param>
/// <returns>returns the specified descendant. If intermediate properties are IEnumerable (Lists, Arrays, Collections), the result *should be* IEnumerable</returns>
public static object ValueForKey(this object source, string keyPath, bool throwErrors = false)
{
try
{
while (true)
{
if (source == null || keyPath == null) return null;
if (keyPath == "") return source;
var segments = keyPath.Split('.');
var type = source.GetType();
var first = segments.First();
var property = type.GetProperty(first);
object value = null;
if (property == null)
{
var method = type.GetMethod(first);
if (method != null)
{
value = method.Invoke(source, null);
}
}
else
{
value = property.GetValue(source, null);
}
if (segments.Length == 1) return value;
var children = string.Join(".", segments.Skip(1));
if (value is IEnumerable || "First|Last|Sum|Average".IndexOf(first, StringComparison.OrdinalIgnoreCase) > -1)
{
var firstChild = children.Split('.').First();
var grandchildren = string.Join(".", children.Split('.').Skip(1));
if (value == null) {
var childValue = source.ValueForKey(children);
value = childValue as IEnumerable<object>;
switch (first.Proper())
{
case "First":
return value == null ? childValue : ((IEnumerable<object>)value).FirstOrDefault();
case "Last":
return value == null ? childValue : ((IEnumerable<object>)value).LastOrDefault();
case "Count":
return value == null ? (childValue == null ? 0 : 1) : (int?)((IEnumerable<object>)value).Count();
case "Sum":
return value == null
? Convert.ToDecimal(childValue ?? "0")
: ((IEnumerable<object>) value).Sum(obj => Convert.ToDecimal(obj ?? "0"));
case "Average":
return value == null
? Convert.ToDecimal(childValue ?? "0")
: ((IEnumerable<object>) value).Average(obj => Convert.ToDecimal(obj ?? "0"));
}
} else {
switch (firstChild.Proper())
{
case "First":
return ((IEnumerable<object>)value).FirstOrDefault().ValueForKey(grandchildren);
case "Last":
return ((IEnumerable<object>)value).LastOrDefault().ValueForKey(grandchildren);
case "Count":
if (!string.IsNullOrWhiteSpace(grandchildren))
{
value = value.ValueForKey(grandchildren);
if (value != null && ! (value is IEnumerable<object>))
{
return 1;
}
}
return value == null ? 0 : ((IEnumerable<object>)value).Count();
case "Sum":
return ((IEnumerable<object>)value).Sum(obj => Convert.ToDecimal(obj.ValueForKey(grandchildren)??"0"));
case "Average":
return ((IEnumerable<object>)value).Average(obj => Convert.ToDecimal(obj.ValueForKey(grandchildren) ?? "0"));
}
}
if (value == null) return null;
var flat = new List<object>();
foreach (var element in (IEnumerable<object>)value)
{
var child = element.ValueForKey(children);
if (child == null)
{
continue;
}
if (child is IEnumerable && !(child is string))
{
flat.AddRange((IEnumerable<object>) child);
}
else
{
flat.Add(child);
}
}
return flat.Count == 0? null: flat;
}
source = value;
keyPath = children;
}
}
catch (Exception)
{
if (throwErrors) throw;
}
return null;
}

Resources