MVC file uploader returns a null - asp.net-mvc

I am basing my solution on this article;
http://dotnetslackers.com/articles/aspnet/ASP-NET-MVC-and-File-Uploads.aspx
However when I try to upload a picture I get a null instead of a filename.
My view looks like this;
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<SHP.Models.HrViewModel>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Edit Employee
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<fieldset>
<legend>Add details to the selected employee</legend>
<p>The photo you select for an employee will appear on MNet.</p>
<p>The qualifications you add for an employee will appear on their business cards when required.</p>
<% using (Html.BeginForm("EditEmployee", "HumanResources", FormMethod.Post,
new{enctype = "multipart/form-data"}))
{%>
<%: Html.AntiForgeryToken() %>
<%: Html.ValidationSummary(true) %>
<%: Html.EditorFor(model => model.EmployeeSelector) %>
<% if (Model.SelectedEmployee != null)
{ %>
<%: Html.HiddenFor(model => model.SelectedEmployee.EmployeeId) %>
<%: Html.HiddenFor(model => model.EmployeeName) %>
<table class="groupBorder" style="margin-top:15px; width:617px;">
<tbody>
<tr>
<th colspan="2">Add Details for <%: Model.EmployeeName %></th>
</tr>
<tr>
<td style="text-align: right;">
<%: Html.LabelFor(model => model.SelectedEmployee.Photo)%>
</td>
<td>
<input type="file" id="Picture" name="Picture" />
</td>
</tr>
<tr>
<td style="text-align: right;">
<%: Html.LabelFor(model => model.SelectedEmployee.Qualifications)%>
</td>
<td>
<%: Html.TextBoxFor(model => model.SelectedEmployee.Qualifications, new {style = "width:500px;"})%>
</td>
</tr>
<tr>
<td colspan="2" style="text-align: center;padding-top:20px;">
<input type="submit" value="Save" id="btnSubmit" /></td>
</tr>
</table>
<% } %>
<% } %>
</fieldset>
</asp:Content>
When you click on the Save button you go to this controller action;
[HttpPost]
[Authorize(Roles = "Administrator, HumanResources, ManagerAccounts, ManagerIT")]
[ValidateAntiForgeryToken]
[ValidateOnlyIncomingValues]
public ActionResult EditEmployee(HrViewModel hrvm)
{
if (ModelState.IsValid)
{
if (hrvm.SelectedEmployee == null
|| hrvm.EmployeeSelector.SearchTextId != hrvm.SelectedEmployee.EmployeeId)
{
return this.RedirectToAction(
"EditEmployee", new { employeeId = hrvm.EmployeeSelector.SearchTextId });
}
if (hrvm.SelectedEmployee.Picture.HasFile())
{
var destinationFolder = Server.MapPath("/Users");
var postedFile = hrvm.SelectedEmployee.Picture;
var fileName = Path.GetFileName(postedFile.FileName);
var path = Path.Combine(destinationFolder, fileName);
postedFile.SaveAs(path);
hrvm.SelectedEmployee.Photo = path;
}
var emp = Employee.GetEmployee(hrvm.SelectedEmployee.EmployeeId);
this.TryUpdateModel<IEmployeeHrBindable>(emp, "SelectedEmployee");
emp.Update();
this.TempData["Message"] = string.Format(
"At {0} Details updated for {1}", DateTime.Now.ToString("T"), hrvm.EmployeeName);
return this.View(hrvm);
}
return this.View(new HrViewModel());
}
So what am I doing wrong?

By default, MVC3 performs model binding based on the Name attribute of the input elements in your view.
To get file upload data, use the HttpPostedFileBase class as a parameter to your ActionResult and call the parameter 'file'.
[HttpPost]
[Authorize(Roles = "Administrator, HumanResources, ManagerAccounts, ManagerIT")]
[ValidateAntiForgeryToken]
[ValidateOnlyIncomingValues]
public ActionResult EditEmployee(HrViewModel hrvm, HttpPostedFileBase file)
{
if (ModelState.IsValid)
{
if (hrvm.SelectedEmployee == null
|| hrvm.EmployeeSelector.SearchTextId != hrvm.SelectedEmployee.EmployeeId)
{
return this.RedirectToAction(
"EditEmployee", new { employeeId = hrvm.EmployeeSelector.SearchTextId });
}
if (file.ContentLength > 0)
{
hrvm.SelectedEmployee.Picture = file;
var destinationFolder = Server.MapPath("/Users");
var postedFile = hrvm.SelectedEmployee.Picture;
var fileName = Path.GetFileName(postedFile.FileName);
var path = Path.Combine(destinationFolder, fileName);
postedFile.SaveAs(path);
hrvm.SelectedEmployee.Photo = path;
}
var emp = Employee.GetEmployee(hrvm.SelectedEmployee.EmployeeId);
this.TryUpdateModel<IEmployeeHrBindable>(emp, "SelectedEmployee");
emp.Update();
this.TempData["Message"] = string.Format(
"At {0} Details updated for {1}", DateTime.Now.ToString("T"), hrvm.EmployeeName);
return this.View(hrvm);
}
return this.View(new HrViewModel());
}
(So if you could grab the image data using model binding, it would have been located at hrvm.Picture instead of hrvm.SelectedEmployee.Picture)

