MVC 5 Delete Action in Details Page - model-view-controller

In this MVC5 project, I want my Details page to also serve as Edit page and Delete page.
I'm tackling this task by creating 2 forms, one with everything needed to update the data, including the submit button.
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.HiddenFor(model => model.EmpresaID)
...
<input type="submit" class="btn btn-primary" value="Submeter" />
}
Now, for my second form, I basicly have a Delete button:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.HiddenFor(model => model.EmpresaID)
<input type="submit" class="btn btn-danger" value="Delete" />
}
Error:
The parameters dictionary contains a null entry for parameter 'id' of
non-nullable type 'System.Int32' for method
'System.Threading.Tasks.Task`1[System.Web.Mvc.ActionResult]
DeleteConfirmed(Int32)'
I tried to use a ActionLink, but then I get a HTTP 404. Which is odd, since I am being sent to the correct destination:
#Html.ActionLink("Delete", "Delete", new { id = Model.EmpresaID })
Sends to
.../Empresa/Delete/6
EDIT1
Action Method:
// POST: Empresa/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> DeleteConfirmed(int id)
{
Empresa empresa = await db.Empresas.FindAsync(id);
db.Empresas.Remove(empresa);
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
EDIT2
Action Method
// POST: Empresa/Delete/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Delete([Bind(Include = "EmpresaID,Nome,Estado,Severidade,Inicio,Fim")] Empresa empresa)
{
//Do something with the posted viewModel
Empresa e = await db.Empresas.FindAsync(empresa.EmpresaID);
db.Empresas.Remove(e);
return RedirectToAction("Index");
}
Details.cshtml:
#using (Html.BeginForm("Delete", "Empresa", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(model => model.EmpresaID)
#Html.ActionLink("Delete", "Delete", new { id = Model.EmpresaID })
<button type="submit" class="btn btn-danger" value="Delete">Delete</button>
}
The ActionLink does not show any errors, but it doesn't delete anything either.
The Button gives me a HTTP 404. What am I doing wrong here?
EDIT3
// POST: Empresa/Delete/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Delete([Bind(Include = "EmpresaID,Nome,Estado,Severidade,Inicio,Fim")] Empresa empresa)
{
//Do something with the posted viewModel
Empresa e = await db.Empresas.FindAsync(empresa.EmpresaID);
db.Empresas.Remove(e);
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
The only problem with EDIT2 was that I forgot to save the changes.
It's now working properly.

You need to tell the form which action to complete along with the FormMethod i.e. GET or POST. So for your delete action for example, something like this:
#model MyProject.SomeViewModel
#using (Html.BeginForm("Delete", "Empresa", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(model => model.EmpresaId)
<button type="submit" class="btn btn-danger" value="Delete">Delete</button>
}
And then in your controller something like:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Delete(SomeViewModel viewModel)
{
//Do something with the posted viewModel
return RedirectToAction("Index");
}

// EmpressasController
// POST: Empresas/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
var empresa = _db.Empresas.Find(id);
if (empresa != null) _db.Empresas.Remove(empresa);
_db.SaveChanges();
return RedirectToAction("Index");
}
// Razor View
#using (Html.BeginForm("Delete", "Empresas", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(model => model.EmpresaId)
<button type="submit" class="btn btn-danger">Delete</button>
}

Related

Form not reloaded with new ViewModel when posting on .Net Core 3.1

