Creating Some thing #using (Html.BeginForm()) Helpers - asp.net-mvc

In Asp.net MVC3 when you write below code , it generates wrapping html itself
#using (Html.BeginForm()) {
#Html.ValidationMessageFor(model => model.Text)
}
It generates codes in below format,
<form method="post" action="/Feeds">
<!-- Fields Here -->
</form>
My question in #using (Html.BeginForm()) automatically adds <form> tag at beginning and end, how can i create something like that of my own.
I am looking for some thing like below
#using (Html.BeginMYCUSTOMDIV())
{
I am text inside div
}
Expected Generated Output
<div class="customDivClass">
I am text inside div
</div>

Something along the lines:
public class MyDiv : IDisposable
{
private readonly TextWriter _writer;
public MyDiv(TextWriter writer)
{
_writer = writer;
}
public void Dispose()
{
_writer.WriteLine("</div>");
}
}
public static class MyExtensions
{
public static MyDiv BeginMYCUSTOMDIV(this HtmlHelper htmlHelper)
{
var div = new TagBuilder("div");
div.AddCssClass("customDivClass");
htmlHelper.ViewContext.Writer.WriteLine(div.ToString(TagRenderMode.StartTag));
return new MyDiv(htmlHelper.ViewContext.Writer);
}
}
and in the view:
#using (Html.BeginMYCUSTOMDIV())
{
<span>Hello</span>
}
generates:
<div class="customDivClass">
<span>Hello</span>
</div>

If I'm not mistaken, Html.BeginForm() returns an IDisposable object. When used in the using block, the object's Disposemethod is called, which is the responsible to write the closing tag to the output.
how does using HtmlHelper.BeginForm() work?
Html.BeginForm() type of extension

Related

MVC BeginCollectionItem

I'm having some issue getting my partial view BeginCollectionItem to save to the database. I have a form which has a dynamic number of "sections" that can be added to the page, and within each of these fields there is a text box where the user can enter the section name.
As far as I can tell the BeginCollectionItem within the partial view is working properly, however I cannot post the info to the database. In my other forms I have used a [bind()] to send the data to the database, is it possible to get this into a list and then post that via a bind?
I've included my code below:
The Model:
namespace project.Models.SetupViewModels
{
public class SOPTopTemplateBuilderViewModel
{
public List<Section> Section { get; set; }
}
public class Section {
public int SectionId { get; set; }
public string SectionText { get; set; }
public string TopTempId { get; set; }
}
}
cshtml:
#model IEnumerable<project.Models.SetupViewModels.Section>
#using (Html.BeginForm("SOPTopTemplateBuilder", "Setup", FormMethod.Post))
{
<div class="main-holder" id="theform">
#foreach(var item in Model)
{
#Html.Partial("_SectionCreator", item)
}
</div>
<button id="add" type="button">Add</button>
<div class="form-group submit-row">
<div class="col-12 float-to-right">
<input type="submit" class="btn btn-default" value="continue" />
</div>
</div>
}
#section Scripts {
<script>
$(document).ready(function () {
var url = '#Url.Action("AddSection")';
var form = $('form');
var recipients = $('#theform');
$('#add').click(function() {
$.post(url, function(response) {
recipients.append(response);
// Reparse the validator for client side validation
form.data('validator', null);
$.validator.unobtrusive.parse(form);
});
});
});
</script>
}
Partial View:
#model project.Models.SetupViewModels.Section
#using HtmlHelpers.BeginCollectionItemCore
#using (Html.BeginCollectionItem("Section"))
{
<div class="new-section">
<div>
<p>New Section</p>
#Html.HiddenFor(m => m.SectionId, new { #class="id" })
#Html.EditorFor(m => m.SectionText, new { #class = "form-control limit-form"})
</div>
</div>
}
Controller:
[HttpPost]
public PartialViewResult AddSection()
{
return PartialView("_SectionCreator", new Section());
}
[HttpGet]
public ActionResult SOPTopTemplateBuilder(){
List<Section> model = new List<Section>();
return View(model);
}
[HttpPost]
public ActionResult SOPTopTemplateBuilder(IEnumerable<Section> soptop)
{
if (ModelState.IsValid)
{}
return View(soptop);
}
Your use of Html.BeginCollectionItem("Section") perpends Section[xxxx] to the name attribute (where xxxx is a Guid) so that you generating inputs with
<input name="Section[xxxx].SectionId" .... />
which posts back to a model containing a collection property named Sections.
Since you already have a model with that property, you can change the POST method to
[HttpPost]
public ActionResult SOPTopTemplateBuilder(SOPTopTemplateBuilderViewModel soptop)
other options include
Using your existing POST method and omitting the "Section" prefix
using Html.BeginCollectionItem("") which will generate
name="[xxxx].SectionId"
Changing the POST method signature to public ActionResult
SOPTopTemplateBuilder(IEnumerable<Section> section) (where the
name of the parameter matches the name of the prefix)
Using a BindAttribute to 'strip' the prefix from the form values
public ActionResult SOPTopTemplateBuilder([Bind(Prefix = "Section")]IEnumerable<Section> soptop)
As a side note, your editing data, so you should always use a view model (say public class SectionViewModel) rather than using data models in your view. - What is ViewModel in MVC?

