Direct delete link from index view instead of going to delete page - asp.net-mvc

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>

Related

method is called twice

What was I doing wrong?
My method is called twice.
First time I get Id and it's correct behavior
Second time I get Null
I can't get it what is correct way to solve it?
My View
<div class="container">
<div class="row">
#foreach (var item in Model)
{
<div class="col-md-4">
<div class="card" style="width: 18rem;">
<img src="~/Content/not-found-image.jpg" class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title">#item.Name</h5>
<p class="card-text">Some quick example text to build on the card title and make up the bulk of the card's content.</p>
<div id="textButton">
<a class="btn btn-primary" data-index="#item.Id" name="link">Go to anywhere</a>
</div>
</div>
</div>
</div>
}
</div>
</div>
My Controller
[HttpGet]
public ActionResult ListCoach(int id)
{
if (id != null)
{
var ChoachList = _TraningService.Coach(id);
return View(ChoachList);
}
return HttpNotFound();
}
Script
I use script on My View use helper #Section {}
#section scripts {
#*<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.2.1.min.js"></script>*#
<script>
function PassToContoller(data) {
//alert(data);
$.ajax({
type: "GET",
url: '/TrainingType/ListCoach',
data: { id: data },
success: function (data) {
console.log(data);
window.location.href = '/TrainingType/ListCoach';
return data;
},
error: function () {
$("#loader").fadeOut("slow");
console.log("error1");
}
});
}
$(document).ready(function () {
$("a[name=link]").on("click", function () {
var valueOfClickedTag = $(this).data("index");
alert("Clicked: " + valueOfClickedTag);
var callFunc = PassToContoller(valueOfClickedTag)
});
$(":button[id=GoToAnywhere3]").on("click", function () {
var valueOfClickedBtn = $(this).data("index");
var callFunc = PassToContoller(valueOfClickedBtn)
});
});
</script>
}
if I use the method:
I can't get into View ListCoach
Can I return Json on My View?
[HttpGet]
public JsonResult ListCoach(int id)
{
var ChoachList = _TraningService.Coach(id);
return Json(ChoachList,JsonRequestBehavior.AllowGet);
}
Your code seems to be OK and in theory, your action should not call twice but I want to show you some ways in order to catch the problem:
when a call is made to the server in chrome developer tools in the tab network you will see a record in which there is a column named Initiator that shows you the call is created by who and where.
you can use keyword DEBUG in the first of function PassToContoller and look at the stack to know who one call your function
and about your final question, my answer is negative and you can not use this way to pass JSON object to your view but you can pass JSON object beside model by ViewBag or ViewData
In your function PassToContoller,in ajax success,you use window.location.href = '/TrainingType/ListCoach';,so after you call the function,it will have two requests,one with ajax,one with window.location.href.
If you want to get jsondata in ajax,you need to use dataType: "json", in ajax.
Here is a demo to pass json data:
Action:
[HttpGet]
public JsonResult GetJson(int id)
{
return new JsonResult(new TestModel { Id=id,Name="Test"});
}
View:
<button onclick="test()">
test
</button>
#section scripts
{
<script>
function test() {
$.ajax({
type: "GET",
url: 'GetJson',
data: { id: 1 },
dataType: "json",
success: function (data) {
console.log(data);
},
error: function () {
$("#loader").fadeOut("slow");
console.log("error1");
}
});
}
</script>
}
result:
Update:
You can try to use a form to pass Id to action(without using <a></a>):
<form asp-action="GetJson" asp-route-id="#item.Id">
<input type="submit" value="Go to anywhere" />
</form>
Action:
[HttpGet]
public IActionResult GetJson(int id)
{
return View("Index",new List<TestModel> { new TestModel { Id = id, Name = "Test" } });
}

How to create a partial view that posts

i have created a partial view (a button that has icon) like this:
#model int
<a type="button" class="btn btn-primary btn-sm" href="#Url.Action("Edit", new {id = Model})">
<span class="glyphicon glyphicon-pencil"></span>
<span>Save</span>
</a>
but this one uses the GET not the POST. also, how can I get all of the value's that I typed from the textboxes as parameter to the partial view?
Try this:-You will use AJAX call for post method and return partial view like below.
[HttpPost]
public ActionResult ActionForAjaxForm(FormModel model)
{
// do something like save the posted model
return PartialView("_YourPartialView", model);
}
Try this way, It may help you.
$.ajax({
type: "POST",
url: "/Client/ShowClient",
data: "ClientCode=" + code/*{ "ClientCode": code }*/, //First item has latest ID
contentType: "application/json; charset=utf-8",
dataType: "html",
success: function (data) {
$('#dialog').html(data);
},
failure: function (response) {
alert(response.responseText);
},
error: function (response) {
alert(response.responseText);
}
});
});
In Controller
[HttpPost]
public ActionResult ShowClient(string ClientCode)
{
if (ClientCode == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
ClientViewModel clientViewModel = this.Service.FindByClientCode(ClientCode);
if (clientViewModel == null)
{
return HttpNotFound();
}
return View("ShowClient", clientViewModel);
}
for more details please see Working example

MVC Bootstrap modal show validation result

The problem:
I want to preform submit to form that is in modal and if validation faild to get the error message on the modal.
I'm using ajax validation (jQuery) as detailed here
Is there an elegant way to perform submit but on faild stay at modal to show error message?
My code:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Cam c)
{
ViewBag.id = c.id;
using (Entities db = new Entities())
{
if (ModelState.IsValid)
{
db.camp.Add(c);
db.SaveChanges();
return RedirectToAction("Index", new { id = c.id });
}
}
return null;
}
Client:
#using (Html.BeginForm("Create", "Camp", FormMethod.Post, new { model =
Model }))
{
#Html.AntiForgeryToken()
<dt>
name:
</dt>
<dd>
#Html.TextBoxFor(model => model.name, new { #class = "form-control", #placeholder = "name", #id = "txtVenueID", style = "width:150px" })
</dd>
<dd>
#Html.ValidationMessageFor(model => model.name)
</dd>
<div class="modal-footer ">
<button type="button" class="btn btn-default" data-
dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Save</button>
</div>
}
Model:
public partial class Cam
{
[Display(Name = "Name")]
[Required(ErrorMessage = "Require {0}")]
string name { get; set; }
}
Show the first form in modal via the action that build it but keep the model state errors - use This
For view to submit the form but keep result on the modal use the following:
<button type="submit" class="btn btn-primary">Save</button>
Javascript to get the submit result into the modal:
$(function () {
$.ajaxSetup({ cache: false });
$(':submit[data-modal]').on("click", function (e) {
e.preventDefault();
var linkObj = $(this).closest('form');
$.ajax({ // create an AJAX call...
data: linkObj.serialize(), // get the form data
type: linkObj.attr('method'), // GET or POST
url: linkObj.attr('action'), // the file to call
success: function(response) { // on success..
$('#Modal-Content').html(response); // update the DIV
}
});
});
});