In your view use the following instead and the default model binding should work:
<%: Html.TextBoxFor(model => model.SelectedEmployee.Photo, new { type = "file" }) %>
That is assuming SelectedEmployee.Photo is of type HttpPostedFileBase.
The reason it isn't working at the moment is that the default model binder will be trying to find a property called Picture directly on the model, because that's the name of your file input. It won't find it, because Picture is a property of SelectedEmployee.
Changing it to what I've suggested above generates the correct id and name for the file input in the markup so when posted back has the correct path. This means the default model binder is then able to map between the form post value and the property.

Related

Asp.net MVC pass input value from view to controller

I've never worked with both asp.net and mvc.
I need to create simple registration form but I can't pass value from input to controller. I'm not using model and if I just could get values from view to controller there is server side function in my controller which will add inputs to database.
I searched a lot but there is always answers with using razor and html.beginform etc. and I don't have any of them.
Here is my view:
<%# Page Language="C#" MasterPageFile="~/Views/Shared/Main.Master" Inherits="System.Web.Mvc.ViewPage" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Index
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<table width="100%">
<tbody>
<tr>
<td valign="top" style="width:300px">
<fieldset>
<legend><b>Registration</b></legend>
<table class="submit">
<tr>
<td>Customer Code:</td>
<td style="width: 50%">
<%: Html.TextBox("cbCode")%>
</td>
</tr>
<tr>
<td>Card No:</td>
<td>
<%: Html.TextBox("cardNo")%>
</td>
</tr>
<tr>
<td>E-Code:</td>
<td>
<%: Html.TextBox("pswrd")%>
</td>
</tr>
<tr>
<td>E-Token:</td>
<td>
<%: Html.TextBox("tokenId")%>
</td>
</tr>
<tr>
<td>
<button type="submit" onclick="tokenSubmit('POST');" class="btn">
Submit</button>
</td>
</tr>
</table>
</fieldset>
<legend><b>Result</b></legend>
<div id="Result">
</div>
</fieldset>
</td>
</tr>
</tbody>
</table>
</asp:Content>
My controller:
namespace Branch.Controllers
{
public class CardEcodeController : Controller
{
//
// GET: /CardEcode/
public ActionResult Index()
{
long cbCode = value from input;
long cardNo = value from input;
long tokenId = value from input;
long pswrd = value from input;
//using functions written in server side
RegisterClient reg = new RegisterClient();
reg.InsertToken(cbCode,cardNo,tokenId,pswrd);
return View();
}
}
}
I think my MVC version is 2 or 3.
I strongly recommend you to read up on working with MVC. The version you are using is MVC2 most likely. If you can I would do the project in MVC3 or MVC4 to use Razor syntax. The quick answer to your question is this.
<%using (Html.BeginForm("Index", "CardEcode", FormMethod.Post)){%>
<table width="100%">
<!-- table code here -->
</table>
<% } %>
For your controller you have to make a post action.
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(long cbCode, long cardNo, long tokenId, long pswrd)
{
//using functions written in server side
RegisterClient reg = new RegisterClient();
reg.InsertToken(cbCode,cardNo,tokenId,pswrd);
return View();
}

Calling PostMethod from HTML.Actionlink

