MVC4 api routing can find one action but not another - ajax

I have the following MVC4 api controller:
public class VisitorController : ApiController
{
private ApplicationDbContext db = new ApplicationDbContext();
public Visitor Get(int id)
{
return db.Visitors.Find(id);
}
public void SaveNotes(int id, string notes)
{
var visitor = db.Visitors.Find(id);
visitor.Notes = notes;
db.SaveChanges();
}
}
the Get works but the SaveNotes doesn't. I can't figure out why, but at run time, SaveNotes can't be found. (For what it's worth, Application Insights can see the attempt to POST to it, and that it gets a 404 error. See: )
Here's how I'm trying to call it: I have a modal dialog, like so:
<div class="modal fade" id="notesModal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">Notes</h4>
</div>
<div class="modal-body">
<form id="notesForm">
<input type="hidden" name="id" id="notesID"/>
<textarea id="notesTextarea" name="notes"></textarea>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" onclick="saveNotes();">Save</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
</div>
</div>
</div>
</div>
And I have two javascript functions;
function showNotes(id) {
$("#searchingModal").modal("show");
$.ajax("#Url.Action("Get", "API/Visitor")/" + id)
.done(function (data) {
$("#searchingModal").modal("hide");
$("#notesID").val(id);
$("#notesTextarea").val(data.Notes);
$("#notesModal").modal("show");
})
.fail(function (xhr, status, errorThrown) {
$("#searchingModal").modal("hide");
genericError(status, errorThrown);
});
}
function saveNotes() {
//alert("#Url.Action("SaveNotes", "API/Visitor")/");
$.ajax({ url: "#Url.Action("SaveNotes", "API/Visitor")/", data: $("#notesForm").serialize(), method:"POST" })
.done(function () {
$("#notesModal").modal("hide");
})
.fail(function (xhr, status, errorThrown) {
$("#notesModal").modal("hide");
genericError(status, errorThrown);
});
}
The function showNotes works great. I run an ajax request, it calls Get on the visitor controller, a visitor is returned, I populate that textarea with the Notes field, and show the modal. The problem is saveNotes. Every time I click the save button on the modal I get the failure function instead of the done function. The errorThrown is "Not Found". So how is it that only one action can't be found? I've tried putting the [HttpPost] attribute on it. I've tried making the id a nullable int (even though it's already optional in the route config, see:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
). I've tried making savenotes return a string instead of void. I've tried using $.post instead of $.ajax.

