I might need an extra set of eyes but my delete btn is not working it does return a message but after clicking yes or ok it doesn't remove the data i wanted to delete basically nothing happens, I think I have an issue with the inventory.Id part. thank you, and i know this is not a good question for other users but i appreciate the help.
<tbody>
#foreach (var inventory in Model)
{
<tr>
<td>#Html.ActionLink(inventory.PartNumber, "Edit", "Inventory", new
{ id = inventory.Id }, null)</td>
<td>#inventory.PinNumber</td>
<td>#inventory.PartQuantity </td>
<td>#inventory.PartPrice </td>
<td>#inventory.PartDescrption</td>
<td> <button data-inventory-id="#inventory.Id" class="btn-link js-delete">Delete</button> </td>
</tr>
}
</tbody>
</table>
#section scripts
{
<script>
$(document).ready(function ()
{
$("#inventories").DataTable();
$("#inventories .js-delete").on("click", function () {
var button = $(this);
if (confirm("Are you sure you want to delete this Part Number?")) {
$.ajax({
url: "/inventory/" + button.attr("data-inventory-id"),
method: "DELETE",
success: function () {
button.parents("tr").remove();
}
});
}
});
});
</script>
}
this is my Controller for the Delete Action:
[HttpDelete]
public void DeleteInventory(int id)
{
var inventoryInDb = _context.Inventory.SingleOrDefault(c => c.Id == id);
_context.Inventory.Remove(inventoryInDb);
_context.SaveChanges();
}
I don't have an API in the Tutorial i am following He has an API but I didn't create one. I am trying to get around that.
Thank you.
How about using POST instead of DELETE as your ajax method? Or simply using the $.post method.
https://www.w3schools.com/jquery/ajax_post.asp
Most likely you did not create a DELETE method in your back-end API. To find out for sure, open Chrome's developer tools (making sure you're on the console tab) and then click your button. You will see an error message that says "Method DELETE is not found" or something similar.
If it says "method is not allowed" then that has to do with permissions (the user clicking the button does not have permission to access that API).
In controller:
public ActionResult Delete(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Inventory inventory = _context.Inventory.Find(id);
if (inventory == null)
{
return HttpNotFound();
}
return View(inventory);
}
in index add delete btn, this is an ajax call to the delete btn its used with dataTables to render data faster..
{
"render": function (data, type, row, meta) {
return '<a class="btn btn-danger"
href ="#Url.Action("Delete", "Inventory")/' + row.Id + '">Delete</a>';
}
create a Delete view:
#model InventoryTracker.Models.Inventory //this has to be re-named to your naming
convention.
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-actions no-color">
<input type="submit" value="Delete" class="btn btn-primary" />
#Html.ActionLink("Cancel", "Index", null, new { #class = "btn btn-primary" })
</div>
}
}
Related
In my view, a foreach loop iterates through my Model list and displays all tasks the user has as a table. This is a snippet of how it looks
<form method="post" asp-action="EditComplete" asp-controller="ToDoTask">
<table>
#foreach (ToDoTask todo in Model)
{
<tr class="theTasks" data-row="row #todo.Id">
<td>
<input type="checkbox" class="roundCheckbox" data-row="row #todo.Id" name="IsCompleted" value="true" onchange="this.form.submit()" #(todo.IsCompleted ? "checked" : "")/>
</td>
<td>
<input type="hidden" name="Id" value="#todo.Id"/>
</td>
</tr>
}
</table>
</form>
The post method takes in a ToDoTask object as a parameter with the Bind attribute like so:
[HttpPost]
public async Task<IActionResult> EditComplete([Bind("Id,IsCompleted")]ToDoTask task)
{
int taskId = task.Id;
bool isCompleted = task.IsCompleted;
ToDoTask theTask = _repo.GetTodoById(taskId);
if(theTask == null)
{
return NotFound();
}
theTask.IsCompleted = isCompleted;
_repo.UpdateTask(theTask);
await _repo.SaveChanges();
return RedirectToAction("Index");
}
The issue is it only seems to be saving one task's information (the Id), because when I try to save multiple tasks, the id for all of them are the same and it just updates that one tasks with the reused Id. Does the app only remember one Id? Any help would be appreciated
So my advice:
You can use AJAX to send the request.
You can learn AJAX here for free if you are interested.
Add the id value in the value property in the input tag.
Remove any unnecessary code if you have any.
#foreach(ToDoTask todo in Model){
<tr class="theTasks" data-row="row #todo.Id">
<td>
<input type="checkbox" class="itemId roundCheckbox" data-row="row #todo.Id" name="IsCompleted" value="todo.Id" #(todo.IsCompleted ? "checked" : "")/>
</td>
<td>
<input type="hidden" name="Id" value="#todo.Id"/>
</td>
</tr>
}
after that, use JQUERY and AJAX to send the request to your controller.
<script>
$(document).ready(function () {
$(".itemId").on('change',function (){
var itemId = $(this).val();
var d = {id: itemId};
$.ajax({
type: 'POST',
url: '#Url.Action("EditComplete", "ToDoTask")',
//I believe these URL is correct for you, if not change it.
data: d,
success: function (data) {
if(data.success == "Success"){
//When it is success reload the page.
//This won't work unless you change the controller.
location.reload();
}
else{
alert("something went wrong");
//Worst error handling. Check best practices.
}
}
});
});
});
</script>
The code above will update the task status on every time the checkbox is changed.
For the location.Reload() function to work you should change the return value in your controller.
return a new JSON in the controller method you have specified.
return Json(new { success = "Success" }); and you are all set.
You can modify this code to your liking and coding style.
I have created a table which dynamically generates records using the "Infinite Loading" process, I have a button in my HTML Code which i need to use to allow users to add the item in their favourite - so this is my HTML code
#model List<PagesAuth.Models.Links.MLink>
#foreach (var I in Model)
{
<tr>
<td class="v-align-top" id="itemcard">
<h4 class="card-title">
#I._title
<small><cite>#I._tp_created.ToString("dd MMM yyyy")</cite> /small>
</h4>
<div class="pull-right" id="options">
<ul class="list-inline text-right" >
#if (I._tp_favourite == 0)
{
<li><button class="btn-link glyphicon glyphicon-heart-empty" onclick="location.href='#Url.RequestContext .Action("_Fav", "Home", "#I._id")'"></button></li>
}
else
{
<li><button class="btn-link glyphicon glyphicon-heart-empty" onclick="location.href='#Url.RequestContext .Action("_UnFav", "Home", "#I._id")'"></button></li>
}
</ul>
</div>
</div>
</td>
</tr>
}
I am trying to use "Favourite" button to allow user to add that website to their favourite list (I am ok with the DB updates etc)
<ul class="list-inline text-right" >
#if (I._tp_favourite == 0)
{
<li><button class="btn-link glyphicon glyphicon-heart-empty" onclick="location.href='#Url.RequestContext .Action("_Fav", "Home", "#I._id")'"></button></li>
}
else
{
<li><button class="btn-link glyphicon glyphicon-heart-empty" onclick="location.href='#Url.RequestContext .Action("_UnFav", "Home", "#I._id")'"></button></li>
}
</ul>
What I want to know is how to achieve this on user front-end - Like I thought I should just create a PartialView and make it child action only in my controller, send it ID and do DB Processing
[ChildActionOnly]
public ActionResult _Fav(int ID)
{//Do DB Processing
return PartialView(ID);
}
First of all the following does not work
onclick="location.href='#Url.RequestContext .Action("_UnFav", "Home", "#I._id")'"
Second, if I end up making this work, it will still refresh the page and I don't want that.
Is there a better way to achieve this
Cheers
I don't know why you want to use partial views but you can do it this way.
Use ajax to send request to controller action.
Handle action result using JavaScript.
View:
#model List<PagesAuth.Models.Links.MLink>
#foreach (var I in Model)
{
<tr>
<td class="v-align-top" id="itemcard">
<h4 class="card-title">
#I._title
<small><cite>#I._tp_created.ToString("dd MMM yyyy")</cite> /small>
</h4>
<div class="pull-right" id="options">
<ul class="list-inline text-right" >
#if (I._tp_favourite == 0)
{
<li><button class="btn-link glyphicon glyphicon-heart-empty" onclick="Fav(#I._id)"></button></li>
}
else
{
<li><button class="btn-link glyphicon glyphicon-heart-empty" onclick="UnFav(#I._id)"></button></li>
}
</ul>
</div>
</div>
</td>
</tr>
}
JS:
Here I am just alerting that the favorite action succeeded, else you have an array of string errors to work with. You could redirect or do some stuff, whichever you prefer.
<script type="text/javascript">
function Fav(id) {
var url = '#Url.Action("_Fav", "Home")';
$.ajax({
url: url,
type: 'POST',
data: {
id: id
},
success: function (data) {
if(data.length == 0) // No errors
alert("Fave success!");
},
error: function (jqXHR) { // Http Status is not 200
},
complete: function (jqXHR, status) { // Whether success or error it enters here
}
});
};
function UnFav(id) {
var url = '#Url.Action("_UnFav", "Home")';
$.ajax({
url: url,
type: 'POST',
data: {
id: id
},
success: function (data) {
if(data.length == 0) // No errors
alert("Unfave success!");
},
error: function (jqXHR) { // Http Status is not 200
},
complete: function (jqXHR, status) { // Whether success or error it enters here
}
});
};
</script>
Controller:
[HttpPost]
public ActionResult _Fav(int ID)
{
List<string> errors = new List<string>(); // You might want to return an error if something wrong happened
//Do DB Processing
return Json(errors, JsonRequestBehavior.AllowGet);
}
[HttpPost]
public ActionResult _UnFav(int ID)
{
List<string> errors = new List<string>(); // You might want to return an error if something wrong happened
//Do DB Processing
return Json(errors, JsonRequestBehavior.AllowGet);
}
I am trying to build a simple CMS which allows the author to upload files (specifically images but the file type is not really important for now).
The upload of the file is working fine. However I want to provide the ability to list and subsequently delete a file (maybe later multiple files but for now a single file at a time is fine).
I have looked around the net. I see plenty of examples using EF to store the location of the file in a DB because they have permissions and roles etc. While that is something I may need way off in the future, its not a layer of complexity I am willing to add right now.
All I want is to simply press a delete link (just as though you are deleting a record in a DB). To trigger an action which calls a delete confirmation view. Then on that view, a delete button to actually delete the file and return the user to the list. Below is my code so far:
This would be the view that lists the files:
#model IEnumerable<FileInfo>
#{
ViewBag.Title = "File List";
}
<h2>Index</h2>
<p>
#Html.ActionLink("Upload", "Upload")
</p>
<table class="table">
<tr>
<th>File Name</th>
<th>Actions</th>
</tr>
#foreach (FileInfo file in Model)
{
<tr>
<td>#file.Name</td>
<td>#Html.ActionLink("Delete", "Delete", new { fileName = #file.Name })</td>
</tr>
}
</table>
I wont show the controller for this view as it's relatively simple and not where I am having the problem (I think). I only showed this so you could see the delete link and tell me if there is anything wrong.
Below is the delete confirmation view:
#model FileInfo
#{
ViewBag.Title = "Delete";
}
<h2>Delete</h2>
<h3>Are you sure you want to delete this?</h3>
<dl class="dl-horizontal">
<dt>
#Html.DisplayNameFor(model => model.FullName)
</dt>
<dd>
#Html.DisplayFor(model => model.FullName)
</dd>
</dl>
#using (Html.BeginForm("Delete", "FileManagement", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.AntiForgeryToken()
<div class="form-actions no-color">
#Html.ActionLink("Back to list of views", "Index", null, new { #class = "btn btn-success" })
|
#*#Html.ActionLink("Delete", "Delete", null, new { #class = "btn btn-danger" })*#
<input type="submit" value="Delete file" formaction="Delete" formmethod="delete" class="btn btn-danger" />
</div>
}
Below are the two Delete actions (GET and POST / DELETE)
// GET: FileManagement/Delete/filename
public ActionResult Delete()
{
return View();
}
// POST: FileManagement/Delete/filename
[HttpDelete]
[ValidateAntiForgeryToken]
public ActionResult Delete(string fileName)
{
var path = Path.Combine(Server.MapPath("~/UserFiles"), fileName);
if (System.IO.File.Exists(path))
System.IO.File.Delete(path);
else
return HttpNotFound();
return RedirectToAction("Index");
}
I don't have view models as I am not connecting to a database (yet). The files are just uploaded to the folder ~/UserFiles/someFileName.ext and the full path is got through appending this to the server.mappath in the normal way.
The problem I am having is getting the file name into the delete confirmation view, and also into the delete button which would pass it to the delete action to do the job.
Thanks for any help.
In your main view (I assume that Index.cshtml), you correctly generate a query string value for the fileName, but the GET method does not have a parameter to accept it. It would need to be
// GET: FileManagement/Delete/filename
public ActionResult Delete(string fileName)
and in that method you would need to initialize a new FileInfo class based on the fileName and pass that model to the view.
The next issue is that your form in the confirm page does not pass the file name back to the POST method, but that raises another issue in that you cannot have a GET and POST method with the same signatute, so you would need to change the name of one of the methods, for example
[HttpGet]
public ActionResult ConfirmDelete(string fileName)
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Delete(string fileName)
and in the confirm delete page, change the form to
#using (Html.BeginForm("Delete", "FileManagement", new { fileName = Model.Name })) // enctype not required
{
#Html.AntiForgeryToken()
<input type="submit" value="Delete file" class="btn btn-danger" />
}
However, you can greatly improve performance by generating the form in the Index view and displaying a confirm dialog (the GET method is no longer required)
#foreach (FileInfo file in Model)
{
....
#using(Html.BeginForm("Delete", "FileManagement", new { fileName = file.Name }))
{
#Html.AntiForgeryToken()
<input type="submit" value="delete" />
}
}
and adding a script to display the dialog
$('form').submit(function() {
return conform("Are your sure .... ");
});
which will display the browsers javascript confirm dialog. You can further enhance the UI by using a jquery plugin for the confirm dialog (or implement your own, as explained in this article)
You should also consider using ajax to submit the form (and in the success callback, remove the button and its associated table row). A typical implementation might look like
#foreach (FileInfo file in Model)
{
<tr>
<td>#file.Name</td>
<td>
<form class="deleteform">
#Html.AntiForgeryToken()
<input type="hidden" name="fileName" value="#file.Name" />
<input type="submit" value="delete" />
</form>
</td>
</tr>
}
var url = '#Url.Action("Delete", "FileManagement")';
$('.deleteform').submit(function() {
var formData = $(this).serialize();
var row = $(this).closest('tr');
$.post(url, formData, function(response) {
if (response) {
row.remove();
} else {
// Oops - display message?
}
}).fail(function (response) {
// Oops
});
return false; // cancel the default submit
});
and the controller method
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Delete(string fileName)
{
.... // delete the file
return Json(true); // indicate success
// or return Json(null); to indicate failure
}
I have this code. My problem is to delete the right row on my table. As you can se on the html every table row have a delete button. How can i in my ajax call in the data propertie get the right table row of based on which delete button i choose to press. right now i have tried this in the data propertie
("id=" + $(this).attr("Id"))
But it dosent work. Do anybody have a better ide?
//My Html
#foreach (var discount in Model.DiscountList)
{
<tr>
<td>#Html.TextBox("codeTextBox", discount.Code) </td>
<td><input type="checkbox" name="freeShippingCheckBox" id="freeShippingCheckBox" checked="#discount.FreeShipping" /> </td>
<td><input type="checkbox" id="activeCheckBox" name="activeCheckBox" checked="#discount.Active" /></td>
<td><input type="datetime" value="#discount.CreatedDate" readonly /></td>
<td>
<input type="button" value="Delete" class="fa fa-remove" />
</td>
<input id="#discount.Id.ToString()" type="hidden" value="#discount.Id" />
</tr>
}
//my jquery/ajax
#section scripts{
<script type="text/javascript">
$(document).ready(function () {
$(".fa-remove").on("click", function () {
if (confirm("Are you sure you want to delete this discount code?")) {
$.ajax({
url: '#Url.Action("DeleteDiscountCode","Discount")',
type: "POST",
data: "id=" + $(this).attr("Id"),
success: function (data) {
if (data) {
alert("Deleted");
}
location.reload();
}
});
}
});
});
</script>
}
//my controller
namespace Web.Controllers
{
public class DiscountController : BaseController
{
[HttpPost]
public void DeleteUser(string id)
{
}
}
}
You can use the jQuery closest() method to get the table row in which your delete button is
$(".fa-remove").on("click", function (e) {
e.preventDefault();
var currentRow = $(this).closest("tr");
//do your ajax call now
});
currentRow is a jQuery wrapped object. So you can call the relevant jQuery methods needed. For example, You probably want to remove the tr in your ajax call's success event
success: function (data) {
if (data) {
alert("Deleted");
currentRow.remove();
}
You might also make use the Url.Action helper method to generate the correct url to your delete action method instead of hardcoding that in javascript code.
<input type="button"
data-url="#Url.Action("DeleteDiscountCode","Discount",new { id= discount.Id})"
value="Delete" class="fa fa-remove" />
Now when user click,simply get the data-url value and use that for the ajax call.
So the full code will be
$(".fa-remove").on("click", function (e) {
e.preventDefault();
var currentRow = $(this).closest("tr");
if (confirm("Are you sure you want to delete this discount code?")) {
$.post($(this).data("url"),function(data){
if (data.status==="success") {
alert("Deleted");
currentRow.remove();
}
else
{
alert("expected truthy! but got something else");
}
});
}
});
Assuming your DeleteDiscountCode accepts an id
[HttpPost]
public ActionResult DeleteDiscountCode(int id)
{
return Json(new { status="success"});
}
I have been following the answers on here but can't seem to get it to work. I think it's firing my function and calling my controller but it isn't rendering my partial view. Any help would be awesome.
Controller
public ActionResult Detail(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
User_Accounts user_accounts = db.User_Accounts.Find(id);
if (user_accounts == null)
{
return HttpNotFound();
}
return PartialView("_Detail", user_accounts);
}
HTML
<h2>Index</h2>
<div class="container left">
<div class="panel-default panelbox" style="position:static">
#*<p>
#Html.ActionLink("Create New", "Create")*#
#using (Html.BeginForm("Index", "Users", FormMethod.Get))
{
<p>
Type: #Html.DropDownList("userType", "All")
</p>
<p>
Last Name: #Html.TextBox("SearchString")
</p>
}
</div>
<div class="panel panel-default left">
<div class="panel-heading">
<label style="text-align:center">
User
</label>
</div>
<div class="table-responsive">
<table id="UserTable" class="table-bordered table leftPanel table-condensed">
#foreach (var item in Model)
{
<tr>
<td>
<button data-url='#Html.Action("Detail", "Users", new { id = item.user_id_IN })' id="js-reload-details">#Html.DisplayFor(modelItem => item.DisplayName)</button>
#*#Html.ActionLink(item.DisplayName, "Detail", new { id = item.user_id_IN }, new { onclick = "renderPartial();" })*#
</td>
</tr>
}
</table>
</div>
</div>
</div>
<div>
<label>Details</label>
<div id="detailsDiv"></div>
</div>
Script
<script>
$('.js-reload-details').click(function (evt) {
var $detailDiv = $('#detailsDiv'),
url = $(this).data('url');
$.get(url, function (data) {
$detailsDiv.replaceWith(data);
});
});
</script>
Let me know if you need anything else.
You cant use data-url='#Html.Action("Detail", "Users", new { id = item.user_id_IN })' in your button to generate a url. #Html.Action() is a method which calls you controller. What would be happening is that for each item in your model you would be hitting the Detail method of UsersController (performance must of been awful if you had a lot of items :) ).
Since you appear to need only the one url (/Users/Detail) I suggest you just store the ID in data to minimize the html generated. As noted in the other answers you also need to use a class name for the button to prevent invalid html, and I also suggest using type="button" because the default (depending on the browser) may be "submit" (you don't have a form so does not matter in this case, but its good practice to get into). There is also no real need to use #Html.DisplayFor() unless your using a custom DisplayTemplate or have a [DisplayFormat] attribute on the property.
Change the html to
<button type="button" data-id="#item.user_id_IN" class="js-reload-details">#item.DisplayName</button>
and the script to
var url = '#Url.Action("Detail", "Users");
$('.js-reload-details').click(function() {
$.get(url, { id: $(this).data('id') }, function (data) {
$('#detailsDiv').html(data);
});
});
Note you do not want to use replaceWith() in your case. .replaceWith() would replace the actual div <div id="detailsDiv"></div> with the html your method returned, so the next time a user clicked on this or any other button, the method would be called, but <div id="detailsDiv"></div> no longer exists and nothing would happen.
$('#detailsDiv').html('Hello world');
renders
<div id="detailsDiv">Hello world</div>
but
$('#detailsDiv').replaceWith('Hello world');
renders
Hello world
The id of your button id="js-reload-details"
Mistake this code is repeated in a foreach loop. which will cause multiple id's of the same name on your HTML page.
Your click event is on : '.js-reload-details'. which is a class:
so make your code like this:
#foreach (var item in Model)
{
<tr>
<td>
<button data-url='#Html.Action("Detail", "Users", new { id = item.user_id_IN })' class="js-reload-details">
#Html.DisplayFor(modelItem => item.DisplayName)
</button>
</td>
</tr>
}
One error I noticed in your jQuery is that you have $detailsDiv.replaceWith(data);
It should be $detailDiv according to your code: var detailDiv = $('#detailsDiv'); instead of $detailsDiv
<script>
$(document).ready(function(){
$('.js-reload-details').click(function (evt) {
evt.stopPropagation();
var detailDiv = $('#detailsDiv');
// TRY using the attr function:
var url = $(this).attr("data-url");
$.get(url, function (data) {
detailDiv.html(data);
});
});
});
</script>
UPDATE:
<script>
$(document).ready(function(){
$('.js-reload-details').click(function (evt) {
evt.stopPropagation();
var detailDiv = $('#detailsDiv');
// TRY using the attr function:
var url = $(this).attr("data-url");
$.get(url).success(function(result) {
detailDiv.html(result);
});
});
</script>
It's a good practice we use unique id's for our HTML elements. Since the following statement is going to be executed mulitple times
<button data-url='#Html.Action("Detail", "Users", new { id = item.user_id_IN })' id="js-reload-details">#Html.DisplayFor(modelItem => item.DisplayName)</button>
You will have multiple buttons with the same id. Instead of doing so, you could use a class.
<button data-url='#Html.Action("Detail", "Users", new { id = item.user_id_IN })' #class="js-reload-details">#Html.DisplayFor(modelItem => item.DisplayName)</button>
Then you have to correct your script:
// Now we bound the click event in all the elements that contain
// the .js-reload-details class
$('.js-reload-details').click(function (evt) {
var $detailDiv = $('#detailsDiv');
// Here was your the error
var url = $(this).attr("data-url");
$.get(url, function (data) {
$detailsDiv.replaceWith(data);
});
});