Multiple if statements in an html attribute - MVC razor - asp.net-mvc-3

If I wanted multiple if statements in an html attribute I might do something like this:
<input type="button" value="Bad, the title has a lot of excess spacing" title="#if(SomeModel.condOne) {
<text>this</text>
}
#if (SomeModel.CondTwo)
{
<text> is</text>
}
#if (SomeModel.CondThree)
{
<text> a title</text>
}
" />
But that creates a lot of empty spaces that need truncating. So this works:
<input type="button" value="Good, the title is condenced" title="#if(SomeModel.condOne) {<text>this</text>}#if (SomeModel.CondTwo){<text> is</text>}#if (SomeModel.CondThree){<text> a title</text>}" />
The same principle can be applied to an element with multiple classes (e.g. class="oddrow class1" -> class="evenrow class2")
But that might be hard to read if it's a long line. And visual studio has a habit of breaking that statement into multiple lines if you touch the bracket or Ctrl-K,Ctrl-D (which any next developer is likely to do).
Is there a better or more fullproof way to implement multiple attribute conditions in a line for MVC razor?

I suggest creating a small helpers method that returns the text you need.
You'd have to pass it SomeModel and inside that method check for your condition that way you'd have something nicer to look at and easier to maintain.
For example:
public static class HtmlHelpers
{
public static string FetchTitle(this HtmlHelper helper, SomeModel model)
{
//Your logic here.
}
}
You can read all about Html Helper methods here on Jon Galloway's blog.
That's where I learned how to use them.

Why not just do it as:
title="#if(SomeModel.condOne) { <text>this</text> }
#if (SomeModel.CondTwo) { <text> is</text> }
#if (SomeModel.CondThree) { <text> a title</text> }
" />
Helper makes sense if you do use this same logic alot, especially assuming the same model, but you may want to also consider a helper that uses a Func<> expression or Action<> expression. This way, it wouldn't be tied to one model.

Related

ASP.NET MVC3 HtmlHelper extension method like BeginForm that uses a partial view?

I created an extension method based on this answer to the SO question c# - How can I create a Html Helper like Html.BeginForm - Stack Overflow and it works fine.
Can I move the embedded HTML in the extension method into a partial view and use that partial view in the method while preserving it's current behavior? In particular, I want to be able to 'wrap' a block of arbitrary HTML.
I ask not out of any pressing need, but simply out of a desire to maintain HTML consistently, e.g. as views and partial views. I imagine it will be a lot easier to spot any problems with the HTML if it's in a view or partial view too.
Here's the HtmlHelper extension method:
public static IDisposable HelpTextMessage(this HtmlHelper helper, bool isHidden, string heading)
{
TextWriter writer = helper.ViewContext.Writer;
writer.WriteLine(
String.Format(
"<div class=\"help-text {0}\">",
isHidden ? "help-text-hidden" : ""));
writer.WriteLine(
String.Format(
"<div class=\"help-text-heading\">{0}</div>",
heading));
writer.Write("<div class=\"help-text-body\">");
return new HelpTextMessageContainer(writer);
}
Here's the HelpTextMessageContainer class:
private class HelpTextMessageContainer : IDisposable
{
private readonly TextWriter _writer;
public HelpTextMessageContainer(TextWriter writer)
{
_writer = writer;
}
public void Dispose()
{
_writer.Write("</div></div>");
}
}
In a view, I can use the extension method like this:
#using(Html.HelpTextMessage(Model.HelpText.IsHelpTextHidden(Model.SomeHelpMessage), "Something"))
{
#:To do something, first do something-more-specific, then do another-something-more-specific.
}
Or I could use it like this too:
#using(Html.HelpTextMessage(Model.HelpText.IsHelpTextHidden(Model.SomeHelpMessage), "Something"))
{
<p>To do something, first do something-more-specific, then do another-something-more-specific.</p>
<p>Also, keep in mind that you might need to do something-else-entirely if blah-blah-blah.</p>
}
I haven't found any way to move the "embedded HTML" into a partial view exactly, but a slightly more-friendly way to encapsulate the HTML in a way that provides HTML and Razor syntax highlighting and Intellisense is to move into a view helper function in a file under the app App_Code folder as described in this post on "Hugo Bonacci's Blog".
Here's my helper function:
#helper HelpTextMessage(bool isHidden, string heading, Func<object, object> content)
{
<div class="help-text #(isHidden ? "help-text-hidden" : "")">
<div class="help-text-heading">
#heading
</div>
<div class="help-text-body">
#content(null)
</div>
</div>
}
Assuming the above helper function is in a file named ViewHelpers.cshtml in the App_Code folder, it can be called in a view like this:
#ViewHelpers.HelpTextMessage(false, "Something",
#:To do something, first do something-more-specific, then do another-something-more-specific.
)
or this:
#ViewHelpers.HelpTextMessage(false, "Something",
<p>To do something, first do something-more-specific, then do another-something-more-specific.</p>
<p>Also, keep in mind that you might need to do something-else-entirely if blah-blah-blah.</p>
)
I like having the embedded HTML in a view more than I do being able to use the #using(Html.HelpTextMessage(...){ ... } syntax, so I'm pretty happy with this as a solution.