I am trying to change the value on a screen after calling a post method. It seems even with the ModelState.Clear(); method added it still does not reflect the new value on the screen. Note that I am using jQuery Unobtrusive AJAX v3.2.6 library.
Please see my razor page and controller code below.
#model TestApp.ViewModels.EmployeeViewModel
<form method="post"
data-ajax="true"
data-ajax-method="post"
data-ajax-url="#Url.Action("Detail", "Employee")">
<input asp-for="EmployeeFirstName" />
<button type="submit" class="btn btn-primary">Submit</button>
</form>
Controller
public IActionResult Detail()
{
EmployeeViewModel employeeViewModel= new EmployeeViewModel ();
employeeViewModel.EmployeeFirstName = "John";
return View(employeeViewModel);
}
[HttpPost]
public IActionResult Detail(EmployeeViewModel employeeViewModel)
{
employeeViewModel.EmployeeFirstName = "Brian";
ModelState.Clear();
return View(employeeViewModel);
}
Since you use unobtrusive ajax,so it will return the html code of the partial view,but it will not refresh the partial view.If you want to refresh the partial view,you can try to replace the html code with data-ajax-complete.Here is a working demo:
Controller:
public IActionResult Detail()
{
EmployeeViewModel employeeViewModel = new EmployeeViewModel();
employeeViewModel.EmployeeFirstName = "John";
return View(employeeViewModel);
}
[HttpPost]
public IActionResult Detail(EmployeeViewModel employeeViewModel)
{
employeeViewModel.EmployeeFirstName = "Brian";
ModelState.Clear();
return PartialView("~/Views/_Partial.cshtml", employeeViewModel);
}
View:
#model WebApplication107.Models.EmployeeViewModel
<div class="row main-section-border">
<div id="div1" class="col-sm-12">
#{
await Html.RenderPartialAsync("~/Views/_Partial.cshtml", Model);
}
</div>
</div>
#section scripts{
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-ajax-unobtrusive/3.2.6/jquery.unobtrusive-ajax.js"></script>
<script>
completed = function (xhr) {
$("#div1").html(xhr.responseText);
};
</script>
}
_Partial view:
#model WebApplication107.Models.EmployeeViewModel
<form method="post"
data-ajax="true"
data-ajax-method="post"
data-ajax-url="#Url.Action("Detail", "Test")"
data-ajax-complete="completed"
>
<input asp-for="EmployeeFirstName" />
<button type="submit" class="btn btn-primary">Submit</button>
</form>
Result:

Load/Refresh only part of a page (View) using AJAX in ASP.NET MVC

I am trying to achieve the same result as mentioned by the OP in this post However when I try to render partial view by checking if it was an AJAX request in my Index action, its evaluating to false.
My Index View:
#using (Ajax.BeginForm("Index", "Home",
new AjaxOptions()
{
HttpMethod = "GET",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "restaurantList"
}))
{
<input type="search" name="searchTerm" />
<input type="submit" value="Search By Name" />
}
#Html.Partial("_Restaurant",Model)
My Partial View:
<div id="restaurantList" style="border:2px dotted red; padding-left:2em; margin-top:4px;">
#foreach (var item in Model)
{
<div>
<h4>#item.Name</h4>
<div>#item.City, #item.Country</div>
<div>#item.CountOfReviews</div>
<hr />
</div>
}
</div>
My Index Action:
public ActionResult Index(string searchTerm = null)
{
var model = ...//Building model object here
if (Request.IsAjaxRequest())
{
return PartialView("_Restaurant", model);
}
return View(model);
I would prefer not to dive into use of any jQuery or javascript as I am in the process of learning ASP.NET MVC, and would want to know why the approach I took is not working? The second answer by Dennis, in the post that I referenced also suggested similar approach.
Could someone kindly tell me what I am doing wrong?
Thanks
This is just an example how you can load view from AJAX without page refresh, it may help you.
It send text value to controller by ajax call and load that value in other view which replace main view, if you don't want to replace main view then you can take other div instead same div to load content.
Controller:
public ActionResult Index()
{
return View();
}
[HttpPost]
public PartialViewResult TestAjax(string Name)
{
ViewBag.Name = Name;
return PartialView();
}
Index.cshtml:
<input type="button" id="btnSearch" class="btn btn-warning" style="height:35px;width:120px" value="Search"/>
<label>Name:</label><input type="text" id="txtName" name="txtName" />
<script>
$('#btnSearch').click(function () {
$.ajax({
url: '#Url.Action("TestAjax", "Home")',
data: { Name: $("#txtName").val() },
type: 'POST',
success: function (data) {
$("#divContent").html(data);
}
});
});
</script>
TestAjax.cshtml:
#ViewBag.Name
As #StephenMuecke pointed out in his comments, the libraries not being loaded correctly was the problem. While creating a new bundle of all libraries and adding it in the BundkeConfig.cs I had missed ~/Scripts/jquery.unobtrusive-ajax.js*. Got it working now.

MVC3 form posting with value and file

I am new to MVC3 and I would like to create a form with input column and file upload.
The problem comes when I try to do both thing at the same time.
Here is my code
...
[HttpPost]
public ActionResult About(string inputStr)
{
string local = inputStr;
string[] word = inputStr.Split(':');
return View();
}
[HttpPost]
public ActionResult GetFile(string inputStr, HttpPostedFileBase file)
{
string filename = file.FileName;
return RedirectToAction("About");
}
These two are my controllers
#using (Html.BeginForm("GetFile", "Home", (new { inputStr = "111" }), FormMethod.Post, new { enctype = "multipart/form-data" })){
<div class="editor">
<input type="file" name="file" />
<input type="submit" value="OK" id="submitFile" class="testingSubmit"/>
</div>
}
This code works well for uploading files, and sending string "111" to the controller.
Here is another jQuery function
$('.testingSubmit').click(function () {
var totalString="";
$('.editor-field :input').each(function () {
alert($(this).val());
totalString += $(this).val().toString() + ":";
});
$('form').submit();
/* $.post("About", { inputStr: totalString}, function (data) {
});*/
});
Here, what I am trying to do is the get the user input and put it on string totalString.
I was able to post the totalString to the controller by using $.post
My questions are:
1. Am i on the right track? i.e. Is that possible to do those two tasks together with one post?
2. If not, what are the possible solution for this?
Thank you very much for your attention and hopefully this can be solved!
I think something like this should work:
#using (Html.BeginForm("GetFile", "Home", (new { inputStr = "111" }), FormMethod.Post, new { #id = "frmGetFile", enctype = "multipart/form-data" })){
<div class="editor">
<input type="file" name="file" />
<input type="hidden" name="totalString" id="totalString"/>
<input type="submit" value="OK" id="submitFile" onclick="submitGetFileForm()" class="testingSubmit"/>
</div>
}
JavaScript:
function submitGetFileForm(e)
{
e.preventDefault();
var total = //build your total string here
$('#totalString').val(total);
$('#frmGetFile').submit();
}
Controller:
[HttpPost]
public ActionResult GetFile(string totalString, HttpPostedFileBase file)
{
// your action logic..
}

