Html.BeginForm works but not Ajax.BeginForm - asp.net-mvc

I have an ASP MVC app that is attempting to submit my ViewModel which has a property called Document on it that is an HttpPostedFileBase. The ViewModel binds fine when I use #Html.BeginForm, however if I change it to #Ajax.BeginForm and keep all things the same, it will bind the all the ViewModel properties EXCEPT for the HttpPostedFileBase property. Any advice?
Relevant Code:
[HttpPost]
public ActionResult Add(ViewModel vm)
{
return new HttpStatusCodeResult(200);
}
#using (Ajax.BeginForm("Add", "Home", new AjaxOptions() { HttpMethod = "Post" , AllowCache = false}, new { enctype = "multipart/form-data" }))
{
#Html.HiddenFor(m => Model.Document.DocumentType);
#Html.HiddenFor(m => Model.Document.DocumentTypeId);
#Html.HiddenFor(m => Model.Document.File);
<div class="container">
<table>
<tr>
<td>
<input class="form-control" type="text" id="lblAllReceivables" /> </td>
<td >
#Html.TextBoxFor(m => m.Document.File, new { type = "file", #class = "inputfile", #name = "file", #id = Model.Document.DocumentTypeId, #accept = ".pdf, .doc, docx" })
<label id="lblUpload" for="#Model.Document.DocumentTypeId"><i class="fa fa-upload" style="margin-right:10px;"></i>File</label>
</td>
</tr>
<tr>
<td colspan="2" >
Comments:<br />
<div class="input-group" style="width:100%;">
#Html.TextAreaFor(m => m.Document.Comments)
</div>
</td>
</tr>
<tr>
<td colspan="2"><hr /></td>
</tr>
<tr><td colspan="2" > <input id="btnSubmit" class="btn btn-default btn-lg" type="submit" style="width:275px;" value="Submit Application" /><a class="btn btn-default">Cancel</a></td></tr>
</table>
</div>
}

I have found that the browser does not support uploading a file via xmlhttprequest, which is what ajax.beginform uses to post data (as do all browser ajax libs). If you are using a html 5 browser, you can use the new file api to upload the file. for older browsers, you use an iframe to post the file. google for jquery plugin that wrap both those functions or just use the iframe appraoch (it pretty trival).
In specific cases I prefer to use another plugins like DropzoneJS it's better to handle it and you can upload multiple files easy.

Related

Save all data of MVC4 html grid

Can anyone give me an example of saving all html grid data in one time. I have a view like this.
#model IList<SURVEY.Models.Question>
#using (Html.BeginForm("Index", "Survey", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { #class = "form-3" }))
{
#foreach(var item in Model)
{
<tr>
<td>#item.Ans1</td>
<td align="center">
<label>
<input type="radio" name="optionAS_#item.QuestionId" value="1" id="optionAS_1" onclick="disableAs(this,#item.QuestionId,1)"/>
</label>
</td>
<td align="center">
<label>
<input type="radio" name="optionAS_#item.QuestionId" value="2" id="optionAS_1" onclick="disableAs(this,#item.QuestionId,2)"/>
</label>
</td>
</tr>
}
}
I am getting null value for these controls in controller post.
[HttpPost]
public ActionResult Index(IList<Question> ques)
{
return View();
}
I am getting ques is null here. Can anyone tell me how can I resolve this?
You should use html helpers to bind properties of your model, your code might be as follows:
#for(var i = 0; i < Model.Count; i++)
{
<tr>
<td>#Html.HiddenFor(_ => Model[i].Id)
Model[i].Ans1
</td>
<td align="center">
<label>
#Html.RadioButtonFor(_ => Model[i].Name)
</label>
</td>
...
</tr>
}
and so for. HiddenFor helper is needed to create hidden input to send Id value to server to give you ability to identify you object. Take a look into Html Helpers in MVC and you will have your model back to server when form is submitted.

Trying to figure out Ajax.BeginForm