MVC 3 Razor, Helpers with custom markup/section

I'm not even sure if this is possible, but I thought I would check to see if there is any way to make this easier.
First, I have some repeated markup in my site that looks like this:
<div class="module">
<h3>Title</h3>
<div>
<p>Information goes here</p>
</div>
</div>
What I want to do is wrap this up in some kind of helper/section so that I could do something like this
#MyHelper("This is my Title") {
<p>Here is my custom markup</p>
}
Then, when it renders, it would inject the title passed in through the parameter between the <h3></h3> and the custom markup in the divs. The custom markup could be anything from test, to form controls, to a partial view. Is this something that is possible?
There's also the other way, without disposable trick, which also requires a little less work, great for little helpers.
#helper MyHelper(string title, Func<object, object> markup) {
<div class="module">
<h3>Title</h3>
<div>
<p>#markup.DynamicInvoke(this.ViewContext)</p>
</div>
</div>
}
Usage of this helper looks like this:
#MyHelper("This is my Title",
#<p>Here is my custom markup</p>
)
Or with multiple lines:
#MyHelper("This is my Title",
#<text>
<p>More than one line</p>
<p>Of markup</p>
</text>
)
Telerik MVC controls used this trick for example to let you add your javascript code at the document load.
Here's also a nice example. There's also some information here.
Well, here's a "standard" way of doing something close to it, using an HTML helper extension. A very simple version of what Html.BeginForm() does.
Approach: Simply return an IDisposable, and let the using statement take care of the rest.
This is just an example of the concept (although it works). Not intended for immediate reuse. Written quickly with lots of shortcuts, not production code, plenty of opportunities for improvement and optimization, may have silly mistakes, could use TagBuilder etc. etc. Could easily be modified to reuse the Wrapper class for different... wrappings (there may even be a generic one already in ASP.NET MVC - haven't had a need for one).
public static class WrapHelper
{
private class Wrapper : IDisposable
{
private bool disposed;
private readonly TextWriter writer;
public Wrapper(HtmlHelper html)
{
this.writer = html.ViewContext.Writer;
}
public void Dispose()
{
if (disposed) return;
disposed = true;
writer.WriteLine(" </div>");
writer.WriteLine("</div>");
}
}
public static IDisposable Wrap(this HtmlHelper html, string title)
{
TextWriter writer = html.ViewContext.Writer;
writer.WriteLine("<div class=\"module\">");
writer.WriteLine(" <h3>" + html.Encode(title) + "</h3>");
writer.WriteLine(" <div>");
return new Wrapper(html);
}
}
Usage:
#using (Html.Wrap("Title"))
{
<p>My very own markup.</p>
}
#TheKaneda, Thanks for the insight. I took your idea and extended it, such that you supply a PartialView name and it knows how to parse it.
<Extension()> _
Public Function UseTemplate(ByVal html As HtmlHelper, ByVal PartialView As String) As IDisposable
Return New TemplateWrapper(html, PartialView)
End Function
Public Class TemplateWrapper
Implements IDisposable
Private _HtmlHelper As HtmlHelper
'Index 0 is header
'Index 1 is footer
Private _TemplateWrapper As String()
Public Sub New(ByVal Html As HtmlHelper, ByVal PartialView As String)
_TemplateWrapper = Html.Partial(PartialView).ToHtmlString.Split("##RenderBody()")
_HtmlHelper = Html
_HtmlHelper.ViewContext.Writer.Write(_TemplateWrapper(0))
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
_HtmlHelper.ViewContext.Writer.Write(_TemplateWrapper(1).Substring(12))
End Sub
End Class
Use the same usage as #TheKaneda's example. In your partial view, instead of calling #RenderBody(), just put ##RenderBody() which acts as a flag for the middle part of your content. Sorry for the VB translation.
Uses an example of my usage.
Using Html.UseTemplate("ContentWrapper")
#Html.EditorFor(Function(m) m.Var1, "TemplateHint")
#Html.EditorFor(Function(m) m.Var2, "TemplateHint")
#Html.EditorFor(Function(m) m.Var3)
End Using
My Partial looks like this...
<div class="content">
##RenderBody()
</div>
If you are using Razor and MVC 3 it's real easy to write a quick helper method that would go inside the app_code folder, (I'll name it MyHtmlHelpers)
I'd try something a little different and a little easier such as:
#helper myTemplate(string title, string markup){
<div class="module">
<h3>#title</h3>
#markup
</div>
}
And the way you use it from a .cshtml file is as followed:
#MyHtmlHelpers.myTemplate('title','markup')
It should work, if I understand you correctly.

