MVC3 Html.BeginForm() with additional HTML - asp.net-mvc-3

I am writing some code where I would like render a MVC BeginForm but to also include additional HTML.
For eg.
#using (Html.BeginMyCustomForm()) {
}
And this spits out
<form action="" etc>
PLUS
<input type="hidden" name="my-additional-field-i-always-want">
#Html.ValidationMessageFor(m => m)
Is this possible?
I know I'll be looking at some kind of extension or something most likely.
Hope this makes sense and thanks in advance!

It's not a good idea to include such things in an extension to BeginForm. The reason is that good design dictates that objects should do one thing. This is called the Single Responsibility Principle.
You now want your form object to also create inputs and validation.
Instead, use the MVC templating system and create a default template to use for your needs. You should also use Html.HiddenFor rather than using an input tag, unless there is some specific reason not to.
Another option is to use MVC Scaffolding to generate what you want.
More about Editor Templates http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-1-introduction.html
More about Scaffolding http://blog.stevensanderson.com/2011/01/13/scaffold-your-aspnet-mvc-3-project-with-the-mvcscaffolding-package/

Maybe you can use something like this?
public static class FormExtensions
{
public static MvcForm BeginMyCustomForm(this HtmlHelper htmlHelper)
{
var form = htmlHelper.BeginForm();
htmlHelper.ViewContext.Writer.write( .... )
return form;
}
}

Related

What replaced ClientScriptManager in MVC?

I'm converting an ASP.NET Forms app (that I didn't write) to an MVC 3 app. The ClientScriptManager is used in the old app. ClientScriptManager doesn't exist in MVC 3. What replaced ClientScriptManager? I've done enough reading to know that CSM has something to do with AJAX functionality in ASP.NET; it registers "sets" of js scripts and functions somewhat akin to how EditorTemplates work for sets of similar controls. I'm not necessarily looking to implement a CSM in MVC; I just want to know what practices Microsoft put in place that rendered CSM obsolete.
ASP.Net MVC was designed to give you complete control over the HTML and js, rather than having the framework render these for you, as with ASP.Net. So, nothing really "replaces" the ClientScriptManager. As a result, it is entirely up to you how you handle your js.
You can do something simple like include <script ...> tags to reference inline script or js files in your _Layout.cshtml file, or you could use some sort of JavaScript Loader like RequireJS or come up with your own scheme entirely using "Html Helpers" or something.
MVC 4 introduced the concept of bundling which lets you define collections of scripts as "bundles" and have MVC automatically minify and merge them together for you when you reference them in your Views like this :
#Scripts.Render("~/bundles/jquery")
Below is an example for rendering the JavaScript(Function) from Controller.
Controller
public ActionResult Index(int? id)
{
ViewBag.MyAlert = "<script type='text/javascript'>MyAlert();</script>";
}
View
<script src="Your Path" type="text/javascript"></script>
#Html.Raw(ViewBag.MyAlert)
JS
function MyAlert() {
alert('Hi');
}
Below is an example for rendering the JavaScript(File) from Controller.
protected override void OnActionExecuted(ActionExecutedContext filterContext)
{
StringBuilder sb = new StringBuilder();
sb.Append("<script src='Your Path' type='text/javascript'></script>");
filterContext.HttpContext.Response.Write(sb.ToString());
}
So using this approach you do not need to mentioned following code in View.
<script src="Your Path" type="text/javascript"></script>

Binding the MVC Form Helper to a Bootstrap modal

