MVC 4 File Upload not working - file-upload

I am trying to simple upload an image but the HttpPostedFileBase in my controller is always remaining null.
Here is the page :
<% using(Html.BeginForm("AddPicture","Album",FormMethod.Post,new{enctype= "multipart/form-data" }))
{ %>
<%: Html.ValidationSummary(true) %>
<fieldset>
<legend>PictureModel</legend>
<div class="editor-label">
<%: Html.LabelFor(model => model.PrivacyTypeID) %>
</div>
<div class="editor-field">
<%: Html.DropDownList("PrivacyTypeID", null, new { name = "PrivacyTypeID", title = "Please select privacy type.", id = "PrivacyTypeID" }) %>
<%: Html.ValidationMessageFor(model => model.PrivacyTypeID) %>
</div>
<div>
Upload Image: <input type="file" name="File1" />
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
And Here is the Controller of the page:
[HttpPost]
public ActionResult AddPicture(Guid id, PictureModel model, HttpPostedFileBase File1)
{
try
{
if (ModelState.IsValid)
{
var file = Request.Files[0];
try
{
Guid albumid = id;
if (File1 != null)
{
.....
Can anyone please guide me what do i have wrong? I tried everything but no results

Change this line:
Upload Image: <input type="file" name="File1" id="File1" />
And change this:
public ActionResult AddPicture(Guid id, PictureModel model, HttpPostedFile File1)

A couple of things to note here that might be wrong, or not the correct way to do it :
Where is the Guid id in the form it should be set. Html.Hidden(yourid); or Html.HiddenFor(model => model.id); <-- make sure to set the model before hand on the latter e.g. :
public ActionResult Index(){
PictureModel p = new PictureModel();
p.id = Guid.NewGuid();
return View(p);
}
Use the proper method for dropdownList and your mapping is wrong too here :
The correct way is to use:
DropDownListFor(model => model.PrivacyTypeID, model => model.selectListOfPrivacyTypes, "-- Select Privacy Type --");
If you did want to use dropdownlist method then you should have used the name PictureModel.PrivacyTypeID instead of PrivacyTypeID to let the mapping work properly
I am not sure if it will fix the issue of the filehandling being null but it will definitly be better code.

Related

Submit button is submitting but not saving - MVC

I have tried many things to get the submit button to submit to the database. I'm not getting any errors, and the two error messages for testing, one from the controller, one from the view, are not printing to let me know that it is not working. Upon user hitting submit, it renders and displays the same button- Set Statement, instead of the new text that the user entered. Not sure how to fix or where the problem is.
Here is the code:
Controller:
[HttpPost, ValidateAntiForgeryToken]
public ActionResult Create(TableA Statement)
{
var model = new Test.Models.Tables();
using (var db = new Entities())
if (ModelState.IsValid)
{
TableA tablea = new TableA();
tablea.Statement = model.Statement;
db.TablesA.Add(tablea);
db.SaveChanges();
return RedirectToAction("Index");
}
else
{
return Content("Failed");
}
}
Main View:
<td>
#if (tablea.Statement != null)
{
#Html.DisplayFor(modelItem => tablea.Statement)
<text>|</text>
#Html.ActionLink("Edit", "Edit", new { id = tablea.ID })
}
else
{
Html.RenderAction("PopUp");
var model = new Test.Models.Tables();
if (model.Statement != null) {
<text>Save Successful!</text>
}
}
</td>
Popup View:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
=
<hr />
#Html.ValidationSummary(true)
<div class="form-group">
#Html.LabelFor(model => model.Statement, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Statement)
#Html.ValidationMessageFor(model => model.Statement)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" onsubmit="Create(Statement)" class="btn btn-default">
</div.. etc>
Popup View is a partial view or what?, I recommend convert it to partial view, and in your controller you can do something like
public ActionResult CreatePartial()
{
var pages = pageRepository.Pages;
return PartialView(pages);
}
and in your view use
#{Html.RenderAction("Popup", "CreatePartial");}
if your model of partial is different of model of main view, if not use:
#Html.RenderPartial("ViewName")

ASP.NET MVC 4 Submit from 2 partials on same view invokes both action methods for each submit

I am brand new to .NET MVC Programming - and have been stuck here with no where to go:
2 Models representing 2 tables in a db
public class t1
{
public string c1 { get; set; }
public string c2 { get; set; } ...
2 partial views one for each model
#model WebApplication2.Models.Database.t1
#using (Ajax.BeginForm( new AjaxOptions { UpdateTargetId = "t1div" }))
{
<fieldset>
#Html.EditorFor(model => model.c1)
<input type="submit" value="t1view" class="btn btn-default" />
....
#model WebApplication2.Models.Database.t2
#using (Ajax.BeginForm( new AjaxOptions { UpdateTargetId = "t2div" }))
{
<fieldset>
#Html.EditorFor(model => model.c4)
<input type="submit" value="t2view" class="btn btn-default" />
....
1 "composite" controller with actions to process submits from both partial views
public ActionResult t1view()
{
t1 model = new t1();....
}
[HttpPost]
public ActionResult t1view([Bind(Include = "c1,c2,c3")] t1 t1)
{
if (ModelState.IsValid)....
}
public ActionResult t2view()
{
t2 model = new t2();....
}
[HttpPost]
public ActionResult t2view([Bind(Include = "c4,c5,c6")] t2 t2)
{
if (ModelState.IsValid)....
}
and the index view
<div id="t1div">
#Html.Action("t1view")
</div>
<br />
<div id="t2div">
#Html.Action("t2view")
</div>
So the "composite" view with the 2 partial views displays data from the tables okay. When I make some changes and hit either of the buttons, even though I have specifically called the correct action method, both the action methods under [HttpPost] get called all the time, and one of the fails as that particular partialview just sent null for the "other" table. Any ideas what am I doing wrong here? Its just been a week of me learning this - what am I doing wrong here?
explicitly you have to give action property in ajax.beginForm for which action you are going to make request
#model WebApplication2.Models.Database.t1
#using (Ajax.BeginForm("t1view","controllerName", new AjaxOptions { UpdateTargetId = "t1div" }))
{
<fieldset>
#Html.EditorFor(model => model.c1)
<input type="submit" value="t1view" class="btn btn-default" />
....
#model WebApplication2.Models.Database.t2
#using (Ajax.BeginForm("t2view","controllerName", new AjaxOptions { UpdateTargetId = "t2div" }))
{
<fieldset>
#Html.EditorFor(model => model.c4)
<input type="submit" value="t2view" class="btn btn-default" />
....

Best way to handle 2 submit buttons for one Html.BeginForm

I have a Html.BeginForm in a view that contains details for a user: firstname, surname, email etc.
Then I have 2 buttons. Approve and Reject
When approve is clicked I go to one view.
When reject, I go to another.
What would be the best way to handle which one was clicked?
in my View:
<% using (Html.BeginForm("PublishGroupsForRecommendedUser", "Recommend", FormMethod.Post, new { id = ViewBag.UserId }))
{ %>
<div class="section _100">
<%: Html.LabelFor(model => model.Email)%>
<div>
<%: Html.EditorFor(model => model.Email)%>
<%: Html.ValidationMessageFor(model => model.Email)%>
</div>
</div>
//etc
<input type="hidden" name="action">
<div class="actions">
<div class="actions-right">
<input type="submit" value="Approve" class="submit_button" />
</div>
<div class="actions-left">
<input type="submit" value="Reject" class="submit_button" />
</div>
</div>
<% } %>
In my Controller:
[HttpPost]
public ActionResult PublishGroupsForRecommendedUser(int userId)
{
var recommendedUser = ZincService.UserService.GetRecommendedUserForId(userId);
var visibleGroups = ZincContext.CurrentUserGroups.Get();
var groupEntities = ZincService.GroupService.GetVisibleGroupsForUser(CurrentUser.UserId).ToList();
var viewModel = GetGroupPublishingViewModelForSelectedGroups(
recommendedUser.RecommendedUserId, Zinc.Enums.PublishingMode.ParticipatingGroupUsersOnly,
recommendedUser.Email, groupEntities);
return View(viewModel);
}
[HttpGet]
public ActionResult RejectUser(RecommendUserViewModel model)
{
Entities.RecommendedUser user = new RecommendedUser();
user.ReasonForRejection = model.ReasonForRejection;
ZincService.UserService.UpdateRecommendedUser(user);
return View(user);
}
So I cannot use the line using (Html.BeginForm("PublishGroupsForRecommendedUser", "Recommend", FormMethod.Post, new { id = ViewBag.UserId })) anymore because depending on which button was clicked I need to go to the PublishGroupsForRecommendedUser or RejectUser action which in turn will call the corresponding View.
Can someone recommend me the best way?
I'm not entirely sure what you want, but I think a little restructuring would help your code:
ASP.NET-MVC makes it easy to handle input from forms by using a specific ViewModel for your view. Make a property for everything you want to post back:
You make a simple POCO object, like:
public class Person {
public int ID { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public bool IsApproved { get; set; }
}
The ID is in a much better place on your Model than it is in ViewBag. It is a part of the model, don't be afraid to put it there. It will also be filled on getting the results of the post. Quite conveniently.
Let's say your first action is:
public ActionResult PersonForm()
{
var model = new Person()
{
ID = WhateverYouWant()
};
return this.View(model);
}
You can use it as a model from your View:
<%# Page Title="Anything" Language="C#" Inherits="System.Web.Mvc.ViewPage<MVC3ASPX.Models.Person>"... %>
...
...
<% using (Html.BeginForm())
{ %>
<%: Html.ValidationSummary(true) %>
<%: Html.HiddenFor(model => model.ID)%>
<div>
<%: Html.LabelFor(model => model.Name)%>
<div>
<%: Html.EditorFor(model => model.Name)%>
<%: Html.ValidationMessageFor(model => model.Name)%>
</div>
</div>
<div>
<%: Html.LabelFor(model => model.Age)%>
<div>
<%: Html.EditorFor(model => model.Age)%>
<%: Html.ValidationMessageFor(model => model.Age)%>
</div>
</div>
<% } %>
Notice how I made a hidden input field for the ID. It will get posted back.
Also notice that I didn't specify the method (get or post). Post is the default, it will suffice for our needs. I also didn't specify WHERE to post it. By default, the form will post back to the url it is on. In our case this will be the action PersonForm.
Posting 2 ways is not the best practice in ASP.NET. Post to one action and make an if there to decide what to do.
So make 2 buttons. <button>s are more flexible than <input>s for submitting something, as they can have different text and value.
<div class="actions">
<div class="actions-right">
<button type="submit" name="IsApproved" value="True" class="submit_button">Approve</button>
</div>
<div class="actions-left">
<button type="submit" name="IsApproved" value="False" class="submit_button">Reject</button>
</div>
</div>
Note that the buttons will have the text "Approve" and "Reject", but the value posted back will be True or False, depending where you clicked.
Your action handling the post should look like:
[HttpPost]
public ActionResult PersonForm(Person model)
{
if (this.ModelState.IsValid) // Validation never hurts
{
if (model.IsApproved)
{
return this.PersonFormApproved(model); // Your first method goes here
}
else
{
return this.PersonFormRejected(model); // Your second goes here
}
}
return this.View(model); // If the model's state is invalid, let's try this one more time!
}
In the model variable you will have every property filled from the values of your form. Also since there is a property called IsApproved, it will get filled by the form element of the same name. The button. And only the pressed one.
Notice that I have extracted most of the inner logic to methods: PersonFormApproved and PersonFormRejected. These should be private to avoid accidental calls by the program mistakenly thinking they are callable actions.
They should return ActionResult though, since PersonForm action will return their results.
Also check ModelState.IsValid. Only handle the info if it is valid. Check out DataAnnotations on how do you want your model to be validated.
If you use jquery, one solution could be,
Give the form an id, and set the action url for both submit button in the element as data attribute.
#using (Html.BeginForm(null, null, FormMethod.Post, new { id = "my-form" }))
{
//form elements
<input type="submit" name="action" value="Approve" data-action="#Url.Action("Approve", "YourController")" />
<input type="submit" name="action" value="Reject" data-action="#Url.Action("Reject", "YourController")"/>
}
and then attach the submit buttons click event with jquery and aattch the action to the form.
$(function () {
$('#my-form :submit').click(function (e) {
var button = $(this);
button.closest('form').attr('action', button.attr('data-action'));
});
});
hope this helps.
in my view:
<asp:Content ID="Content2" ContentPlaceHolderID="ScriptPlaceHolder" runat="server">
<script type="text/javascript">
function RejectUser(userId) {
$.ajax({
url: '<%= Url.Action("RejectUser", "Recommend") %>',
type: 'GET',
dataType: 'json',
data: { id: userId, reasonForRejection: $('#textarea').val() },
success: function (data) {
window.location.href = data.redirectTo;
}
});
}
</script>
</asp:Content>
<div class="actions">
<div class="actions-right">
<input type="submit" value="Approve" class="submit_button" />
</div>
<div class="actions-left">
Reject
</div>
</div>
and in the controller:
[HttpGet]
public JsonResult RejectUser(int id, string reasonForRejection)
{
if (!String.IsNullOrWhiteSpace(reasonForRejection))
{
Entities.RecommendedUser user = new RecommendedUser();
user = ZincService.UserService.GetRecommendedUserForId(id);
user.ReasonForRejection = reasonForRejection;
ZincService.UserService.UpdateRecommendedUser(user);
ZincService.SaveChanges();
}
return Json(new
{
redirectTo = Url.Action("RecommendedUsers"),
}, JsonRequestBehavior.AllowGet);
}
thanks all!

html.beginform doesn't post back to controller, Error Loading Page message?

I have been trying post a value back to the controller so I can update a balance elsewhere, but I cannot get it to work. I get a message
Error Loading Page
All I want is a double value, but to be honest, I would just like to get the code into the controller in the first instance.
My .cshtml is like:
#using (Html.BeginForm("Deposit", "AccountController", FormMethod.Post))
{
<fieldset>
<legend>Deposit Amount</legend>
<div class="editor-label">
#Html.LabelFor(model => model.UserName)
</div>
<div class="editor-field">
#Html.TextBoxFor(model => model.UserName)
#Html.ValidationMessageFor(model => model.UserName)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
And the controller is:
[HttpGet]
public ActionResult Deposit()
{
return View();
}
[HttpPost]
public ActionResult Deposit(LoginModel model)
{
// do things
return View(model);
}
I am new to MVC. I know the view code doesn't have the amount value that I ultimately would like to use, or I may add it to a model and just pass that, but I cannot get it to the controller at all.
Thanks in advance.
I think you're just slightly off on the naming conventions of MVC. Assuming the name of your controller class is AccountController, you should specify that minus the "Controller" part in the BeginForm() method:
#using (Html.BeginForm("Deposit", "Account", FormMethod.Post))
{
...
}

How to upload Photos/ Images in Asp.Net 4.o Project

I want to add upload photo features in my MVC 4.0 Project, Please can you tell me how can I do that. I am using MVC 4.o and for Photo option i want to give file / image upload. Here is my Code
#model WebCart.Category
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm("Create", "Category", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Category</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Title)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Title)
#Html.ValidationMessageFor(model => model.Title)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Description)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Description)
#Html.ValidationMessageFor(model => model.Description)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Photo)
</div>
<div class="editor-field">
<input name="PhotoFile" id="File" type="file"/>
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
[HttpPost]
[ValidateInput(false)]
public ActionResult Create(Category category)
{
if (ModelState.IsValid)
{
db.Categories.AddObject(category);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(category);
}
Your controller action could take an additional PhotoFile argument:
[HttpPost]
[ValidateInput(false)]
public ActionResult Create(Category category, HttpPostedFileBase photoFile)
{
if (ModelState.IsValid)
{
// you could manipulate the PhotoFile parameter here
// for example let's store the uploaded file to a folder on the server
if (photoFile != null && photoFile.ContentLength > 0)
{
var fileName = Path.GetFileName(photoFile.FileName);
var path = Path.Combine(Server.MapPath("~/App_Data"), fileName);
photoFile.SaveAs(path);
}
db.Categories.AddObject(category);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(category);
}
Or if you want to read the uploaded file as a byte array you could use the following:
byte[] photo = new byte[photoFile.InputStream.Length];
photoFile.InputStream.Read(photo, 0, photo.Length);
// Now you could assign the byte array to some property of your model
// and persist it into the database (even if this is a bad practice -
// it would be more correct to store the photo on the file system and
// only store the full path to the photo in the database)
A must read article for uploading files in ASP.NET MVC is the following: http://haacked.com/archive/2010/07/16/uploading-files-with-aspnetmvc.aspx