Submitting a form view to controller

I have a form in my form view defined as
using (Html.BeginForm("Index", "Home", FormMethod.Post, new { enctype = "multipart/form-data" }))
This form contins a number of input of type button and button
<input type="submit" value="val1" name="action">
<input type="submit" value="val2" name="action">
<input type="submit" value="val3" name="action" />
<button class="button" type="submit" name="action" value="val4">Val4</button>
I have 2 controllers for this view
Public ActionResult form{
}
and
[HttpPost]
public ActionResult form(String button)
{
switch (actionType)
{
case "val1":
return RedirectToAction("AnotherView");
case "val2":
return RedirectToAction("AnotherView2");
default:
return RedirectToAction("AnotherView3");
}
}
But whichever button I click, I am being redirected to the Home defined in the form
using (Html.BeginForm("Index", "Home",
My question is how can I fix this and how can I be certain that this post method is bound to my view as i just typed it in?
using (Html.BeginForm("action_name", "controllername", FormMethod.GET, new { enctype = "multipart/form-data" }))
use input type select:
<select name="action">
<option value="val1" >val1</option>
<option value="val2" >val2</option>
<option value="val3" >val3</option></select>
and method at controller
public ActionResult action_name(String action){
switch (action)
{
case "val1":
return RedirectToAction("AnotherView");
case "val2":
return RedirectToAction("AnotherView2");
default:
return RedirectToAction("AnotherView3");
}
}
If you specify a form field named action MVC will interpret this as the action on the controller to route to. Thus clicking val1 will ultimately execute the method:
public ActionResult val1()
{
}
If this method does not exist, your error handling will take over.
Solution: Don't use action as a name of a form field.

Validation firing for both controls

I currently have two partial views on a single page, added with:
#Html.Action("_LogonBox","Account")
#Html.Action("_TrackingBox","Tracking")
each has its own form and model...
but if I enter values into the _logonbox view and submit it it causes the validation to fire on the TrackingBox and because the values in tracking box are empty it highlights the textboxes as errors.
How do I sort this out, in webforms it was simply validationGroups?
EDIT
here is the markup:
LogOn View
#model Models.LogonModel
#using (Html.BeginForm("Login", "account", FormMethod.Post, new { Id = "Login" }))
{
<div class="boxwrapper">
<div class="loginbox">
<a href="#" style="float: right; color: #fff; font-size: 95%%; padding: 5px 10px;">Forgotten
Password?</a>
<h3>
My Account</h3>
<div class="content">
<fieldset class="logincontrols">
<table>
<tr>
<td class="loginlabel">
#Html.LabelFor(m => m.UserName)
</td>
<td class="logintextbox">
#Html.TextBoxFor(m => m.UserName, new { ValidationGroup = "Account" })
</td>
</tr>
<tr>
<td class="loginlabel">
#Html.LabelFor(m => m.Password)
</td>
<td class="logintextbox">
#Html.PasswordFor(m => m.Password, new { ValidationGroup = "Account" })
<input type="image" value="Sign In" src="/Content/Images/buttons/signin.png" style="vertical-align: bottom;" ValidationGroup="Account" />
</td>
</tr>
</table>
</fieldset>
</div>
</div>
</div>}
Tracking View
#model .Models.TrackingModel
#using (Html.BeginForm("Index", "Tracking", new { Id = "Tracking" }))
{
<div class="boxwrapper">
<div class="bluebox">
<fieldset class="logincontrols">
<h3>
Shipment Tracking</h3>
<div class="content">
<p style="text-align: left;">
Please enter your reference number:</p>
#Html.TextBoxFor(m => m.TrackingNumber)
<br />
<p style="text-align: right; margin-top: 10px;">
<input type="image" value="Track Shipment" src="/Content/Images/buttons/trackingbutton.png" /> </p>
</div>
<fieldset>
</div>
</div>
}
Further EDIT
added controllers as requested
public class TrackingController : Controller
{
//
// GET: /Tracking/
public ActionResult Index()
{
return View();
}
[HttpPost]
public ActionResult Index(TrackingModel model)
{
return View(model);
}
[HttpPost]
public PartialViewResult _TrackingBox(TrackingModel model)
{
return PartialView(model);
}
public PartialViewResult _TrackingBox()
{
return PartialView();
}
}
public class AccountController : Controller
{
public ActionResult Login()
{
return View();
}
[HttpPost]
public ActionResult Login(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (Membership.ValidateUser(model.UserName, model.Password))
{
FormsAuthentication.SetAuthCookie(model.UserName, false);
if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/")
&& !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
public PartialViewResult _Logonbox()
{
return PartialView();
}
[HttpPost]
public PartialViewResult _Logonbox(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
//do something here
}
// If we got this far, something failed, redisplay form
return PartialView(model);
}
}
I have managed to fix this, but if someone can comment on WHY this fixes it that would be great.
I changed the #Html.Action("_partialViewName") to #Html.Partial("_partialViewName"), and this caused the same problems.
To fix this I had to include a new model object as below.
#Html.Partial("_LogonBox", new TGEFreight.UI.Web.Models.LogOnModel())
#Html.Partial("_TrackingBox", new TGEFreight.UI.Web.Models.TrackingModel())
As to why this works I dont know, this is probably due to my infancy with MVC, but this is the answer anyway.
Thanks for the help guys.
I'm guessing that both of these field sets are wrapped in the same <form>. You can get around this by having a separate <form> for each partial / child action.
Unlike webforms, MVC lets you take full advantage of HTML, meaning you can have more than 1 form on a page. In webforms, everything needed to be on 1 page so that viewstate could be persisted for the entire page during each postback.
If you are using jquery unobtrusive validation, validations only fire for the form that was submitted.
Edit
Sorry, I re-read your question and you say each of these has its own form. Can you show more of the razor markup? What does your submit button look like? Are there any jquery or javascript behaviors attached to the forms? Is validation firing on the client or the server?
Update
Have you tried using #Html.Partial instead of #Html.Action? Html.Action results in a new HTTP request to the child action method. What view does your login action on your account controller return? It could be that the login POST is calling the tracking action as a POST, and causing validation to fire on it.
I agree with #olivehour you should try using #Html.Partial instead of #Html.Action. Now when you say that if you use Partial that you can't define the action for them to use, what exactly do you mean. You should be able to define an HttpGet and HttpPost action without a problem for each of the partials, so for instance as an example you should be able to easily do the following:
public class AccountController : Controller
{
...
[HttpGet]
public ActionResult Login()
{
return PartialView();
}
[HttpPost]
public ActionResult Login(LoginModel model)
{
... Validate Model & additional logic
// return some redirect action
}
}
I noticed that in your post you are simply returning the PartialView again, was that just pseudo code? If not, at least for the login partial, I would assume that if someone logs in that the post action would redirect them to another page. In either case you would display the partial view by using #Html.Partial or #{Html.RenderPartial(...);} in your calling view. Hope that helps.

Resources