MVC5 One Click Delete from Details Page - asp.net-mvc

I started with the scaffolding that VS MVC 5 can create, and it was working fine, but I wanted to be able to delete records ("Interviews", in this case) from the details page.
I started by copying the markup from the delete button on the Delete page over to Details, but it would simply redirect to the Details action. How can I get a button on the Details page to run the DeleteConfirmed method?
Here is the relevant code from the controller:
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Interview interview = db.Interviews.Find(id);
if (interview == null)
{
return HttpNotFound();
}
return View(interview);
}
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
Interview interview = db.Interviews.Find(id);
db.Interviews.Remove(interview);
db.SaveChanges();
return RedirectToAction("Index");
}
public ActionResult Delete(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Interview interview = db.Interviews.Find(id);
if (interview == null)
{
return HttpNotFound();
}
return View(interview);
}
and here is the markup that I copied from the Delete page and put into the Details view:
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
<div class="form-actions no-color">
<input type="submit" value="Delete" class="btn btn-danger" />
</div>
}
Here is the markup I needed to make it work:
#using (Html.BeginForm("Delete", "Interviews", new { id = Model.ID })) {
#Html.AntiForgeryToken()
<div class="form-actions no-color">
<input type="submit" value="Delete" class="btn btn-danger" />
</div>
}

You need to post to the DeleteConfirm action. Here, you're posting to the Details action because you're using just Html.BeginForm(). You need:
#using (Html.BeginForm("Delete", new { id = Model.Id })) {

You can get delete confirm by javascript on onclickling method like this
//change input type to button to prevent form submit
<input **type="button"** onClick="deleteConfirm" value="Delete" class="btn btn-danger" />
function deleteConfirm()
{
$.ajax({
url: "DeleteConfirmed Action Url",
type:"post",
data:{id:}
success: function(response) {
//check your server side result
}
});
}

Related

I can't send a POST request from view to Controller in ASP.NET MVC

I have this form shown below:
#using (Html.BeginForm("NewArticle", "News",FormMethod.Post, new { name = "createForm", onsubmit = "return validateFormNewPost()" }))
{
#Html.TextBoxFor(p => p.adName, new { #class="form-control"})
#Html.DropDownListFor(p => p.tyepId, listType, "---Select Type---", new { #class = "form-control" })
#Html.TextBox("imgPath", "", new { #class = "form-control", id = "imgPath" })
#Html.TextAreaFor(p =>p.AdDescription,new { id = "editor" })
<button class="btn btn-reset btn-dark" type="reset">Reset</button>
<button class="btn-submit btn-primary btn" type="submit">Submit</button>
}
Controller:
[HttpGet]
public ActionResult NewArticle()
{
List<AdType> listType = typeModel.listForAdd();
SelectList s = new SelectList(listType, "id", "AdType1");
ViewBag.ListType = s;
return View();
}
[HttpPost]
public ActionResult NewArticle(Advertisement model,string imgPath)
{
// my code
return RedirectToAction("Category","News");
}
I'm trying to make a POST request by submit the form but it always send form data with a GET request. How can I fix it?
How are you sending the POST? Are you using AJAX or JavaScript from your MVC page or are you trying to hit the endpoint with an external application like Telerik Fiddler or Postman?
You can not make a POST request directly from the browser using the URL, my assumption is that may be what you are doing.
#using (Html.BeginForm("MethodNameinController", "Controller Name", FormMethod.Post))
{
//form fields with submit button
<input type="submit" value="Save" class="btn btn-default" id="create" />
}
And in Controller
[HttpPost]
public ActionResult MethodNameinController(Model model)
{
}

Direct delete link from index view instead of going to delete page