MVC 5 partial view async postback

I have a sort of Master-Detail Edit form and I'm trying to follow this post: Using Ajax... to get the partial view to postback.
My Edit form has a partial view that has a list of sub items, and another partial create view in it to add new items. I'd like the partial create view to post back and update the list without refreshing the whole page if possible.
Here's what I have so far:
MyController.cs -
public ActionResult Edit(int? id)
{
//...
ViewBag.CustomFormId = id;
using (var _db = new MkpContext())
{
//...
return View(profileEdit);
}
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(CustomForm editForm)
{
//...
if (!ModelState.IsValid) return View(editForm);
using (var _db = new MkpContext())
{
var form = _db.CustomForms.Find(editForm.CustomFormId);
//...
_db.Entry(form).State = EntityState.Modified;
_db.SaveChanges(User.ProfileId);
return RedirectToAction("Index");
}
}
public ActionResult _CustomFieldList(int id)
{
ViewBag.CustomFormId = id;
using (var _db = new MkpContext())
{
var formCustomFields = (from cf in _db.CustomFields
where cf.CustomFormId == id
select cf);
return PartialView(formCustomFields.ToList());
}
}
// Nested in _CustomFieldList
public ActionResult _CustomFieldCreate(int id)
{
var newField = new CustomField
{
CustomFormId = id
};
return PartialView(newField);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult _CustomFieldCreate(CustomField addField)
{
ViewBag.CustomFormId = addField.CustomFormId;
if (ModelState.IsValid)
{
using (var _db = new MkpContext())
{
_db.CustomFields.Add(addField);
_db.SaveChanges();
}
var newField = new CustomField
{
CustomFormId = addField.CustomFormId
};
return PartialView(newField); // Probably need to change this somehow
}
return PartialView(addField);
}
And the views:
Edit.cshtml -
#model PublicationSystem.Model.CustomForm
#{
ViewBag.Title = "Edit Custom Form";
Layout = "~/Views/Shared/_LayoutSmBanner.cshtml";
}
<div class="form-horizontal">
<div class="row">
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#* Fields for this form *#
}
<div id="CustomFields" class="col-md-6">
#Html.Action("_CustomFieldCreate", new { id = ViewBag.CustomFormId })
</div>
</div>
</div>
<script>
$(function () {
$("#createFieldForm").on("submit", function (e) {
e.preventDefault(); //This prevent the regular form submit
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (result) {
$("#CustomFields").html(result);
}
});
return false;
});
});
</script>
_CustomFieldCreate.cshtml -
#model PublicationSystem.Model.CustomField
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div id="result"></div>
<div class="form-horizontal">
<h4>CustomField</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model =>model.CustomFormId)
<div class="row">
#* Fields for the form *#
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div id="customFieldList">
#Html.Action("_CustomFieldList", new { id = ViewBag.CustomFormId })
</div>
_CustomFieldList.cshtml
#model System.Collections.Generic.IEnumerable<PublicationSystem.Model.CustomField>
<table class="table">
#* List table code *#
</table>
Edit: I rewrote the pages so that the list is part of the create partial view. What happens now is, if you enter data for _CustomFieldCreate and press submit, the first time, it refreshes just that view (including the nested list view). However the second time, it redirects to the view, probably because the first refresh didn't rebind the javascript to the submit button. Also, the Create view doesn't clear out the fields, but persists the originally entered data.
You will need a form in your partial view whose submit action binds to a javascript function that posts to your controller.
For example if your form id is MyForm:
$('#MyForm').on('submit', function (e) {
e.preventDefault(); //This prevent the regular form submit
$.ajax({
url: $(this).action, // This will submit the post to whatever action your form goes to
type: "POST", // This tells it that it is a post
data: $(this).serialize(), // This sends the data in the form to the controller
success: function (data) {
// do some javascript on success
},
error: function (xhr, ajaxOptions, thrownError) {
// do some javascript on error
}
});
});
This javascript overrides the default form submit and does an ajax post to your controller and then returns with success or error where you can do anything you want.
Here is some jquery ajax documentation:
http://api.jquery.com/jquery.ajax/
You should look into using AJAX. That should accomplish what I think you are describing. You'll want to create a javascript function that handles the submit event on the form, then post the form data to some create action in your MVC app using AJAX. If you are using jQuery, the library makes it pretty simple.
http://api.jquery.com/jquery.ajax/

MVC5 One Click Delete from Details Page

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
}
});
}

Resources