MVC Razor Html helper

In Asp.net MVC 4, I want to make a html extend method.
I want it work like this:
in Razor View:
<button type='button' click='domethod'>click</button>
#helper.minify(
<script>
function domethod() {
alert('I'm script!');
}
</script>
);
I think how can I make it render:
<button type='button' click='domethod'>click</button>
<script>function domethod() {alert('I'm script!');}</script>
May I create a extend in backcode, that it can recevie <script Tag> ?
you can use Extensions:
public static class Extensions
{
public static MvcHtmlString Test(this HtmlHelper html, string expression)
{
return new MvcHtmlString("Data");
}
}
#Html.Test("my test")

How can i maintain objects between postbacks?

I'm not so experienced using MVC. I'm dealing with this situation. Everything works well until call the HttpPost method where has all its members null. I don't know why is not persisting all the data on it.
And everything works well, because I can see the data in my Html page, only when the user submit the information is when happens this.
[HttpGet]
public ActionResult DoTest()
{
Worksheet w = new Worksheet(..);
return View(w);
}
[HttpPost]
public ActionResult DoTest(Worksheet worksheet)
{
return PartialView("_Problems", worksheet);
}
This is class which I'm using.
public class Worksheet
{
public Worksheet() { }
public Worksheet(string title, List<Problem> problems)
{
this.Title = title;
this.Problems = problems;
}
public Worksheet(IEnumerable<Problem> problems, WorksheetMetadata metadata, ProblemRepositoryHistory history)
{
this.Metadata = metadata;
this.Problems = problems.ToList();
this.History = history;
}
public string Title { get; set; }
public List<Problem> Problems { get; set; } // Problem is an abstract class
public WorksheetMetadata Metadata { get; set; }
public ProblemRepositoryHistory History { get; set; }
}
And my razor view.... the razor view shows successfully my view. I realized something rare, please note in my 5 and 6 lines that I have HiddenFor method, well if I used that, when calls HTTPPOST persists the data, I don't know why.
#model Contoso.ExercisesLibrary.Core.Worksheet
<div id="problemList">
<h2>#Html.DisplayFor(model => model.Metadata.ExerciseName)</h2>
#Html.HiddenFor(model => model.Metadata.ExerciseName)
#Html.HiddenFor(model => model.Metadata.ObjectiveFullName)
#for (int i = 0; i < Model.Problems.Count; i++)
{
<div>
#Html.Partial(Contoso.ExercisesLibrary.ExerciseMap.GetProblemView(Model.Problems[i]), Model.Problems[i])
</div>
}
</div>
UPDATE
I'm using a static class to get the view name, but as I'm testing I'm just using this Partial view
#model Contoso.ExercisesLibrary.AbsoluteArithmetic.Problem1
<div>
<span style="padding:3px; font-size:18px;">#Model.Number1</span>
<span style="padding:5px; font-size:18px;">+</span>
<span style="padding:5px; font-size:18px;">#Model.Number2</span>
<span style="padding:5px; font-size:18px;">=</span>
<span style="font-size:18px">
#Html.EditorFor(model => model.Result, new { style = "width:60px; font-size:18px;" })
#Html.ValidationMessageFor(model => model.Result)
</span>
</div>
#section Scripts {
}
And here the user do the post
#model Contoso.ExercisesLibrary.Core.Worksheet
<form method="post">
#Html.Partial("_Problems", Model)
<input type="submit" value="Continue" />
</form>
The Model Binder will 'bind' or link input fields on your view to the model. It will not bind display fields (like label), that is why you need the HiddenFor it will add an <input type="hidden" which will then be bound to the Model when you Post.
You can use 'TempData'. It is used to pass data from current request to subsequent request means incase of redirection.
This link also helps you.
TempData
SO Tempdata
Make sure your form tag looks like the following, for instance the controller name, action method, the form method and an id for the form. I am referring to the #using statement. In my case the controller name is RunLogEntry, the action method is Create and the id is form.
Normal Post from View to Controller
#using (Html.BeginForm("Create", "RunLogEntry", FormMethod.Post, new { id = "form", enctype = "multipart/form-data" }))
{
<div id="main">
#Html.Partial("_RunLogEntryPartialView", Model)
</div>
}
If you want to post via Jquery, could do the following:
$.post("/RunLogEntry/LogFileConfirmation",
$("#form").serialize(),
function (data) {
//this is the success event
//do anything here you like
}, "html");
You must specify a form with correct attribute in your view to perform post action
<form action="Test/DoTest" method="post">
...
</form>
or
#using(Html.BeginForm("DoTest", "Test", FormMethod.Post)) {
...
}
The second is recommended.
Put your entire HTML code under:
#using(Html.BeginForm())
tag.