Hi I am fairly new to MVC and facing a issue.
I have a View which lists all the countries having just 2 columns
I want column headers clickable and should sort on clicking on it
I have shown my Controller & view code below and I expect that when I click on the column header it should hit Index action method decorated with [HttpPost]
When I click the header link page just refreshes without hitting Action method I expect it to hit
Any help would be appreciated.
Here is my controller code
[HttpPost]
public ActionResult Index(string sortcolumn)
{
return View(SortedList(sortcolumn));
}
private List<Country> SortedList(string sortcol)
{
List<Country> sortedlist = new List<Country>();
switch (sortcol)
{
case "idCountry":
if ((SortOrder)ViewData["sortorder"] == SortOrder.Ascending)
{
sortedlist = db.Countries.OrderByDescending(c => c.idCountry).ToList<Country>();
ViewData["sortorder"] = SortOrder.Descending;
}
else
{
sortedlist = db.Countries.OrderBy(c => c.idCountry).ToList<Country>();
ViewData["sortorder"] = SortOrder.Ascending;
}
break;
case "Countryname":
if ((SortOrder)ViewData["sortorder"] == SortOrder.Ascending)
{
sortedlist = db.Countries.OrderByDescending(c => c.Countryname).ToList<Country>();
ViewData["sortorder"] = SortOrder.Descending;
}
else
{
sortedlist = db.Countries.OrderBy(c => c.Countryname).ToList<Country>();
ViewData["sortorder"] = SortOrder.Ascending;
}
break;
}
return sortedlist;
}
Here is my view
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IEnumerable<eduSmart.Data.Entities.Country>>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Index
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>
List of Countries</h2>
<p>
Manage list of countries on this page. You can choose your action creating, editing,
removing the country data.
</p>
<% using (Html.BeginForm("Index","Country")) { %>
Total Countries : <%: Model.Count() %>
<table>
<tr>
<th>
<%: Html.ActionLink("CountryID","Index",new { sortcolumn = "CountryID" }) %>
</th>
<th>
<%: Html.ActionLink("CountryName ", "Index", new { sortcolumn = "CountryName" })%>
</th>
<th>
</th>
</tr>
<% if (Model != null)
{ %>
<% foreach (var item in Model)
{ %>
<tr>
<td>
<%: item.idCountry%>
</td>
<td>
<%: item.Countryname%>
</td>
<td>
<%: Html.ActionLink("Edit", "Edit", new { id = item.idCountry })%>
|
<%: Html.ActionLink("Details", "Details", new { id = item.idCountry })%>
|
<%: Html.ActionLink("Delete", "Delete", new { id = item.idCountry })%>
</td>
</tr>
<% }
}%>
</table>
<%} %>
<p>
<%: Html.ActionLink("Create New", "Create") %>
</p>
</asp:Content>
You can't hit a post method with an Action link. One thing you could do, if all you're doing is sorting here, is change your post method to a get and change the name
Instead of
[HttpPost]
public ActionResult Index(string sortcolumn)
Have something like
public ActionResult Sort (string sortColumn)
And point your actionlink to Sort.

How to check if checkbox is checked

I have question about accessing html.checkbox() in controller method.
In my view i have
<% foreach (var item in Model.PredmetTbl){ %>
<td>
<%:Html.CheckBox(item.Predmet) %>
<%:item.Predmet %>
</td>
<%} %>
Predemts are in DB and I want create new db records. How can i test if the checbox is checked or not ?
My controller code
[HttpPost]
public ActionResult PridajSaduPredmetov(int id, FormCollection data)
{
var zoznam = from predmet in ziakDB.PredmetTables select predmet;
ZoznamPredmetovTable predmety;
foreach (var item in zoznam)
{
if (HERE TESTING IF CHECKED)//IF Checked==true will add to db
{
predmety = new ZoznamPredmetovTable();
predmety.ZiakID = id;
predmety.PredmetID = item.PredmetID;
predmety.SkolskyRokID = IndexViewModel.GetSkolskyRokTeraz();
try
{
ziakDB.ZoznamPredmetovTables.InsertOnSubmit(predmety);
ziakDB.SubmitChanges();
}
catch { }
}
}
return RedirectToAction("DetailZiaka", "Administration", new { id = id });
}
(controlid).checked will return true/false
I did not make use of the html checkbox extension but I based my solution on this post:
How to handle checkboxes in ASP.NET MVC forms?
<% For Each item As x In Model.predmetTbl%>
<div><input type="checkbox" name="SelectedPredMet"
<% If Model.SelectedPredMet.Contains(item.Id) Then%>
checked="checked"
<% End If %>
value="<%: item.Id %>" /> <%: item.Predmet %></div>
<% Next%>

How to bind a List of ViewModel in ASP MVC?