When you're getting post data, it must be an object. So make an object like
public class SaveNotesInput
{
public int id { get; set; }
public string notes { get; set;}
}
change your function to accept that class, like so:
public void SaveNotes(SaveNotesInput input)
and it should work.
(couldn't tell you why)
(Credit to Ju66ernaut's comment, I'm just making this an answer for easier searchability)

Related

I have a probllem when the button delete has clicked but the data cannot delete and the url not show the id

I have tried to solve this problem several hours but I never solve this problem
I have a problem with my code, when I click the delete button from json, I can't get the ID just link from the console like this:
example :
That happened : request
I want Like this : request/?id=1
I paste some code to check :
Controller request.php:
public function delete()
{
// $this->m_request->delete($this->input->post('id_form'));
$id = $this->input->post('id_form');
$data = $this->m_request->DeleteRequest($id);
echo json_encode($data);
}
Model m_request.php:
public function DeleteRequest($id)
{
$hasil = $this->db->query("DELETE FROM request WHERE id_form='$id'");
return $hasil;
}
And Then View (I just put the modal script and ajax json script) :
Modal View :
<div class="modal fade" id="ModalHapus" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="myModalLabel">Hapus Request</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">X</span></button>
</div>
<form class="form-horizontal">
<div class="modal-body">
<input type="hidden" name="kode" id="textkode" value="">
<div class="alert alert-warning">
<p>Apakah Anda yakin mau menghapus request ini?</p>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Tutup</button>
<button class="btn_hapus btn btn-danger" id="btn_hapus">Hapus</button>
</div>
</form>
</div>
</div>
Ajax/JSON Script :
//GET HAPUS
$(document).on('click', '.hapus', function() {
var id = $(this).attr('data');
$('#ModalHapus').modal('show');
$('[name="kode"]').val(id);
})
// Hapus Request
$('#btn_hapus').on('click',function(e){
e.preventDefault();
var id = $('textkode').val();
$.ajax({
type: "POST",
url: "<?= site_url('request/delete')?>",
dataType: "JSON",
data: {id:id},
success: function(data){
// $('#ModalHapus').modal('hide');
console.log(data)
load_data();
}
});
return false;
})
There are a lot of reasons why the ajax request is possibly not working. The first thing which came in my mind is, that you have not specified an ID and method of the input form. Make sure you have both in your HTML form tag. For example:
<form id=“id_form” method=“post” class=“...”>
...
<input type="text" name="kode" id="textkode">
</form>
In you JS Code do the following
$.ajax({
type: "POST",
url: "<?= site_url('request/delete')?>",
dataType: "JSON",
data: $(“#id_form”).serialize(),
success: function(data){
console.log(data)
load_data();
}
error: function(xhr, desc, err) {
console.log(xhr);
console.log("Details: " + desc + "\nERROR: "+ err);
}
});
Also change the your delete function to this:
public function deleteTableRow()
{
$id = $_POST['textkode']; // Because I'm not sure what this->input->post() makes
$result = $this->m_request->DeleteRequest($id);
echo json_encode(array('id' => $id, 'result' => $result)); // The contents of array should then be displayed in the console of your webbrowser
}
Note that I changed the function name. It could be very misleading for other programmers, because delete is used in many programming languages as destructor for dynamic objects!
Additionally I would recommend to create an ajax.php file to parse different kind of ajax request. This file would also work as a controller, but just for ajax calls. In case you have several forms, the code is more readable.

Passing Model Data to a Bootstrap Modal on click

I'm extremely new to MVC and I don't know how to make this work-
I have a model that stores different news items based on categories:
NewsModel.cs:
public class NewsModel
{
public int ID { get; set; }
public string category { get; set; }
public String headline { get; set; }
public string source { get; set; }
public DateTime date { get; set; }
public string body { get; set; }
public string summary { get; set; }
}
I have a View that displays each of the news items as a list Item:
Sports.cshtml:
#model IEnumerable<Test.Models.NewsModel>
#foreach (var item in Model)
{
<div class="news_target-left floatleft">
<div class="image-container">
<img src="~/Content/Images/demo_img.png" alt="website template image">
<div class="top-left-text">
#item.category
</div>
</div>
<h3>#item.headline</h3>
<p> #item.summary </p>
<p class="single_cat_left_content_meta"><span>#item.source</span> | #item.date</p>
<span class="readmore">#Html.ActionLink("Read More", "NewsModal", "Home", #item)</span>
</div>
}
When the user clicks on Read More, I want to load a bootstrap modal that gets the current model object and displays the entire news data in detail. Currently, the readmore span uses an Html actionlink that does not seem to be working. I want to load the modal using Ajax but cannot figure out how to do so.
This is the bootstrap Modal that I have:
NewsModal.cshtml:
#model Test.Models.NewsModel
<div id="newsModal" class="modal fade" role="dialog">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h4 class="modal-title">Leadsquared Express</h4>
</div>
<div class="modal-body">
<h2>#Model.headline</h2>
<i><small>#Model.source</small></i>
<p>#Model.body</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
The Model is in Models/NewsModel
The Sports View is in Views/Categories/
The Sports controller Action is in Controllers/Categories:Sports
The NewsModal is currently in Views/Categories/. I have tried putting this view in Shared, as well as its own folder, but I'm obviously doing something wrong.
Any help?
Edit:
I used this link to make the following changes but clicking on "Read more" does not open the modal popup.
http://www.c-sharpcorner.com/UploadFile/092589/implementing-modal-pop-up-in-mvc-application/
changed <span class="readmore">#Html.ActionLink("Read More", "NewsModal", "Home", #item)</span> in Sports.cshtml to
<a id="openmodal "href="javascript:void(0)" class="readmore" data-model="#Json.Encode(#item)">Read More</a>
and added this script:
$(document).ready(function () {
var url = "/Home/NewsModal";
var model = $("#openmodal").attr("data-model");
alert("script running- sports.html");
$.ajax({
type: 'GET',
url: '/Home/NewsModal',
data: model,
contentType: 'application/json; charset=utf-8',
success: function (data, status, settings) {
$("#openmodal").html(data);
},
error: function (ajaxrequest, ajaxOptions, thrownError) {
$("#openmodal").html('Failed to load more content');
}
});
});
Additionally, this is the Action Method I have for the Modal Popup, in HomeController:
public ActionResult NewsModal(NewsModel tempData)
{
NewsModel currentItem = new NewsModel();
currentItem = tempData;
return PartialView("NewsModal", currentItem);
}
You can do by make a click event by using Anchor tag instead ActionLink,
and in that you can do like this.
Open a Modal by jQuery
function openModal()
{
$('#newsModal').modal('show');
}
// $('#newsModal').modal('toggle');
// $('#newsModal').modal('show');
// $('#newsModal').modal('hide');
OR can do directly with data-target,
<button type="button" data-toggle="modal" data-target="#newsModal">Launch modal</button>

Deleting using Entity Framework and calling controller method in ajax

I have been trying to create a delete button in mvc using json to call the delete method in my controller but so far its not working.
I did trap the error alert in the script and the success section is not registering a thing, I need help well here is my view, that picks data from SQL Server using Entity Framework 5.1.
I am working with a table called Departments and it has two columns, DepartmentId and DepartmentName.
<div class="container" style="width:40%; margin-top:2%;">
<hr />
<table class="table-responsive">
<tr>
<th>Deprtment Name</th>
<th></th>
</tr>
<tbody>
#if(ViewBag.RowDepartmentList != null)
{
foreach(var item in ViewBag.RowDepartmentList)
{
<tr id="row_#item.DepartmentId">
<td>#item.DepartmentId</td>
<td>#item.DepartmentName</td>
<td><a href="#" class="btn btn-danger" onclick="ConfirmDelete(#item.DepartmentId)">
<i class="glyphicon glyphicon-trash"></i></a></td>
</tr>
}
}
</tbody>
</table>
<input type="hidden" id="HiddenDepartmentId" />
</div>
I added a hidden attribute to capture the DepartmentId, the form too has a delete button that first calls a delete dialog modal.
My DELETE dialog modal code:
<div class="modal fade" id="mymodal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog" style="width:350px;">
<div class="modal-content">
<div class=" modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="close">
<span aria-hidden="true">x</span>
</button>
<h3 class="modal-title">Delete record</h3>
</div>
<div class="modal-body">
<h4>Are you sure you want to delete this?</h4>
</div>
<div class="modal-footer">
Cancel
Delete
</div>
</div>
</div>
And this is what my controller looks like:
public class EmployeeController : Controller
{
// GET: Employee
public ActionResult DepartmentIndex()
{
MVCTutorialEntities2 db = new MVCTutorialEntities2();
List<EmployeeViewModel> emlist = db.Departments.Where(x => x.IsDeleted == 0).Select(x => new EmployeeViewModel {DepartmentId=x.Departmentid, DepartmentName = x.DepartmentName }).ToList();
ViewBag.RowDepartmentList = emlist;
return View();
}
// the delete function
[HttpPost]
public JsonResult DelDepartment(int depId)
{
MVCTutorialEntities2 db = new MVCTutorialEntities2();
bool result = false;
Department dep = db.Departments.SingleOrDefault(x => x.Departmentid == depId);
if (dep != null)
{
db.Departments.Remove(dep); // I don't know why this is not deleting .... the table is not cascaded
db.SaveChanges();
result = true;
}
return Json(result, JsonRequestBehavior.AllowGet);
}
}
// now my script is all here that calls the
<script>
var ConfirmDelete = function (DepartmentId) {
$("#HiddenDepartmentId").val(DepartmentId);
$("#mymodal").modal("show");
}
var DelDepartment = function ()
{
var depId = $("#HiddenDepartmentId").val();
$.ajax({
type: 'POST',
url: 'Employee/DelDepartment',
data: { DepartmentId: depId },
success: function (result) {$("#mymodal").modal("hide"); },
error: function (result) { alert(result); $("#mymodal").modal("hide"); } // only the error section resturns a message of [object] of [object]
});
}
</script>
Trying so hard to learn this language ... and if so, is there any way I can just use razor and call the delete function from the
#using (Html.BeginForm("", "",FormMethod.POST)) ?
function?
data: { DepartmentId: depId } needs to be
data: { depId: depId } because the left JS parameter name has to match up with the parameter name on the controller side.
Or you may need to do: data: JSON.stringify({ depId: depId })
If that doesn't work, you can do url: 'Employee/DelDepartment?depId=' + deptId and just get rid of the data property altogether.

Spring MVC and bootstrap modal form : how to create server-side validation for it?

I'm newbie at spring and front-end at all.
I have (not mine) an old front-end code to show modal form over some page :
modal form is:
<div id="myModalForm" class="modal inmodal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog ">
<div class="modal-content animated fadeIn">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span
aria-hidden="true">×</span><span class="sr-only"></span></button>
<h4 class="modal-title"><spring:message code="entity.modal.title"/></h4>
</div>
<div id="modalContent" class="modal-body">
<form id="createForm" name="createForm" class="" action="/entity/create"
method="post" enctype="multipart/form-data">
...
</form>
</div>
</div>
</div>
</div>
form is shown from java scripts. Button that invokes form is:
<script>
$(document).ready(function () {
$('#tbl').DataTable({
responsive: true,
dom: 'l<"toolbar">frtip',
initComplete: function () {
$("div.toolbar")
.html('<button id="new_entity_btn" type="button" class = "btn btn-white" onclick="openCreateWindow()" ><spring:message
code="entity.table.create.btn"/> </button>');
}
});
/*activate tooltips*/
$(function () {
$('[data-toggle="popover"]').popover();
});
});
</script>
and onclick for button is:
<script>
function openCreateWindow() {
$('#myModalForm').modal('show');
}
</script>
Now as you can see #myModalForm is not a Spring view form as it doesn't generate even get-request to be shown. It just appears over existing page in modal mode. Now the question is how to create server-side validation for it? I've tried next:
1) refactor it to spring mvc form i.e. via
<form:form ... />
tag with attribute
ModelAttribute='MyFormAttribute'. But i have no idea how to create back-end object for MyFormAttribute as appearance of form doesn't generate get-request. And idea was to have on post method of controller something like:
#RequestMapping(value = {"/entity/create"}, method = RequestMethod.POST)
String createNewEntity(
#ModelAttribute("MyFormAttribute") MyEntity entity,
BindingResult bindingResult,
Model model)
2) Trying to change post method of controller so that it might return error. But there is no way to create bindingResult parameter in case if form is not mvc one
3) Trying to validate via jQuery.validate i.e. smth. like :
<script>
/*form validation*/
$().ready(function () {
$("#createForm").validate({
rules: {
Field1: {
remote: function () {
var r = {
url: '/validateEntityField1',
type: "POST"
};
return r;
}
},
Field2: {
remote: function () {
var r = {
url: '/validateEntityField2',
type: "POST"
};
return r;
}
}
...
},
errorElement: "em",
highlight: function (element, errorClass, validClass) {
$(element).fadeOut(function () {
$(element).fadeIn();
});
$(element).addClass(errorClass).removeClass(validClass)
},
unhighlight: function (element, errorClass, validClass) {
$(element).removeClass(errorClass).addClass(validClass)
}
})
;
});
</script>
But it looks rather ugly as i should validate every field separately on its own end point and. Besides form has file input so i should send it twice or smth.
So, how could i achieve next issue:
leave modal form over existing page
validate it on server side. It would be nice to make validation against whole entity.
Answering on my commented question. Not the exact code you are looking but will give you an idea. Though it depends on individual choice, in controller you can use ValidationUtils as shown below. Here you can map your error messages and json response which later on you want on your front-end:
#RequestMapping(value = "/registration", method = RequestMethod.POST)
public #ResponseBody JsonResponse addUser(#ModelAttribute("user") User user, BindingResult result, Errors errors, Model model) {
JsonResponse response = new JsonResponse();
ValidationUtils.rejectIfEmptyOrWhitespace(result, "firstname", "Firstname is required. It can not be empty.");
if (user.getFirstname().length() < 4 || user.getFirstname().length() > 32) {
errors.rejectValue("firstname", "Firstname must be between 4 and 32 characters.");
}
ValidationUtils.rejectIfEmptyOrWhitespace(result, "lastname", "Lastname is required. It can not be empty.");
if (user.getLastname().length() < 4 || user.getLastname().length() > 32) {
errors.rejectValue("lastname", "Lastname must be between 4 and 32 characters.");
}
if (!result.hasErrors()) {
userList.add(user);
response.setStatus("SUCCESS");
response.setResult(userList);
} else {
response.setStatus("FAIL");
response.setResult(result.getAllErrors());
}
return response;
}
And I picked the sample for Bootstrap Modal from here:
https://www.w3schools.com/bootstrap4/bootstrap_modal.asp
Somewhere in your html:
<!-- Modal body -->
<div class="modal-body">
<div class="form-group">
<label class="col-form-label" for="username">Enter your First Name</label>
<i class="fas fa-users"></i>
<input type="text" id="firstname" class="form-control">
</div>
....
....
</div>
Put up (similar to this) in your ajax:
if (response.status == "SUCCESS") {
userInfo = "<ol>";
for (i = 0; i < response.result.length; i++) {
userInfo += "<br><li><b>Firstname</b> : "
+ response.result[i].firstname
+ response.result[i].lastname;
}
And
errorInfo = "";
for (i = 0; i < response.result.length; i++) {
errorInfo += "<br>" + (i + 1) + ". "
+ response.result[i].code;
}
$('#error').html(
"<b>Errors found during validation : </b>"
+ errorInfo);
I created a small piece of code for this. You can take a look here on GitHub.

AJAX Model Validation with Partial View

I have a partial view, which is a login that functions as a popup. All I want to do is have my model do the validation (server side) and return any errors via AJAX. The code below returns the partial view only with the errors. I want my action result to not return a a view, but only the errors. In old ASP.NET, this would be a Partial Post back. I am not sure how to accomplish this in MVC.
Here is the Model
public class LoginModel
{
[Required]
public String Email { get; set; }
[Required]
[DataType(DataType.Password)]
public String Password { get; set; }
}
Here is the Partial View
#model MySite.Models.LoginModel
#using (Ajax.BeginForm("Authenticate", "Account", null, new AjaxOptions { OnFailure = "error" }, new { id = "LoginForm" }))
{
<div class="modal-body" id="LoginPopupDialogMessage">
The page you have requested requires you to login. Please enter your credentials and choose your country:
<br />
<br />
<div class="row">
<div class="form-group col-lg-offset-2 col-lg-8">
<label>Email Address</label>
#Html.TextBoxFor(u => u.Email, new { #class = "form-control input-lg input-sm", id = "Email", name = "Email" })
#Html.ValidationMessageFor(u => u.Email)
</div>
</div>
<div class="row">
<div class="form-group col-lg-offset-2 col-lg-8 ">
<label>Password</label>
#Html.PasswordFor(u => u.Password, new { #class = "form-control input-lg input-sm", name = "Password" })
#Html.ValidationMessageFor(u => u.Password)
</div>
</div>
<div style="text-align: center; padding-top: 20px;" class="ImageGroup">
<button name="companyCode" value="LB_US" class="btn-link" type="submit">
<img src="../../WebContent/Images/icon-flag-usa.png" />
</button>
<button name="companyCode" value="LB_CA" class="btn-link" type="submit">
<img src="../../WebContent/Images/icon-flag-canada.png" />
</button>
<button name="companyCode" value="LB_EU" class="btn-link" type="submit">
<img src="../../WebContent/Images/icon-flag-europe.png" />
</button>
</div>
</div>
}
I call the parial view from _layout.cshtml.
<div class="modal fade" id="LoginPopupDialog" tabindex="-1" role="dialog" aria-labelledby="myModalLabel1" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header" style="background: #e7e3e7; color:#000;">
<button type="button" class="close" data-dismiss="modal" aria-label="Close" style="color:#000;">
<span aria-hidden="true">×</span>
</button>
<div class="modal-title" id="LoginPopupDialogHeader">Please Login</div>
</div>
#Html.Action("Login", "Account")
</div>
</div>
</div>
My Controller Action:
[HttpPost]
[Route("account/authenticate")]
public ActionResult Authenticate(String companyCode, LoginModel model)
{
if (!ModelState.IsValid)
{
// ??
}
return PartialView("Login", model);
}
Since your code is doing an ajax form submission for the login, you should try to return a JSON response from the server. If model validation fails, you may read the validation errors from the model state dictionary and store that in a collection of strings (error messages) and return that as part of the json response. If model validation passes, you can continue executing your code to verify the login credentials and if those looks good, send back a json response with the next url for the user (to which we can redirect the user).
[HttpPost]
public ActionResult Authenticate(String companyCode, LoginModel model)
{
if (!ModelState.IsValid)
{
var errors = ViewData.ModelState.Values
.SelectMany(x => x.Errors.Select(c => c.ErrorMessage));
return Json(new { Status = "Error", Errors = errors });
}
//to do :Verify login, if good, return the below respose
var url=new UrlHelper(Request.RequestContext);
var newUrl = url.Action("About");
return Json(new { Status="Success", Url = newUrl});
}
Now in your view, you may specify a OnSuccess handler as part of the AjaxOptions. This will be a javascript object to which the json response from the server will come. We basicallly need to check the Status property value and do the appropriate things.
new AjaxOptions { OnFailure = "error" , OnSuccess="loginDone"}
The below implementation of loginDone simply alerts the error messages. You can update it to show it as part of the DOM.
function loginDone(d) {
if (d.Status === "Success") {
window.location.href = d.Url;
} else {
$.each(d.Errors,function(a, b) {
alert(b);
});
}
}
You may also consider enabling the unobtrusive client side validation which does the client side validation before trying to make a call to server. This will also show the error messages in the validation error spans (same as the normal mvc model validation does)

Resources