ASP.NET MVC 5 HttpPostedFileBase always null - asp.net-mvc

I checked everything and can't figure it out. The Form is multipart/form-data, the name of the input matches the name of the variable in the post method, and still the file is always null and Request.Files is always empty.
#model Gnosis.View.Areas.OpportunityAction.Models.Rfp.RfpModel
#using (Html.BeginForm(MVC.OpportunityAction.ChangeRfp.Edit(), FormMethod.Post, new { enctype = "multipart/form-data", id="submitForm" }))
{
<input type="file" name="file" id="file" style="width: 400px"/>
}
controller method
[HttpPost]
public virtual ActionResult Edit(HttpPostedFileBase file, RfpModel model)
{
}
If I do an ajax post that works, but I should work with MVC mechanisms also and can't figure it out why it doesn't...
Update. This is the code in the view:
#model Gnosis.View.Areas.OpportunityAction.Models.Rfp.RfpModel
#using (Html.BeginForm(MVC.OpportunityAction.ChangeRfp.Edit(), FormMethod.Post, new { enctype = "multipart/form-data", id="submitForm" }))
{
#Html.HiddenFor(m => m.Id)
#Html.HiddenFor(m => m.SystemEditDate)
#Html.AntiForgeryToken()
#Html.HiddenFor(m => m.Attachment)
#Html.HiddenFor(m => m.Description)
#Html.HiddenFor(m => m.FileSize)
#Html.HiddenFor(m => m.FileDate)
#Html.HiddenFor(m => m.FileTime)
#Html.HiddenFor(m => m.FileExtension)
#Html.HiddenFor(m => m.OpportunityRfpId)
<table class="keyvalue withrowpadding">
<tr>
<td class="key">#Html.LabelFor(m => m.Rfp)</td>
<td class="value">#Html.EditorFor(m => m.Rfp)</td>
</tr>
<tr>
<td class="key">#Html.LabelFor(m => m.RfpDate)</td>
<td class="value">#Html.EditorFor(m => m.RfpDate)</td>
</tr>
<tr>
<td class="key">#Html.LabelFor(m => m.FileName)</td>
<td class="value">#Html.TextBox("NameForDisplay", Model.FileName, new {disabled = "disabled", style = "width:300px"})</td>
</tr>
<tr>
<td class="key">RFP File Name:</td>
<td class="value"><input type="file" name="file" id="file" style="width: 400px"/> #Html.ValidationMessageFor(m => m.Attachment)</td>
</tr>
<tr>
<td></td>
<td>
<input type="submit" name="submit" value="Save" id="Save"/>
<input type="button" value="Cancel"/>
</td>
</tr>
</table>
#Html.ValidationSummary()
}
<script>
$(document).on('click', '#Save', function () {
var vcfData = new FormData($('#submitForm')[0]);
var rfp = $("#Rfp").val();
var rfpDate = $("#RfpDate").val();
var id = $("#Id").val();
var opportunityRfpId = $("#OpportunityRfpId").val();
console.log(id);
$.ajax({
url: '#Url.Action(MVC.OpportunityAction.ChangeRfp.SubmitFile())' + "?file=" + vcfData + '&id=' + id + '&Rfp=' + rfp + '&RfpDate=' + rfpDate + '&OpportunityRfpId='+ opportunityRfpId,
type: "post",
data: vcfData,
processData: false,
contentType: false,
cache: false,
success: function (data) {
}
});
});
</script>
The code is inherited, and probably the original developer couldn't figure out this puzzle either so he did the ajax post. But I need to expand this functionality, and it needs to be done in the Edit method and dependent on processing of this file...

Related

Html.BeginForm works but not Ajax.BeginForm

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.

Returning specific table data from the view to the controller? asp.net MVC