Why is my mvc3 viewmodel not updating correctly?

I am writing an image upload form using ASP.NET MVC 3.
In the view, please notice that I am displaying #Model.ImagePath as text and as a #Html.TextBoxFor(model => model.ImagePath).
View:
#using (Html.BeginForm(null, null, FormMethod.Post, new { enctype="multipart/form-data" }))
{
#Html.HiddenFor(model => model.BannerSlideId)
#Html.HiddenFor(model => model.Key)
<p>
Image Path:#(Model.ImagePath)<br />
Image Path in TextBoxFor: #Html.TextBoxFor(model => model.ImagePath)
</p>
<p>
<label class="styled">Upload Slide Image</label>
<input type="file" name="image" />
</p>
<p>
<button type="submit">Save</button> #Html.ActionLink("Back to List", "Index")
</p>
}
I then select an image using the file input, and I submit the form to the Controller.
Controller action:
[HttpPost]
public ActionResult Create(BannerSlide model, HttpPostedFileBase image)
{
if (image != null)
model.ImagePath = image.FileName;
return View("Edit", model);
}
When I debug with a breakpoint, the image.FileName string is assigned to model.ImagePath. However, when I get back to the View I get Two different values from ImagePath.
Results
Image Path:#(Model.ImagePath)<br />
Correctly returns the image filename that was assigned. But,
Image Path in TextBoxFor: #Html.TextBoxFor(model => model.ImagePath)
Incorrectly returns blank!
Any ideas for why this is happening?
You should remove the ImagePath property from ModelState if you intend to modify it in your POST controller action or HTML helpers such as TextBoxFor will first look for a value inside ModelState when binding and then in the model:
if (image != null)
{
ModelState.Remove("ImagePath");
model.ImagePath = image.FileName;
}
The TextBoxFor HtmlHelper method, and all input helper methods for that matter, set the value of the input control to the value held in ModelState rather than immediately binding to the property of the ViewModel. Since the ImagePath present in the POST to your Create method, it is displayed as a blank value on the subsequent Response.
I would suggest to use following attribute on such actions
public class ModelStateFixAttribute : ActionFilterAttribute
{
public ModelStateFixAttribute()
{
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
ModelStateDictionary modelState = filterContext.Controller.ViewData.ModelState;
String[] arrKeys = new string[modelState.Keys.Count];
modelState.Keys.CopyTo(arrKeys, 0);
foreach (string key in arrKeys)
{
if (modelState.IsValidField(key))
modelState.Remove(key);
}
}
}