I am trying to create a link that will delete one of the records without the need to go to the delete page.
My controller for delete section:
// GET: Cars/Delete/5
public async Task<ActionResult> Delete(Guid? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Car car = await db.Cars.FirstOrDefaultAsync();
if (car == null)
{
return HttpNotFound();
}
return View(car);
}
// POST: Cars/Delete/5
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> DeleteConfirmed(Guid id)
{
Car car = await db.Cars.FindAsync(id);
db.Cars.Remove(car);
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
In the view I am trying to have a link to directly delete it like this:
<a href="#Url.Action("DeleteConfirmed", "Cars", new { id=item.Id })" class="btn btn-danger btn-sm" onclick = "return confirm('Are sure wants to delete?');">
<i class="fa fa-times" aria-hidden="true"></i>
</a>
The most common solution to do it is by creating a form and do it like this:
#using (Html.BeginForm("DeleteConfirmed", "Cars", new { id = item.Id }))
{
#Html.AntiForgeryToken()
<input type="submit" value="" class="btn btn-danger" onclick="return confirm('Are sure wants to delete?');" />
}
Is there is a way to make it without the need of using the form?
Yes. Please refer to the code below:
input type="button" id="btnDelete" class="btn btn-danger"
value="Delete" />
<script type="text/javascript">
$("#btnDelete").click(function () {
$.ajax({
url: "/Cars/Delete",
type: 'POST',
data: { id='-your-id' },
async: 'true',
dataType: 'html',
success: function (data) {
//Do your thing on Success if needed
}
});
});
</script>

Post Request is not returning same object as passed

I am trying to do a simple CRUD app asp.net core using MVC and the strangest thing is happening to me.
create the model and pass it in to the form but when I go to save it, it no longer has the Id that I passed into it.
[HttpGet]
public IActionResult CreateCompany(Guid id)
{
//id = 677b57f1-d0b2-484b-9892-b06e6eb9f1f7
var pageId = id;
var company = new CompanyListItem() {PublicPageId = pageId};
return View(company);
}
[HttpPost]
public IActionResult CreateCompany(CompanyListItem model)
{
//model.PublicPageId = 00000000-0000-0000-0000-000000000000
if (ModelState.IsValid)
{
model.Id = Guid.NewGuid();
var newModel = _companyDataProvider.Add(model);
PublicPageViewModel page = null;
if (newModel != null)
{
page = _pageDataProvider.GetPageIdFromCompanyListId(newModel.Id);
}
if (page != null)
{
return RedirectToAction("Details", page);
}
}
return View();
}
my form:
#model CompanyListItem
<h1>Add Company</h1>
<form method="post">
<div class="col-md-6">
<div class="form-group">
<label asp-for="Header"></label>
<input asp-for="Header" class="form-control"/>
</div>
<div>
<input type="submit" class="btn btn-success" value="save"/>
<a class="btn btn-default">Cancel</a>
</div>
</div>
</form>
Does anyone know why this is happening? Or how i can prevent it from happening?

MVC 3 - passing two parameters from view to controller

I have an issue with passing 2 parameters from view to controller, when assigning them as a button. If I use this code in my View:
#using (Html.BeginForm("Edit", "Shift", new { lineName = item.Line, dateTime=item.Date }))
{
<input type="submit" value="Edit"/>
}
I get this string as result, which doesn't work because ampersand is replaced with &
<form action="/Shift/Edit?lineName=Line%203&dateTime=04%2F01%2F2004%2007%3A00%3A00" method="post"> <input type="submit" value="Edit"/>
</form>
So to resolve that I found I could use the Html.Raw
#using (Html.Raw(Url.Action("Edit", "Shift", new { lineName = item.Line, dateTime=item.Date })))
{
<input type="submit" value="Edit"/>
}
But this give me error:
'System.Web.IHtmlString': type used in a using statement must be implicitly convertible to 'System.IDisposable'
My Controller metdhods: (Edited)
//Displays Edit screen for selected Shift
public ViewResult Edit(string lineName, DateTime dateTime)
{
Shift shift = repository.Shifts.FirstOrDefault(s => s.Line == lineName & s.Date == dateTime);
return View(shift);
}
//Save changes to the Shift
[HttpPost]
public ActionResult Edit(Shift shift)
{
// try to save data to database
try
{
if (ModelState.IsValid)
{
repository.SaveShift(shift);
TempData["message"] = string.Format("{0} has been saved", shift.Date);
return RedirectToAction("Index");
}
else
{
//return to shift view if there is something wrong with the data
return View(shift);
}
}
//Catchs conccurency exception and displays collision values next to the textboxes
catch (DbUpdateConcurrencyException ex)
{
return View(shift);
}
}
Could you please support me with this, I spend couple of days on this one now.
Thanks
According to my understanding of your code, I suggest you following solution:
In View:
#using (Html.BeginForm("Edit", "Shift", FormMethod.Post, new { enctype = "multipart/form-data"}))
{
<input type="hidden" name="lineName" value="#item.Line"/>
<input type="hidden" name="dateTime" value="#item.Date"/>
<input type="submit" value="Edit"/>
}
In Controller :-
[HttpPost]
public ActionResult Edit(datatype lineName , datatype dateTime)
{
}
Please correct me If I am wrong.
add parameter in controller method for eg.
View :-
#using (Html.BeginForm("Edit", "Shift", new { lineName = item.Line, dateTime=item.Date }))
{
<input type="submit" value="Edit"/>
}
Controller :-
public ActionResult yourMethod(datatype lineName , datatype dateTime)

Missing something in "Delete" post method in MVC (EF 4.1)

Following this example .. http://msdn.microsoft.com/en-us/data/gg685489
I am running into issues with Delete functionality.
[HttpPost]
public ActionResult Delete(int id, Blog blog)
{
try
{
using (var db = new BlogDataEntities())
{
//Having issue here.. as soon as the next line is run in debug
//mode .. it goes to catch.. and returns a view with null values.
db.Entry(blog).State = System.Data.EntityState.Deleted;
db.SaveChanges();
}
return RedirectToAction("Index");
}
catch
{
return View();
}
}
In the parameters I checked 'blog' does not get the actual blog model that needs to be deleted. All the other methods work fine (Edit, Delete (get)..etc..
but Delete post fails. Am I missing something? thanks in advance for the help.
EDIT:
view code
#model DBFirstMVC.Models.Blog
#{
ViewBag.Title = "Delete";
}
<h2>Delete</h2>
<h3>Are you sure you want to delete this?</h3>
<fieldset>
<legend>Blog</legend>
<div class="display-label">Title</div>
<div class="display-field">#Model.Title</div>
<div class="display-label">BloggerName</div>
<div class="display-field">#Model.BloggerName</div>
</fieldset>
#using (Html.BeginForm()) {
<p>
<input type="submit" value="Delete" /> |
#Html.ActionLink("Back to List", "Index")
</p>
}
EDIT 2:
Non Razor Code in view:
<% using (Html.BeginForm()) { %>
<p>
<input type="submit" value="Delete" /> |
<%: Html.ActionLink("Back to List", "Index") %>
</p>
<% } %>
EDIT 3: (I tried in aspx)
<% using (Html.BeginForm()) { %>
<p>
<%=Html.DisplayForModel();%> //Tried Html.EditorForModel also..
<input type="submit" value="Delete" /> |
<%: Html.ActionLink("Back to List", "Index") %>
</p>
<% } %>
FINAL EDIT (Corrected Solution)
#model DBFirstMVC.Models.Blog
#{
ViewBag.Title = "Delete";
}
<h2>Delete</h2>
<h3>Are you sure you want to delete this?</h3>
#using (Html.BeginForm()) {
<p>
<fieldset>
<legend>Blog</legend>
<div class="display-label">Title</div>
<div class="display-field">#Model.Title</div>
<div class="display-label">BloggerName</div>
<div class="display-field">#Model.BloggerName</div>
<input type="submit" value="Delete" /> |
#Html.ActionLink("Back to List", "Index")
</fieldset>
</p>
}
The context probably doesn't have an Entry for your Blog because it isn't attached to the Context.
You probably need to retrieve the Blog first and then mark it as deleted using the Entry method:
[HttpPost]
public ActionResult Delete(int id, Blog blog)
{
try
{
using (var db = new BlogDataEntities())
{
// retrieve the blog from the database
var realBlog = db.Blogs.Find(blog.Id);
// nothing to do here, just redirect
if( realBlog == null )
return RedirectToAction("Index");
// since you have the entity just do this instead:
db.Blogs.Remove(realBlog);
db.SaveChanges();
}
return RedirectToAction("Index");
}
catch( Exception )
{
return View();
}
}
I don't really agree with the idea of using your entities as your models though. You should use View Models instead.
EDIT
Since you now are saying that Blog isn't being passed, try this:
#model Blog
#using ( Html.BeginForm() )
{
#Html.EditorForModel()
<input type="submit" value="Delete" />
}
You weren't actually giving the model binder any of the details it needed to construct your model.
is it possible to try the following:
[HttpPost]
public ActionResult Delete(Blog deletedBlog)
{
try
{
using (var db = new BlogDataEntities())
{
// get blog entry from db context!!
Blog blog = db.Blogs.Find(deletedBlog.Id);
//Having issue here.. as soon as the next line is run in debug
//mode .. it goes to catch.. and returns a view with null values.
db.Entry(blog).State = System.Data.EntityState.Deleted;
db.SaveChanges();
}
return RedirectToAction("Index");
}
catch(Exception e)
{
// should catch more specific exception
// but catching 'e' should be a start
return View();
}
}
[Update] - pass in your Blog model from the view, tho as Dismissile says, you should really use a viewmodel, rather than the entity model for this purpose.
Also, you should catch the inner exception message and examine that for further clues.
The blog parameter in your Delete Action is null most likely because you are only posting the blog's id, not the entire blog object. I would either modify the Delete Action to accept just the id (per Dismissile's answer), or modify the Delete View to post the entire blog object and remove the id from the Action (since it belongs to the blog object):
[HttpPost]
public ActionResult Delete(Blog blog)
{
try
{
using (var db = new BlogDataEntities())
{
db.Entry(blog).State = System.Data.EntityState.Deleted;
db.SaveChanges();
}
return RedirectToAction("Index");
}
catch
{
return View();
}
}
I posted this in a few comments, but I felt it merited a separate answer for an "alternative".
Your controllers should be slim and adhere as best as possible to the single responsibility principle.
BlogController
public class BlogController : Controller
{
private BlogService blogService;
public BlogService()
{
blogService = new BlogService();
}
[HttpPost]
public ActionResult Delete(int id)
{
// make sure the user has permission to delete before actually deleting
// now that we know the user has permission
if (blogService.Delete(id))
{
return RedirectToAction("Index");
}
else
{
return View();
}
}
}
Now you'll have a reusable service layer that adheres to the single responsibility principle.
BlogService
public class BlogService
{
private BlogDataEntities dc;
public BlogService()
{
dc = new BlogDataEntities();
}
public bool Delete(int Id)
{
try
{
var blog= (from b in dc.Blogs where Blog.ID == Id select b).First();
// blog doesn't exist, exit and return false.
if( blog == null )
return false;
// blog exists, remove it
dc.Blogs.Remove(blog);
// push the delete to the database
SaveChanges();
// it worked, return true.
return true;
}
catch(System.Exception ex)
{
// an error happened, handle it and return false.
return false;
}
}
// I like to keep my `SubmitChanges()` Separate in case I need to
// stack up a few processes before hitting the database.
public void SaveChanges()
{
dc.SaveChanges();
}
}

Resources