So I have a view which is currently displaying a table of data from the database with a select/selectall checkbox in place. What I'd like to do is be able to get a list of my CustomerNumbers and whether or not they have been checked and return it to my controller for further action.
Below is my how my view is currently written. I have tried numberous ways to get this to work and I am stumped.
#model MassInactiveInspections.Models.CustomerInfoList
<div class="container">
<h2>#ViewBag.Title</h2>
<div class="bs-callout bs-callout-info" style="margin-bottom: 0px;">
</div>
<div class="col-md-8">
#using (Html.BeginForm("SelectedCustomers", "Home", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { #class = "form-horizontal", role = "form" }))
{
#Html.AntiForgeryToken()
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
<table class="table table-bordered">
<tr>
<th class="active">#Html.DisplayName("Select All?")<input type="checkbox" class="select-all checkbox" name="select-all" /></th>
<th>#Html.DisplayNameFor(m => m.CustomerInfoListView.FirstOrDefault().BusinessName)</th>
<th>#Html.DisplayNameFor(m => m.CustomerInfoListView.FirstOrDefault().CustomerName)</th>
<th>#Html.DisplayNameFor(m => m.CustomerInfoListView.FirstOrDefault().CustomerNumber)</th>
<th>#Html.DisplayNameFor(m => m.CustomerInfoListView.FirstOrDefault().InspectionCycleDescription)</th>
<th>#Html.DisplayNameFor(m => m.CustomerInfoListView.FirstOrDefault().LastInspectionDate)</th>
<th>#Html.DisplayNameFor(m => m.CustomerInfoListView.FirstOrDefault().RouteCode)</th>
<th>#Html.DisplayNameFor(m => m.CustomerInfoListView.FirstOrDefault().RouteID)</th>
<th>#Html.DisplayNameFor(m => m.CustomerInfoListView.FirstOrDefault().SiteNumber)</th>
<th>#Html.DisplayNameFor(m => m.CustomerInfoListView.FirstOrDefault().SystemCode)</th>
</tr>
#foreach (var item in Model.CustomerInfoListView)
{
<tr>
<td class="active"> <input id="Selected" input type="checkbox" class="select-item checkbox" name="select-item" value="1000" /> </td>
<td>#Html.DisplayFor(modelItem => item.BusinessName)</td>
<td>#Html.DisplayFor(modelItem => item.CustomerName)</td>
<td>
#Html.DisplayFor(modelItem => item.CustomerNumber)
#Html.HiddenFor(m => m.CustomerInfoListView.FirstOrDefault().CustomerNumber)
</td>
<td>#Html.DisplayFor(modelItem => item.InspectionCycleDescription)</td>
<td>#Html.DisplayFor(modelItem => item.LastInspectionDate)</td>
<td>#Html.DisplayFor(modelItem => item.RouteCode)</td>
<td>#Html.DisplayFor(modelItem => item.RouteID)</td>
<td>#Html.DisplayFor(modelItem => item.SiteNumber)</td>
<td>#Html.DisplayFor(modelItem => item.SystemCode)</td>
</tr>
}
</table>
</div>
<div class="form-group">
<div style="margin-top: 50px">
<input type="submit" id="btnLost" class="btn btn-primary" value="Lost"/>
<input type="submit" class="btn btn-primary" name="OOBButton" value="OOB" />
<input type="submit" class="btn btn-primary" name="RefusedButton" value="Refused" />
</div>
</div>
}
</div>
</div>
and finally my controller
[HttpPost]
public ActionResult SelectedCustomers(CustomerInfoList
customerNumberList)
{
return View("ViewCustomerInfo");
}
also here is my javascript for the checkboxes
[HttpPost]
public ActionResult SelectedCustomers(CustomerInfoList customerNumberList)
{
return View("ViewCustomerInfo");
}
//column checkbox select all or cancel
$("input.select-all").click(function () {
var checked = this.checked;
$("input.select-item").each(function (index, item) {
item.checked = checked;
});
});
//check selected items
$("input.select-item").click(function () {
var checked = this.checked;
console.log(checked);
checkSelected();
});
//check is all selected
function checkSelected() {
var all = $("input.select-all")[0];
var total = $("input.select-item").length;
var len = $("input.select-item:checked:checked").length;
console.log("total:" + total);
console.log("len:" + len);
all.checked = len === total;
}
});
Not sure if it is the best way to handle it but I simply added the fields that I wanted into a HiddenFor helper and they were returned to my the appropriate controller. Thanks everyone for the help.
#Html.HiddenFor(m => m.additionalCustomerInfoListView[i].CustomerNumber)

How to update Partial View without full replacement