MVC Separation of Concerns

so I was going on creating my MVC 3 web app when it dawned on me that I might be putting entirely too much logic in my Controller, that it needs to be in the Model instead. The problem with this is, that in this particular instance I'm dealing with a file.
The SQL database table stores the path of the file, and the file itself is saved in a directory. So in the database, the file path is stored as an nvarchar, and in the model, the file is a string, everything's consistent to that point. The issue comes when it's time to upload the file, at that point I'm dealing with a System.IO.File.
So the question is, how do you provide System.IO.File logic inside the model for the file when in the back-end it is actually a string?
I had finished the functional version of the Controller and had some logic already in it, and was about to add more when I realized that I was working against the system. What I mean is that in order to have server-side validation, the logic needs to be in the Model in order for the input validation to behave and work according to proper MVC rules, obviously optionally using client-side validation in conjunction.
Currently...
Here is my View:
#model ProDevPortMVC3.Profile
#{
ViewBag.Title = "Profile Photo Upload";
}
<h2>Photo Upload</h2>
<img alt="Profile Image" src="#Html.DisplayFor(model => model.ProfilePhotoPath)" />
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
#using (Html.BeginForm("UploadPhoto", "Profile", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.ValidationSummary(true)
<br />
<input type="file" name="File1" />
#Html.ValidationMessageFor(model => model.ProfilePhotoPath)
<input type="submit" value="Upload" />
}
Here is my Controller (just the relevant action method):
[HttpPost]
public ActionResult UploadPhoto(int id, FormCollection form)
{
Profile profile = db.Profiles.Find(id);
var file = Request.Files[0];
if (file != null && file.ContentLength > 0)
{
try
{
string newFile = Path.GetFileName(file.FileName);
file.SaveAs(Server.MapPath("/Content/users/" + User.Identity.Name + "/" + newFile));
profile.ProfilePhotoPath = "/Content/users/" + User.Identity.Name + "/" + newFile;
UpdateModel(profile);
db.SaveChanges();
return RedirectToAction("Index");
}
catch
{
return View();
}
}
return View();
}
And here is my Model (just the part relevant to the file):
public string ProfilePhotoPath { get; set; }
So I guess, what are your guys' solutions in these particular situations?
Description
Assuming i understand your question. I have read your question a few times. ;) If i don't understand, please comment my answer in order to get a better answer (i will update)
I think that you want is.. How to Model Validation for your particular case.
You can add Model Validation errors using the ModelState.AddModelError("Key", "Message) method.
ModelState.AddModelError Adds a model error to the errors collection for the model-state dictionary.
Sample
ModelState.AddModelError("ProfilePhotoName", "YourMessage");
This will affect ModelState.IsValid
So you can do whatever you want (your logic) and can make your Model invalid.
More Information
MSDN - ModelStateDictionary.AddModelError Method
There are any number of answers to this question. I'll take a crack at it knowing the risk going in due to varying opinion. In my personal experience with MVC3 I like to use flatter, simpler Models. If there is validation that can be done easily in a few lines of code that doesn't require external dependencies then I'll do those in the Model. I don't feel like your System.IO logic is validation, per se. Validation that could go in the Model, in my mind, is whether the filename is zero length or not. The logic to save is something you can put in your controller. Better yet, you could inject that logic using the Inversion of Controller pattern and specifically a Dependency Injection solution.

MVC3 Razor and simulating same-page sections

Razor doesn't support same-page sections, so I can't do something like this:
#if (wrapSection)
{
<div class="section-wrapped-in-div">
#RenderSection("mySection")
</div>
}
else
{
#RenderSection("mySection")
}
#section mySection
{
some stuff here...
}
I know I can accomplish this with a partial view, but this is specific to this page, and really would be best kept on the same page.
Is something like this possible?
You should make a helper method:
#helper MySection(...) {
...
}
#MySection(...)
Unlike sections, helpers can also take parameters.