I have a view in 2 sections.
The top section I input fields and submit to save them.
In the second section I have an autocomplete textbox. I select an item in autocomplete, and when I click submit I want to add that item to a datatable.
So for the first part when I click submit I save the details via a HttpPost method on the controller.
For the second part I intend to save it via an Ajax call for the controller and then bring back a partial view with the results. I have not coded the partial view yet, that is next.
Now I am new to Ajax.BeginForm and I am struggling with it.
I was hoping that the submit button inside the Ajax.BeginForm would only apply to that part of the form.
But in fact it calls the HttpPost method for the whole form.
So how do I fix this?
My view looks like;
#using ITOF.HtmlHelpers
#model ITOF.Models.OngoingContractViewModel
#{
ViewBag.Title = "EditOngoingContractDetails";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (Html.BeginForm("EditOngoingContractDetails", "Contract", FormMethod.Post,
new { enctype = "multipart/form-data" }))
{
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.Contract.ContractId)
<h1>Edit Ongoing Contract Details</h1>
<fieldset>
<legend>#Model.Contract.Heading</legend>
<p>Where you see <span class="error">*</span> you must enter data.</p>
<table>
<tr>
<td style="text-align: right">
#Html.LabelFor(model => model.Contract.EndDate)
</td>
<td>
#Html.EditorFor(model => model.Contract.EndDate)
</td>
</tr>
<tr>
<td style="text-align: right">
#Html.LabelFor(model => model.Contract.Organogramme)
</td>
<td>
<input type="file" id="PDF" name="file" />
#Html.HiddenFor(model => model.Contract.Organogramme)
</td>
</tr>
#if (!string.IsNullOrWhiteSpace(Model.Contract.Organogramme))
{
<tr>
<td></td>
<td>
The current organogramme is <span class="HighlightTextRed">#Model.GetOrganogrammeName()</span>
for the contract <span class="HighlightTextRed">#Model.Contract.ContractName</span><br/>
Click here to see the last saved organogramme
</td>
</tr>
}
<tr>
<td style="text-align: right">
#Html.LabelFor(model => model.Contract.AssistantRLOManagerId)
</td>
<td>
#Html.DropDownListFor(model => model.Contract.AssistantRLOManagerId, Model.AssistantRloManagerSelectList, "--N/A--")
</td>
</tr>
#if (this.TempData["SuccessMessage"] != null)
{
<tr>
<td colspan="2" class="success">#this.TempData["SuccessMessage"].ToString()</td>
</tr>
}
<tr>
<td colspan="2" style="padding-top: 20px; text-align: center;"><input type="submit" value="Save" /></td>
</tr>
</table>
</fieldset>
<fieldset>
<legend>Add an existing Site to this contract: </legend>
#using (Ajax.BeginForm("AddExistingSite", new AjaxOptions { UpdateTargetId = "siteRows" }))
{
<input type="text" name="q" style="width: 800px"
data-autocomplete="#Url.Action("SiteSearch", "DataService", new { contractId = #Model.Contract.ContractId })" />
<input type="submit" value="Add site to contract" />
}
#if (Model.SiteList.Count > 0)
{
<table id="siteDataTable" class="display">
<thead>
<tr>
<th>Main Site?</th>
<th>Type</th>
<th>Address</th>
<th>Map</th>
<th>Telephone</th>
<th>Email</th>
</tr>
</thead>
<tbody id="siteRows">
#foreach (var item in Model.SiteList)
{
<tr id="#item.SiteContract.SiteContractId">
<td>#item.SiteContract.MainSiteFlag</td>
<td>#item.Site.SiteType</td>
<td>#item.Site.Address</td>
<td>#item.Site.MapUrl</td>
<td>#item.Site.Telephone</td>
<td>#item.Site.Email</td>
</tr>
}
</tbody>
</table>
<div class="add_delete_toolbar" />
}
#Html.ListLink("Back to List")
</fieldset>
}
Oh no, you just cannot nest HTML forms. That's not supported. You will have to rethink your design. This really has absolutely nothing to do with ASP.NET MVC and things like Html.BeginForm or Ajax.BeginForm. The HTML specification simply tells you that the <form> tag cannot be nested and if you nest it you will get undefined behavior that could vary between browsers.
For example you could implement the autocomplete functionality using jquery UI autocomplete plugin and get rid of the Ajax.BeginForm.

Delete and Update List of Objects in Asp.Net MVC 4