I have this partial view. This works, ie, when the user clicks the button, an ajax trip is made to the server, and it updates the partial view and it comes down and replaces the current div with the updated Div and shows the Promo Message.
However, it seems there should be a better way to do this. In other words, is it necessary to replace the entire partial view? Isn't there a way to send just the data up to the server, and then update just the message when it gets back, like maybe via a JSON call?
Controller:
public ActionResult ApplyPromoCode(OrderViewModel orderViewModel) {
orderViewModel.PromoMessage = "Promo has been applied";
return PartialView("PromoPartial", orderViewModel);
}
Partial View:
#model NTC.PropertySearch.Models.OrderViewModel
#using (Ajax.BeginForm("ApplyPromoCode", "OrderSummary", new AjaxOptions { InsertionMode = InsertionMode.Replace, UpdateTargetId = "promo" }))
{
<div id="promo">
<table>
<td>
#Html.LabelFor(m => m.PromoCode)
</td>
<td>
#Html.TextBoxFor(m => m.PromoCode)
</td>
<td>
#Html.ValidationMessageFor(m => m.PromoCode)
</td>
<td>
<input type="submit" value="Apply Promo Code" />
</td>
<td>
#Html.DisplayFor(m=> m.PromoMessage)
</td>
</table>
</div>
}
you can do this to
Controller
public ActionResult ApplyPromoCode(OrderViewModel orderViewModel) {
//your processing code
return Content("Promo has been applied");
}
View
#model NTC.PropertySearch.Models.OrderViewModel
#using (Ajax.BeginForm("ApplyPromoCode", "OrderSummary", new AjaxOptions { UpdateTargetId = "pcode" }))
{
<div id="promo">
<table>
<td>
#Html.LabelFor(m => m.PromoCode)
</td>
<td>
#Html.TextBoxFor(m => m.PromoCode)
</td>
<td>
#Html.ValidationMessageFor(m => m.PromoCode)
</td>
<td>
<input type="submit" value="Apply Promo Code" />
</td>
<td>
<div id="pcode"></div>
</td>
</table>
</div>
}
Instead of returning a PartialView you can always return a JSON object/array or some XML and use jQuery/JavaScript on your callback function to update the values of your input fields.
Here's an example of some code I use to return JSON from a Controller:
public ActionResult CurrentTags(int entityID)
{
Entity entity = db.Entity.Find(entityID);
var tags = from tag in entity.Tag
select new
{
id = tag.Name,
text = tag.Name
};
return this.Json(tags, JsonRequestBehavior.AllowGet);
}

jquery post to asp.net mvc controller

I have these two controller actions:
public ActionResult Index(string search, int? page)
{
return View(db.powners.Where(x => x.petowner.StartsWith(search) || search == null).OrderBy(x => x.petowner).ToList().ToPagedList(page ?? 1, 5));
}
public ActionResult Ownerfind(string search, int? page)
{
return View(db.powners.Where(x => x.petowner.StartsWith(search) || search == null).OrderBy(x => x.petowner).ToList().ToPagedList(page ?? 1, 5));
}
And this json method also:
public JsonResult Getowner(int nownerid)
{
OwnerEntities owner = new OwnerEntities();
var existingCust = owner.powners.Single(p => p.ownerid == nownerid);
return Json(existingCust);
}
Index view:
#model PagedList.IPagedList<Mvc4test2.Models.powner>
#using PagedList.Mvc;
#using PagedList;
#{
ViewBag.Title = "Index";
}
<link href="../../Content/PagedList.css" rel="stylesheet" type="text/css" />
<div style="font-family:Arial">
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<p>
#using (#Html.BeginForm("Index", "owner", FormMethod.Get))
{
<b>Search</b>#Html.TextBox("search")<input type="submit" value="search" />
}
</p>
<table id="ownertable">
<tr>
<th>
#Html.DisplayNameFor(model => model.First().petowner)
</th>
<th>
#Html.DisplayNameFor(model => model.First().ostreet)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.ownerid)
</td>
<td>
#Html.DisplayFor(modelItem => item.petowner)
</td>
<td>
#Html.DisplayFor(modelItem => item.ostreet)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.ownerid }) |
#Html.ActionLink("Details", "Details", new { id=item.ownerid }) |
#Html.ActionLink("Delete", "Delete", new { id=item.ownerid })
</td>
</tr>
}
</table>
#Html.PagedListPager(Model, page=>Url.Action("Index", new{page, search=Request.QueryString["search"]}), new PagedListRenderOptions(){Display=PagedListDisplayMode.IfNeeded, DisplayPageCountAndCurrentLocation=true,MaximumPageNumbersToDisplay=5})
Ownerfind view:
#model PagedList.IPagedList<Mvc4test2.Models.powner>
#using PagedList.Mvc;
#using PagedList;
#{
ViewBag.Title = "Index";
}
<link href="../../Content/PagedList.css" rel="stylesheet" type="text/css" />
<div style="font-family:Arial">
<h2>Index</h2>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<p>
#using (#Html.BeginForm("ownerfind", "owner", FormMethod.Get))
{
<b>Search</b>#Html.TextBox("search")<input type="submit" value="search" />
}
</p>
<p>
hello
</p>
<table id="ownertable">
<tr>
<th>
#Html.DisplayNameFor(model => model.First().petowner)
</th>
<th>
#Html.DisplayNameFor(model => model.First().ostreet)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.ownerid)
</td>
<td>
#Html.DisplayFor(modelItem => item.petowner)
</td>
<td>
#Html.DisplayFor(modelItem => item.ostreet)
</td>
</tr>
}
</table>
#Html.PagedListPager(Model, page=>Url.Action("ownerfind","owner", new{page, search=Request.QueryString["search"]}), new PagedListRenderOptions(){Display=PagedListDisplayMode.IfNeeded, DisplayPageCountAndCurrentLocation=true,MaximumPageNumbersToDisplay=5})
</div>
Jquery segment:
$("#ownertable td:nth-child(1)").click(function (event) {
event.preventDefault();
var $td = $(this).closest('tr').children('td');
var currentid = $.trim($td.eq(0).text());
alert("hi:" + currentid);
$.ajax({
url: 'owner/Getowner',
type: 'POST',
data: 'nownerid=' + currentid,
dataType: "json",
success: function (result) {
alert("hello");
//alert(result.petowner);
opener.$('input#ownerid').val(result.ownerid);
opener.$('#petowner').val(result.petowner);
opener.$('input#ostreet').val(result.ostreet);
self.close();
}
});
});
Here's the problem:
If I use the first controller action public ActionResult Index(string search, int? page), then I get the json result as expected. However if I use the second controller action
public ActionResult Ownerfind(string search, int? page) then the I donot get a json result back.
Both controller actions work on screen, and I get the alert("hi:" + currentid);
But I only get a json result if I use click on the table used in the Index view.
Why isn't the ownerfind view working with jquery? It displays correctly just no json result.
I want seperate view, because one will be a lookup and another a regular for adding and editing.
Add [HttpPost] attribute to Getowner action from Contoller
And return from get owner something like this:
return Json(new { Data = existingCust });