Rewriting Html.BeginForm() in MVC 3.0 and keeping unobtrusive javascript

This is going to seem like a bit of a silly endeavor, but it's something I want to learn nonetheless.
Right now, in ASP.NET MVC 3.0, you need to use the syntax #using (Html.BeginForm()) { and then later, the } to close a form block to get the fancy new 'unobtrusive javascript', lest you want to write all of it by hand (which is fine).
For some reason (Read: *OCD*) I don't like that. I'd really rather do this..
#Html.BeginForm()
<div class="happy-css">
</div>
#Html.EndForm()
Seem stupid yet? Yeah, well to each their own. I want to understand why it is working how it is and mold it to my liking. So I thought the first place I would start digging is the MVC 3.0 source itself. So I jumped into codeplex to find the BeginForm Extension method.
( http://aspnet.codeplex.com/SourceControl/changeset/view/63452#288009 )
So now I am a little confused as to how to begin achieving my goal. Reading through the code, I discovered that they all go down to a root method (not surprising, as most extension methods seem to be hierarchical methods all reaching down into a single one to avoid redundancy).
private static MvcForm FormHelper(this HtmlHelper htmlHelper, string formAction, FormMethod method, IDictionary<string, object> htmlAttributes) {
TagBuilder tagBuilder = new TagBuilder("form");
tagBuilder.MergeAttributes(htmlAttributes);
// action is implicitly generated, so htmlAttributes take precedence.
tagBuilder.MergeAttribute("action", formAction);
// method is an explicit parameter, so it takes precedence over the htmlAttributes.
tagBuilder.MergeAttribute("method", HtmlHelper.GetFormMethodString(method), true);
HttpResponseBase httpResponse = htmlHelper.ViewContext.HttpContext.Response;
httpResponse.Write(tagBuilder.ToString(TagRenderMode.StartTag));
return new MvcForm(htmlHelper.ViewContext.HttpContext.Response);
}
What I am not seeing here is how this method relates to the unobtrusive javascript. If I simply type out ..
<form action="/Controller/Action" method="post">
and then put in my validation like so...
#Html.ValidationSummary(false)
I do not get the unobtrusive javascript. But if I use
#using (Html.BeginForm()) { then I do. I've even examined the generated markup and I really can't find the difference.
Now then it gets strange. If I just type in ...
#Html.BeginForm() and then put all of my form code, the form works and I get the unobtrusive javascript, but I have to manually type in </form> at the end. #Html.EndForm() doesn't work. But ontop of that, I now get the text System.Web.Mvc.Html.MvcForm written to the output stream right beneath the <form action="/Controller/Action" method="post"> html.
Can someone enlighten and/or help me?
The answer to your underlying question (i.e. how to use BeginForm/EndForm syntax) is to do it in the following manner:
#{ Html.BeginForm(...); }
<div> content</div>
#{ Html.EndForm(); }
Unfortunately the Razor syntax right now is a bit more verbose when invoking helpers that write to the output (as opposed to the majority of helpers which just return an html snippet). You could probably make this easier by writing your own extension methods like so:
public static IHtmlString FormBegin(this HtmlHelper helper, ...) {
helper.BeginForm(...);
return new HtmlString("");
}
public static IHtmlString FormEnd(this HtmlHelper helper) {
helper.EndForm();
return new HtmlString("");
}
The reason it works I believe is that the BeginForm method returns an MvcForm object and not html, it writes the html directly to the page. When the using block ends it is disposed and it writes the closing end tag. That's the reason you see the text System.Web.Mvc.Html.MvcForm appear in your output. You have to put the closing tag in there manually because the MvcForm object isn't disposed.
The using syntax is like doing this:
#{ MvcForm mf = Html.BeginForm(); /* writes directly to html stream */ }
<div class="happy-css">
</div>
#{ mf.Dispose(); /* like calling mf.EndForm() */ }

Resources