I have a form that has an input section at the top of the page and in the bottom half it displays a list of the objects that were added. I need to be able to Edit and Delete these objects and I'm not sure where to start or how to do it.
This code display the list of objects.
#if (Model.ListOfRecipients != null)
{
for (int i = 0; i < Model.ListOfRecipients.Count; i++)
{
<div class='recipient-wrapper'>
<div class='decision_block'>
<table class='recipient'>
<tr>
<td class='recipient-title'>
#Html.HiddenFor(model=>model.ListOfRecipients[i].RecipientId)
<h3>
#Html.DisplayTextFor(model => model.ListOfRecipients[i].RecipientName)
#Html.HiddenFor(model => model.ListOfRecipients[i].RecipientName)
</h3>
<div class='delivery-type'>
Delivery Type: #Html.DisplayTextFor(model => model.ListOfRecipients[i].DeliveryType)
#Html.HiddenFor(model => model.ListOfRecipients[i].DeliveryType)
</div>
</td>
<td class='na express'>
#Html.CheckBoxFor(model => model.ListOfRecipients[i].ExpressIndicator)
#Html.HiddenFor(model => model.ListOfRecipients[i].ExpressIndicator)
</td>
<td class='quantity'>
<h3>
Qty #Html.DisplayTextFor(model => model.ListOfRecipients[i].Quantity)
#Html.HiddenFor(model => model.ListOfRecipients[i].Quantity)
</h3>
</td>
<td class='action'>
<input class='button edit_recipient' type='button' value='Edit' />
<input class='button delete_recipient' type='button' value='Delete' />
</td>
</tr>
</table>
<input class='button update_recipient' type='button' value='Update' />
<a class='cancel_update' href='#'>Cancel</a>
</div>
</div>
</div>
}
}
You need to write Edit and Delete action methods in your controller and then set your two buttons to call the appropriate method.
The code for the action methods will depend on several factors that may to broad to address here.
Using Razor Syntax -- sorry the others were using wrong markup:(
<input class='button edit_recipient' type='button' value='Edit'
onclick="location.href='#Url.Action("Edit", "Controller", new { id = Model.Id } )'" />
You could create a delete controller and an edit controller and in the above view have a beside each one that has a delete and edit which recieves the objects id and each button goes to it's own view where they can edit or delete

Table row not updating correctly using MVC Ajax

I have a table with each row as an ascx. This control consists of a Ajax form which includes cells which might have inputs. Here is the ascx:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<NerdDinner.Models.Dinner>" %>
<% using (Ajax.BeginForm("ViewAll", new AjaxOptions
{UpdateTargetId = "Dinner" + Model.DinnerID, InsertionMode = InsertionMode.InsertAfter, OnSuccess = "jsfunction"
})) {%>
<%= Html.Hidden("ID", Model.DinnerID)%>
<td><input type="submit" value="Save" /></td>
<td><strong>'<%=Model.Title%>'</strong></td>
<td><%=Model.EventDate.ToShortTimeString()%> on <%=Model.EventDate.ToShortDateString()%></td>
<td><%=Model.Address%></td>
<td><%= Html.TextBox("HostedBy", Model.HostedBy)%></td>
<td><%= Html.ActionLink("Edit", "Edit", new { id = Model.DinnerID })%></td>
<%} %>
After a submit, I complete my processing and send back the updated ascx to replace the existing one.
However this ascx gets constructed a little different than the original which causes it to render incorrectly
This is what the original ascx looks like in FF:
<tr id="Dinner5">
<form onsubmit="Sys.Mvc.AsyncForm.handleSubmit(this, new Sys.UI.DomEvent(event), { insertionMode: Sys.Mvc.InsertionMode.insertAfter, updateTargetId: 'Dinner5', onSuccess: Function.createDelegate(this, jsfunction) });" method="post" action="/NerdDinner/Dinners/ViewAll"/>
<input id="ID" type="hidden" value="5" name="ID"/>
<td>
</td>
<td>
</td>
<td>12:00 AM on 2/2/2010</td>
<td>ZSA2</td>
<td>
</td>
<td>
</td>
</tr>
This is what the returned control (ajaxContext.get_data()) looks like:
<tr id="Dinner1">
<form onsubmit="Sys.Mvc.AsyncForm.handleSubmit(this, new Sys.UI.DomEvent(event), { insertionMode: Sys.Mvc.InsertionMode.replace, updateTargetId: 'Dinner1' });" method="post" action="/NerdDinner/Dinners/ViewAll">
<input id="ID" type="hidden" value="1" name="ID"/>
<td>
</td>
<td>
</td>
<td>12:00 AM on 1/1/2010</td>
<td>ZSA1</td>
<td class="red-back">
</td>
<td>
</td>
</form>
</tr>
Notice the latter does not contain any tds directly under tr but the form encloses all tds and so nothing is rendered.
In IE, I get an error saying 'htmlfile: Unknown runtime error' in MicrosoftAjax.js.
I'm sure I'm missing something basic here. Any help would be appreciated. Thanks.
A few things. This code from your original snippet doesn't look quite right as the input is outside of the form. If you notice the end of the form tag is self closing in this case.
<form onsubmit="Sys.Mvc.AsyncForm.handleSubmit(this, new Sys.UI.DomEvent(event), { insertionMode: Sys.Mvc.InsertionMode.insertAfter, updateTargetId: 'Dinner5', onSuccess: Function.createDelegate(this, jsfunction) });" method="post" action="/NerdDinner/Dinners/ViewAll"/>
<input id="ID" type="hidden" value="5" name="ID"/>
In your code below it appears as though you're explicitly telling the form to encapsulate all of the td's, note the positioning of the curly braces for the BeginForm method.
<% using (Ajax.BeginForm("ViewAll", new AjaxOptions
{UpdateTargetId = "Dinner" + Model.DinnerID, InsertionMode = InsertionMode.InsertAfter, OnSuccess = "jsfunction"
})) {%>
<%= Html.Hidden("ID", Model.DinnerID)%>
<td><input type="submit" value="Save" /></td>
<td><strong>'<%=Model.Title%>'</strong></td>
<td><%=Model.EventDate.ToShortTimeString()%> on <%=Model.EventDate.ToShortDateString()%></td>
<td><%=Model.Address%></td>
<td><%= Html.TextBox("HostedBy", Model.HostedBy)%></td>
<td><%= Html.ActionLink("Edit", "Edit", new { id = Model.DinnerID })%></td>
<%} %>
I'm not quite sure what you're trying to accomplish but hopefully this gets you started in the right direction.
How about using a nested table? This should help it in Firefox.
<td>
<% using (Ajax.BeginForm("ViewAll", new AjaxOptions
{UpdateTargetId = "Dinner" + Model.DinnerID, InsertionMode = InsertionMode.InsertAfter, OnSuccess = "jsfunction"
})) {%>
<%= Html.Hidden("ID", Model.DinnerID)%>
<table><tr>
<td><input type="submit" value="Save" /></td>
<td><strong>'<%=Model.Title%>'</strong></td>
<td><%=Model.EventDate.ToShortTimeString()%> on <%=Model.EventDate.ToShortDateString()%></td>
<td><%=Model.Address%></td>
<td><%= Html.TextBox("HostedBy", Model.HostedBy)%></td>
<td><%= Html.ActionLink("Edit", "Edit", new { id = Model.DinnerID })%></td>
</tr>
</table>
<%} %>
</td>
Interesting- i ran into this EXACT problem just a few minutes ago.
Apparently, IE8 does not like to update elements, only divs.
this blog post got me on the right path, and i just used a div as my updatetargetid rather than the i was originally using. it's ugly now, but at least it works. i need to figure out how to make it look a little nicer.
hope this helps some!

binding asp.net mvc form element to complex object for posting to controller

I am trying to refactor to avoid parsing the FormCollection from the view so i changed this to pass in an strongly typed object. My form elements are the same names as the properties on the LinkUpdater Object. But when i put a breakpoint on the first link in the controller all of the properties are null.
any ideas or suggestions?
View:
<%using (Ajax.BeginForm("AddNewLink", "Links", new AjaxOptions { UpdateTargetId = "LinkList", LoadingElementId = "updating", OnSuccess = "done" }))
{ %>
<fieldset style="text-align:left">
<table>
<tr><td>Url:</td><td> <input style="width:500px" type="text" name="URL" /></td></tr>
<tr><td>Description: </td><td><input style="width:400px" type="text" name="Description" /></td></tr>
<tr><td>Tags: </td><td><input style="width:400px" id="Tags" name="tags" type="text" /></td></tr>
<tr><td><input type="submit" value="Add Link" name="submit" /></td></tr>
</table>
</fieldset>
<% } %>
Controller Post:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult AddNewLink(LinkUpdater linkUpdater_)
{
string[] tags = linkUpdater_.Tags.Replace(" ", "").Split(',');
linkRepository.AddLink(linkUpdater_.URL, linkUpdater_.Description, tags);
.....
}
LinkUpdater class:
public class LinkUpdater
{
public string URL;
public string Description;
public string Tags;
}
Model binder in MVC binds to properties, while you use fields. Change to
public string URL { get; set; }
And by the way, there're other drawbacks, like if you use private set, it will silently skip binding, too.
Is there any particular reason your are not using the strongly-typed HTMLHelpers to render your input fields?
<%using (Ajax.BeginForm("AddNewLink", "Links", new AjaxOptions { UpdateTargetId = "LinkList", LoadingElementId = "updating", OnSuccess = "done" }))
{ %>
<fieldset style="text-align: left">
<table>
<tr>
<td>
Url:
</td>
<td>
<%=Html.TextBox("URL", Model.URL, new { style = "width:500px;" }) %>
</td>
</tr>
<tr>
<td>
Description:
</td>
<td>
<%=Html.TextBox("Description", Model.Description, new { style = "width:400px;" }) %>
</td>
</tr>
<tr>
<td>
Tags:
</td>
<td>
<%=Html.TextBox("Tags", Model.Tags, new { style = "width:400px;" }) %>
</td>
</tr>
<tr>
<td>
<input type="submit" value="Add Link" name="submit" />
</td>
</tr>
</table>
</fieldset>
<% } %>
I'm not sure it will fix your problem, but it's a step in the right direction at least.

Resources