Why the value is changed after clicking the submit button?

I make one action method for 2 activities (new input and edit), and there is also only one
view to handle those activities.
But I don't understand no matter what activity happen, the action method always think it is a new input.
I learned that it because of the ID is always 0, but the problem is, when doing the edit, when in the view the ID is correct as the ID of the data, but when I click the submit button, the action method just see the 0 value of ID.
Here is the action method I used:
[HttpPost]
public ActionResult AddAssignment(SateliteSchedule SatSched)
{
var txt = "";
if (ModelState.IsValid)
{
if (SatSched.ID == 0)
{
db.SateliteSchedules.Add(SatSched);
txt = "{0} has been added!";
}
else
{
db.Entry(SatSched).State = EntityState.Modified;
txt = "{0} has been modified!";
}
db.SaveChanges();
Utility utl = new Utility();
TempData["message"] = string.Format(txt, utl.GetSateliteName(SatSched.SateliteID));
return RedirectToAction("FormAssignment");
}
else
{
ViewBag.Message = "ModelState is not Valid!";
return View("ErrorView");
}
}
And here is the view:
#using (Html.BeginForm("AddAssignment", "admin", FormMethod.Post))
{
#Html.ValidationSummary(true)
<table>
<tr>
<td>#Html.LabelFor(m => m.Tanggal)
</td>
<td>
#Html.EditorFor(m => m.Tanggal)
#Html.ValidationMessageFor(m => m.Tanggal)
</td>
</tr>
<tr>
<td>#Html.LabelFor(m => m.SateliteID)</td>
<td>
#Html.DropDownList("SateliteID", (IEnumerable<SelectListItem>)ViewBag.SatList, "--- Satelite ---")
#Html.ValidationMessageFor(m => m.SateliteID)
</td>
</tr>
<tr>
<td>#Html.LabelFor(m => m.WMOnDuty)</td>
<td>
#Html.DropDownList("WMOnDuty", (IEnumerable<SelectListItem>)ViewBag.WMList, "--- Worship Manager ---")
#Html.ValidationMessageFor(m => m.WMOnDuty)
</td>
</tr>
<tr>
<td>#Html.LabelFor(m => m.SMOnDuty)</td>
<td>#Html.EditorFor(m => m.SMOnDuty)</td>
</tr>
<tr>
<td>#Html.LabelFor(m => m.WLOnDuty)</td>
<td>#Html.EditorFor(m => m.WLOnDuty)</td>
</tr>
<tr>
<td>#Html.LabelFor(m => m.MLOnDuty)</td>
<td>#Html.EditorFor(m => m.MLOnDuty)</td>
</tr>
<tr>
<td>#Html.LabelFor(m => m.SoundMan)</td>
<td>#Html.EditorFor(m => m.SoundMan)</td>
</tr>
<tr>
<td valign=top>#Html.LabelFor(m => m.Note)</td>
<td>#Html.TextAreaFor(model => model.Note, new { #class = "memo-text" })</td>
</tr>
</table>
<div>
<input type="submit" value="Save" />
#Html.ActionLink("Kembali", "FormAssignment")
</div>
}
What should I check to fix this?
You have to have the Id as a hidden, so when you go in the method the model will have the id asigned to it (does it make sense?). try placing this in your form
#Html.HiddenFor(m => m.ID)

Resources