Working with a series of MVC-driven forms where I would like to leverage the conventional '#using (Html.BeginForm(...' method of generating client-side forms that will be rendered inside Bootstrap-based modals. I've found a technique that binds the modal to an ajax request/response scheme where the form is defined thusly:
<form class="form-horizontal well" data-async data-target="#rating-modal" action="/some-endpoint" method="POST">
I understand how to insert the class attributes ( new { #class="example"} ) but I haven't found samples that show me how to deal with the 'data-async' and 'data-target=xxx'.
Wondering if Darin Dimitrov's answer to this question is partial answer to my need. Or perhaps I need to better understand EditorTemplates as suggested here.
Just as you do with a class:
new { #class="example"}
For a data-atribute can do:
new { #class="example", data_target="#rating-modal"}
data_target output data-target when razor interpret the htmlAttributes parameter

Razor #if inside <script> is rejected by VS11

Is the following markup incorrect, or is it a bug in the Razor engine in MVC4? Either way, what's the best workaround?
The entire content of View1.cshtml is shown. It compiles fine, but the Intellisense error highlighting is annoying. (Included as an image so you can see how the syntax is rejected by VS11.)
Hote that the #: syntax (instead of <text></text>) isn't accepted either.
It's a little tricky to mix JavaScript with Razor. Personally, I find it easier to always separate these two guys by using either Razor #function or Razor custom #helper.
Here's a simple example:
<script type="text/javascript">
function btn_clicked() {
#generateMyJavaScript()
}
</script>
<a href="javascript:void(0)" onclick='btn_clicked()'>Click on me</a>
#functions {
private MvcHtmlString generateMyJavaScript()
{
string jsBuilder = string.Empty;
if (true)
jsBuilder = "alert('Hey!');";
return MvcHtmlString.Create(jsBuilder);
}
}
I noticed strange behavior of the syntax highlighter too. This is not really an answer but:
I try staying away from mixing model data into javascript. Although powerfull it is a bit spaghetti-like.
Also I try to have as little as possible tags in the html. I use js includes and jQuery $(document).ready(function () ... functions inside those.
There you can extract the JSON data from the data attributes and base javascript decisions on those.
(Wanted to write this as a comment but didn't find the way to do that, sorry for the debump)

What is ColdFusion Model Glue's equivalent to ASP.NET MVC 3's #section?

In ASP.NET MVC 3, you can have an #section within a view:
#section SideBar {
<p>Some content</p>
<p>Some more content</p>
}
<p>Body content</p>
Then in the master view, you would use this to render it:
<div id="sidebar">
#RenderSection("SideBar", false)
</div>
#RenderBody()
What would be the ColdFusion equivalent of this in the Model Glue framework? I know I can set simple variables in the view:
<cfset event.setValue("section", "Tables")>
Then use them in the master template like so:
<cfif event.exists("section")><h3>#event.getValue("section")#</h3></cfif>
But this only works well for one-liners and simple strings. What I'd like to do is include an entire HTML block. What is the best way of accomplishing this? I think this would work in theory:
<cfsavecontent variable="sidebar">
<p>Some content</p>
<p>Some more content</p>
</cfsavecontent>
<cfset event.setValue("sidebar", sidebar)>
But I was wondering if there's a better way of doing it.
Edit:
In response to Adam Cameron's answer, Model Glue, from what I can tell, only supports the ability to combine separate files into one template:
SideBar.cfm:
<p>Some content</p>
<p>Some more content</p>
Page.cfm:
<p>Body content</p>
ModelGlue.xml:
<event-handler name="page.text">
<views>
<include name="sidebar" template="SideBar.cfm"/>
<include name="body" template="Page.cfm"/>
<include name="main" template="main.cfm"/>
</views>
</event-handler>
main.cfm:
<cfoutput>#viewCollection.getView("sidebar")#</cfoutput>
<cfoutput>#viewCollection.getView("body")#</cfoutput>
I need to be able to declare the sidebar content within the page.cfm view. The thought here is that there will be a div somewhere in the main template that allows for a small HTML snippet, say an image with a text description and a link, which any view can populate. It wouldn't make sense to have something like Page1.cfm and Page1SidebarContent.cfm, Page2.cfm and Page2SidebarContent.cfm, etc...
ModelGlue doesn't support what you want to do out of the box. However its easy enough to achieve using Peter's suggestion and ModelGlue helpers for encapsulation.
Create a new cfc, call it PageFragment.cfc and drop it in to your ModelGlue helpers directory.
// untested!
component name="PageFragment" {
public boolean function exists(string name) {
return structkeyexists(request.subcontent, arguments.name);
}
public string function get(string name) {
if(exists(arguments.name)) return request.subcontent[arguments.name];
return "";
}
public void function set(string name, string value) {
request.subcontent[arguments.name] = arguments.value;
}
}
Then in your views you can do
index.cfm
<cfset helpers.PageFragment.set("sidebar", "<p>My sidebar content</p>") />
main.cfm
<cfif helpers.PageFragment.exists("sidebar")>
<div id="sidebar">#helpers.PageFragment.get("sidebar")#</div>
</cfif>
To avoid having to cfsavecontent all your fragments you could create a customtag that used thistag.generatedcontent and the caller scope to access your helpers.
By using helpers to encapsulate the functionality its really easy to reuse, or to change later without altering your views, for example you may want to add caching.
(Unfortunately) I haven't touched MG for ages, but I just googled the docs, as a reminder.
You need to read up on how views work, but this page of the docs summarises it succinctly:
http://docs.model-glue.com/wiki/ReferenceMaterials/ViewApi#ViewAPI
Specifically this code snippet:
<cfoutput>#viewcollection.getView("body")#</cfoutput>
It's probably a case of reading through the docs a bit, and reminding yourself about how model glue's implementation of MVC (specifically the V part, in your case!) works.
I haven't used Model-Glue nor ASP.NET MVC, but it seems what you want can be achieved like this:
In page.cfm do:
<cfsavecontent variable="Request.SubContent['ThisPage'].Sidebar">
<p>Some content</p>
<p>Some more content</p>
</cfsavecontent>
<p>Body content</p>
Then in main.cfm use:
<div id="sidebar">
<cfif StructKeyExists(Request.SubContent,PageName)
AND StructKeyExists(Request.SubContent[PageName],'Sidebar')
>
#Request.SubContent[PageName].Sidebar#
<cfelse>
#viewCollection.getView("default_sidebar")#
</cfif>
</div>
<cfoutput>#viewCollection.getView("body")#</cfoutput>
Depending on how things are structured, you might prefer to cache content in a persistent scope and/or hide it behind a couple of methods (possibly even extending Model-Glue to allow this natively; it is Open Source after all), but hopefully this gives a general idea?
I see what you mean now. I'm not sure MG will have that sort of functionality built-in because in an MVC environment, it's not really up to a view to be doing this sort of thing: you're kinda coupling controller & model stuff together in a view file. There might be a good reason for you doing this, but I wonder if it's your approach that might be your undoing here? Can you not put the "get the sidebar" stuff into the controller & have that call a model, and add in a sidebar view if needed? That'd be how I'd approach this.
That said, I know it's unhelpful having people say "I'm not going to answer your question, I'm just going to complain about it", so I'll have a look-see around and see if I can come up with something.
However given you're wanting to break-out of a MVC approach here, perhaps don't try to get MG to do this for you, just do what Peter suggests and capture a variable in the page.cfm view, stick it in a sensibly-structured struct (nice tautology, Cameron), and then look for it in the view you want to render it.

Razor Nested Layouts with Cascading Sections

I have an MVC3 site using Razor as its view engine. I want my site to be skinnable. Most of the possible skins are similar enough that they can derive from a shared master layout.
Therefore, I am considering this design:
However, I would like to be able to call RenderSection in the bottom layer, _Common.cshtml, and have it render a section that is defined in the top layer, Detail.cshtml. This doesn't work: RenderSection apparently only renders sections that are defined the next layer up.
Of course, I can define each section in each skin. For instance, if _Common needs to call RenderSection("hd") for a section defined in Detail, I just place this in each _Skin and it works:
#section hd {
#RenderSection("hd")
}
This results in some duplication of code (since each skin must now have this same section) and generally feels messy. I'm still new to Razor, and it seems like I might be missing something obvious.
When debugging, I can see the complete list of defined sections in WebViewPage.SectionWritersStack. If I could just tell RenderSection to look through the entire list before giving up, it would find the section I need. Alas, SectionWritersStack is non-public.
Alternatively, if I could access the hierarchy of layout pages and attempt execution of RenderSection in each different context, I could locate the section I need. I'm probably missing something, but I don't see any way to do this.
Is there some way to accomplish this goal, other than the method I've already outlined?
This is in fact not possible today using the public API (other than using the section redefinition approach). You might have some luck using private reflection but that of course is a fragile approach. We will look into making this scenario easier in the next version of Razor.
In the meantime, here's a couple of blog posts I've written on the subject:
http://blogs.msdn.com/b/marcinon/archive/2010/12/08/optional-razor-sections-with-default-content.aspx
http://blogs.msdn.com/b/marcinon/archive/2010/12/15/razor-nested-layouts-and-redefined-sections.aspx
#helper ForwardSection( string section )
{
if (IsSectionDefined(section))
{
DefineSection(section, () => Write(RenderSection(section)));
}
}
Would this do the job ?
I'm not sure if this is possible in MVC 3 but in MVC 5 I am able to successfully do this using the following trick:
In ~/Views/Shared/_Common.cshtml write your common HTML code like:
<!DOCTYPE html>
<html lang="fa">
<head>
<title>Skinnable - #ViewBag.Title</title>
</head>
<body>
#RenderBody()
</body>
</html>
In ~/Views/_ViewStart.cshtml:
#{
Layout = "~/Views/Shared/_Common.cshtml";
}
Now all you have to do is to use the _Common.cshtml as the Layout for all the skins. For instance, in ~/Views/Shared/Skin1.cshtml:
#{
Layout = "~/Views/Shared/_Common.cshtml";
}
<p>Something specific to Skin1</p>
#RenderBody()
Now you can set the skin as your layout in controller or view based on your criteria. For example:
public ActionResult Index()
{
//....
if (user.SelectedSkin == Skins.Skin1)
return View("ViewName", "Skin1", model);
}
If you run the code above you should get a HTML page with both the content of Skin1.cshtml and _Common.cshtml
In short, you'll set the layout for the (skin) layout page.
Not sure if this will help you, but I wrote some extension methods to help "bubble up" sections from within partials, which should work for nested layouts as well.
Injecting content into specific sections from a partial view ASP.NET MVC 3 with Razor View Engine
Declare in child layout/view/partial
#using (Html.Delayed()) {
<b>show me multiple times, #Model.Whatever</b>
}
Render in any parent
#Html.RenderDelayed();
See the answer link for more use-cases, like only rendering one delayed block even if declared in a repeating view, rendering specific delayed blocks, etc.

Resources