Html.BeginForm in MVC3 is rendering too much

I have the following code. Note that I am not using a using as I want to begin and end the form in two different areas of my code. Has anyone seen this action where the helper adds "System.Web.Mvc.Html.MvcForm". I cannot see why this is added.
Code:
#Html.BeginForm(new { action = ViewContext.Controller.ValueProvider.GetValue("action").RawValue })
#{ Html.EndForm() }
Result:
<form action="/adminTests/Edit" method="post">System.Web.Mvc.Html.MvcForm
When I use the following I don't get the "System.Web.Mvc.Html.MvcForm"
#using (Html.BeginForm(new { action = ViewContext.Controller.ValueProvider.GetValue("action").RawValue }))
{
}
You need to wrap that in a using statement. BeginForm writes directly to the response stream, it isn't intended to return a string like the other helpers. The way you are using it, it is writing it's output, then returning the MvcForm object which, when disposed, will write the closing form tag. Instead, your syntax forces it to call ToString on the returned object resulting in the output you see.
#using(Html.BeginForm(...
{
...
}
Edit: Since you can't wrap it in a using statement, you need to either explicitly close the form with HTML or call EndForm later. In either case you have to use the BeginForm (and EndForm) in a code block instead of using it with the string output syntax. Both of these methods write directly to the response stream.
#{ Html.BeginForm() .. }
</form> or #{ Html.EndForm() }
Here's the relevant bits of the code for those who think I'm wrong (without permission):
public static void EndForm(this HtmlHelper htmlHelper) {
HttpResponseBase httpResponse = htmlHelper.ViewContext.HttpContext.Response;
httpResponse.Write("</form>"); // <-- look ma, Response.Write()
}
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)); <-- and here
return new MvcForm(htmlHelper.ViewContext.HttpContext.Response);
}
The BeginForm/EndForm helpers don't return a IHtmlResult so you should use them like this:
#{ Html.BeginForm(new { action = ViewContext.Controller.ValueProvider.GetValue("action").RawValue }); }
...
#{ Html.EndForm(); }
Try something like this:
#using (Html.BeginForm()) {
#Html.ValidationSummary(true)
<fieldset>
<p>
#Html.LabelFor(model => model.Username) <br />
#Html.EditorFor(model => model.Username)
#Html.ValidationMessageFor(model => model.Username)
</p>
<p>
#Html.LabelFor(model => model.Password) <br />
#Html.EditorFor(model => model.Password) #Html.RouteLink("Forgot Password", "password") <br />
#Html.ValidationMessageFor(model => model.Password)
</p>
<p class="mtop10">
<div class="float-left small blue super-button">
<a class="auto-submit" href="#">Login</a>
</div>
<div class="alt-action">
or #Html.RouteLink("Register", "register")
</div>
</p>
</fieldset>
}
I also saw System.Web.Mvc.Html.MvcForm when using:
Html.BeginForm("Index", "Home", FormMethod.Post)
The following change fixed it:
Html.BeginForm("Index", "Home", "POST");
Seems like FormMethod.Post forces the unwanted output but I can't explain it better than that. The change worked for me though.

Resources