Does anyone know how to get POST values for MODELVIEW Pattern below. I can display the MenuItem as Checkboxes and Radio buttons,
but when user submits the form i.e. POST, ModelViewTest is null. I'm expecting List of MenuItems that user have selected.
public class ModelViewTest
{
public IEnumerable<MenuItem> MenuItemList { get; set; } //Will be displayed as listboxes and checkboxes
public Restaurant restaurant {get;set;}
}
ACTIONS:
public ActionResult Edit()
{
//some code here
}
return View(new ModelViewTest());
}
[HttpPost]
public ActionResult Edit(ModelViewTest model)
{
//I'm not getting List of MenuItems
return View();
}
MenuItem Class:
public class MenuItem
{
public string MenuItemCode{get;set;}
public string MenuItemDescription{get;set;}
public string UIType {get;set;} //This determines whether it's radio or checkbox
public string UIGroupType {get;set;} //Determines the Group for radio/checkbox.
}
public class Restaurant
{
public string restaurantName{get;set;}
public MenuItem MenuItem{get;set;}
}
Update
Please see my View code snippet below:
<table>
#foreach (var menu in Model.MenuList)
{
if (menu.UIType == "Radio")
{
<tr>
<td align="left">
<input id="MenuCheckboxRadio" name="#Menus.UIGroup" value="#Menu.MenuItemCode" type="radio" />
<label>#Menu.MenuItemDescription</label>
</td>
</tr>
}
else
{
<tr>
<td align="left">
<input id="MenuCheckbox" name="#Menus.UIGroup" value="#Menus.#MenuItem" type="checkbox" />
<label>#Menu.MenuItemDescription</label>
</td>
</tr>
}
i++;
}
</table>
In order to get the list of menu items in the POST action you need their corresponding values must be included in the html <form> and because this is a collection follow the standard naming convention so that the default model binder can parse them.
First you should show us your view to know how you render your ViewModel.
However try this:
make partial view to be editor template for your MenyItem
<%# Control Inherits="ViewUserControl<MenyItem>" %>
<%: Html.TextBoxFor(m => m.MenuItemCode) %>
<%: Html.TextBoxFor(m => m.MenuItemDescription) %>
.......
then in your view make for loop NOT foreach:
<%# Page Inherits="ViewPage<ModelViewTest>" %>
<% using (Html.BeginForm()) {%>
<% for (int i = 0; i < 3; i++) { %>
<%: Html.EditorFor(m => m.MenuItemList[i]) %>
<% } %>
<% } %>
And please see this answer
Please see my View code snippet below:
<table>
#foreach (var menu in Model.MenuList)
{
if (menu.UIType == "Radio")
{
<tr>
<td align="left">
<input id="MenuCheckboxRadio" name="#Menus.UIGroup" value="#Menu.MenuItemCode" type="radio" />
<label>#Menu.MenuItemDescription</label>
</td>
</tr>
}
else
{
<tr>
<td align="left">
<input id="MenuCheckbox" name="#Menus.UIGroup" value="#Menus.#MenuItem" type="checkbox" />
<label>#Menu.MenuItemDescription</label>
</td>
</tr>
}
i++;
}
</table>

delete rows of table on checking of checkboxes

I have table containing data . In every row there is a checkbox plus a checkbox to select all checkbox at the headers.
Upon checking this checkboxes,corresponoding rows are to be deleted from database table.Plus,on chiecking the checkbox at the header,all rows will be deleted from the database table.How can i achieve this asp.net mvc.
As always start with a model:
public class ProductViewModel
{
public int Id { get; set; }
public string Name { get; set; }
}
Then a controller:
public class HomeController : Controller
{
// TODO: Fetch this from a repository
private static List<ProductViewModel> _products = new List<ProductViewModel>
{
new ProductViewModel { Id = 1, Name = "Product 1" },
new ProductViewModel { Id = 2, Name = "Product 2" },
new ProductViewModel { Id = 3, Name = "Product 3" },
new ProductViewModel { Id = 4, Name = "Product 4" },
new ProductViewModel { Id = 5, Name = "Product 5" },
};
public ActionResult Index()
{
return View(_products);
}
[HttpPost]
public ActionResult Delete(IEnumerable<int> productIdsToDelete)
{
// TODO: Perform the delete from a repository
_products.RemoveAll(p => productIdsToDelete.Contains(p.Id));
return RedirectToAction("index");
}
}
And finally the Index.aspx view:
<% using (Html.BeginForm("delete", "home", FormMethod.Post)) { %>
<table>
<thead>
<tr>
<th>Name</th>
<th>Select</th>
</tr>
</thead>
<tbody>
<%= Html.EditorForModel()%>
</tbody>
</table>
<input type="submit" value="Delete selected products" />
<% } %>
And the product editor template (~/Views/Home/EditorTemplates/ProductViewModel.ascx):
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<ToDD.Controllers.ProductViewModel>" %>
<tr>
<td>
<%: Model.Name %>
</td>
<td>
<input type="checkbox" name="productIdsToDelete" value="<%: Model.Id %>" />
</td>
</tr>
I would use AJAX. On changing the checked state, I would submit a request to delete all the selected IDs and refresh the table data.
Use jQuery, some other javascript library, or just hand code an AJAX request on check of checkbox. Then alter the DOM on success.
Using jQuery you could do something like:
<table>
<tr>
<td><input type="checkbox" class="check" id="1" /></td>
</tr>
<tr>
<td><input type="checkbox" class="check" id="2" /></td>
</tr>
<tr>
<td><input type="checkbox" class="check" id="3" /></td>
</tr>
</table>
$('.check').click(function() {
var tableRow = $(this).parent().parent();
var id = $(this).attr('id');
$.ajax({
url: 'http://www.YOURDOMAIN.com/Controller/Action/' + id,
success: function(data) {
$(tableRow).remove();
}
});
)};
This is very basic implementation, you could dress it up with some animation in the removal of the row. You also need to pass data and return data with some error handling. Check out here for a jQuery AJAX tutorial.

Resources