Edit action has not been hitting while I push the submit button - asp.net-mvc

I have an edit button in each row of my Datatable. I have two actions for editing. One for Getting data in a Datatable and the other one for posting my information. The code behind my Edit button in the my Home Index is:
{
"data": "Id",
"render": function (data, type, full, meta) {
return `<div class="text-center"> <a class="btn btn-info"
href="/Home/EditGet/` + data + `" >Edit</a> </div> `;
}
and my home controller methods are:
/// Get Edit
[HttpGet]
[Route("{Id}")]
public IActionResult EditGet(int? id)
{
if (id == null || id == 0)
{
return NotFound();
}
var obj = _sv.OpenRecord(id);
if (obj == null)
{
return NotFound();
}
return View("EditGet", obj);
}
/// Post Edit
[HttpPost]
public IActionResult EditPost(SalesVeiwModel sales)
{
if (ModelState.IsValid)
{
var res= _sv.Update(sales.Comment);
if (res==null )
{
return Json(data: "Not found");
}
return RedirectToAction("EditGet");
}
return Json(data: "Is not valid");
}
And finally my EditGet view is like bellow:
<form id="contact-form" method="post" asp-controller="Home" asp-
action="EditPost" role="form" >
<input asp-for="Id" hidden />
<div class="form-group">
<label>Invoice Nomber</label>
<input id="form_IBNo" type="text" class="form-control" disabled asp-for="IBNo">
</div>
.
.
.
<div class="col-md-12">
<input type="submit" class="btn btn-success btn-send" value="Confirm" asp-
controller="Home" asp-action="EditGet">
</form>

You should have two buttons,one call EditGet,one call EditPost,here is a demo:
<form id="contact-form" method="post" asp-controller="Home" asp-
action="EditPost" role="form" >
<input asp-for="Id" hidden />
<div class="form-group">
<label>Invoice Nomber</label>
<input id="form_IBNo" type="text" class="form-control" disabled asp-for="IBNo">
</div>
.
.
.
<div class="col-md-12">
<input type="submit" class="btn btn-success btn-send" value="Confirm">
<a class="btn btn-success btn-send" value="Confirm" asp-controller="Home" asp-action="EditGet" asp-route-id="1">EditGet</a>
</div>
</form>

Related

ASP.NET MVC Show message before redirects if success

Here's my web api on account controller using identity that can change the password wit validation using data annotations
[HttpGet]
[AllowAnonymous]
public IActionResult ChangePassword(string email, string returnUrl)
{
return email == null || returnUrl == null ? View("Error") : View();
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> ChangePassword(ChangePasswordViewModel model)
{
if (model == null)
{
return BadRequest();
}
if (ModelState.IsValid)
{
var result = await _changePasswordCommand.ChangePassword(model);
if (result.Succeeded)
{
ViewBag.IsSuccess = true;
ModelState.Clear();
return Redirect(model.returnUrl);
}
foreach (var error in result.Errors)
{
ModelState.AddModelError(string.Empty, error.Description);
}
}
return View(model) ;
}
then in my div razor views
<form class="change-password-form" asp-controller="Account" asp-action="ChangePassword" method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input id="email-hidden" asp-for="Email" type="hidden" />
<input id="return-url" asp-for="ReturnUrl" type="hidden" />
<div class="form-group change-password">
#if (ViewBag.IsSuccess == true)
{
<div class="alert alert-success alert-dismissible fade show text-center">Successfully Changed Password!</div>
}
<label asp-for="CurrentPassword" class="control-label"></label>
<input class="form-control" type="password" asp-for="CurrentPassword">
<span asp-validation-for="CurrentPassword" class="text-danger"></span>
</div>
<div class="form-group change-password">
<label asp-for="NewPassword" class="control-label">New password</label>
<input id="password" class="form-control" type="password" name="newPassword" asp-for="NewPassword" onkeyup="isGood(this.value)">
<span asp-validation-for="NewPassword" class="text-danger"></span>
</div>
<div class="form-group change-password">
<label asp-for="ConfirmNewPassword" class="control-label"></label>
<input asp-for="ConfirmNewPassword" class="form-control" type="password" />
<span asp-validation-for="ConfirmNewPassword" class="text-danger"></span>
</div>
<div class="change-password-footer">
<button id="btn-cancel" type="button" class="btn btn-primary cancel">
CANCEL
</button>
<button id="btn-save" type="submit" class="btn btn-secondary save" disabled>
SAVE
</button>
</div>
</form>
I want to be able to show the successfully changed password message before it redirects problem is it always redirect to account/changepassword after submit
You can write if condition outside the form tag, like this
#if (ViewBag.IsSuccess == true)
{
<div class="alert alert-success alert-dismissible fade show text-center">Successfully
Changed Password!
</div>
}
<form class="change-password-form" asp-controller="Account" asp-action="ChangePassword" method="post">
#*your code*#
</form>
Or remove method="post" then try
look at this place in your account controller.
...
if (result.Succeeded)
{
ViewBag.IsSuccess = true;
ModelState.Clear();
return Redirect(model.returnUrl);
}
...
Instead of the re-direct, use "return View(model);". then you can redirect the page ajax, (testing if the ViewBag.IsSuccess == true and model.returnUrl != null) you in the account controller, you will instead have.
...
if (result.Succeeded)
{
ViewBag.IsSuccess = true;
ModelState.Clear();
//return Redirect(model.returnUrl);
return View(model);
}
...
With this, the success alert would show. Then have a javascript method onload, test if success is true and the the return is not null. something like this (in the view)
...
#{
bool isSuccess = ViewBag.IsSuccess;
string rUrl = model.returnUrl;
}
...
<script>
function redirect(isSuccess, returnUrl)
{
//use ajax to redirect;
}
redirect(#isSuccess, #rUrl);
</script>

Loading partialView through ajax not working

i am trying to show order Summary through partial View and ajax.Request is going to server but my action method of Showsummary never hits. i want to summary of order through partial view.
[HttpPost]
public PartialViewResult Showsummary(OrderViewModel model)
{
try
{
var p = model.Packages.SelectMany(x => x.Packages).Select(y => new OrderPackagesViewModel()
{
PkgName = y.PkgName,
pkg_Id = y.id,
Ser_Id = y.Ser_Id,
Quantity = y.Quantity,
price = (y.TotalPrice - (y.DiscountPercent / 100 * y.TotalPrice)) * y.Quantity
}).ToList();
model.OrderPackages = p;
return PartialView("OrderSummary", model);
}
catch
{
return PartialView("OrderSummary", model);
}
}
My Ajax
$("#summary").click(function () {
console.log("calling summary");
event.preventDefault();
$.ajax({
type: "POST",
url: "/Order/Showsummary",
data: $("form.signup-form").serialize(),
success: function (data) {
console.log(data)
$('#page_2').hide();
$('#page_3').show();
$('#page_3').html(data);
},
failure: function (response) {
console.log(response.responseText);
},
error: function (response) {
console.log(response.responseText);
}
});
})
//Html Code
<div id="page_1">
<input asp-for="cus_name" placeholder="First Name" >
<input asp-for="Email" placeholder="Email" >
<select asp-for="Country" class="ui search dropdown">
<option value="">Select Country</option>
<option value="AF">Afghanistan</option>
<option value="AX">Åland Islands</option>
</select>
<input asp-for="cus_phone" placeholder="Phone Number"/>
<select asp-for="FirstPreferences" class="custom-select mr-sm-2"
asp-items="#(newSelectList(Preferences))">
<option value="">Select</option>
</select>
<select asp-for="FirstPreferedTimeStart" class="menu">
<option value="">HH:MM</option>
<option value="00:00:00">00:00</option>
<option value="01:00:00">01:00</option>
</select>
<textarea asp-for="Message" class="form-control"> </textarea>
</div>
//Page2 details of packages available. It is list of GroupByServices which contain fields ser_id Ser_Name and List of ServicePackages.
<div id="page_2" style="display:none">
<div>
<h1 id="heading">Choose a Package!</h1>
</div>
<div class="buttons">
#foreach (var services in Model.Packages)
{
<a href="#service_#services.Ser_Id"><div class="logo-p">
<h2>#services.Ser_Name</h2></div></a>
}
</div>
<!-- packages -->
#for (int i = 0; i < Model.Packages.Count; i++)
{
<div class="packages" id="service_#Model.Packages[i].Ser_Id">
<h1 id="custom-website-design">
#Model.Packages[i].Ser_Name
</h1>
<div class="packs-content">
#for (int j = 0; j < Model.Packages[i].Packages.Count(); j++)
{
<div class="pack1">
<div class="pack-price">
<div>
<input asp-for="#Model.Packages[i].Packages[j].id" hidden />
<input asp-for="#Model.Packages[i].Packages[j].PkgName" hidden />
<input asp-for="#Model.Packages[i].Packages[j].Ser_Id" hidden />
<input asp-for="#Model.Packages[i].Packages[j].Ser_Name" hidden />
<h1>#Model.Packages[i].Packages[j].PkgName</h1>
<p>#Model.Packages[i].Packages[j].Ser_Name PACKAGE</p>
</div>
<p>$#Model.Packages[i].Packages[j].TotalPrice</p>
<input asp-for="#Model.Packages[i].Packages[j].TotalPrice" hidden />
</div>
<div class="pack-features">
<div class=""></div>
#foreach (var features in Model.Packages[i].Packages[j].Description)
{
<h2>#features</h2>
}
</div>
<div class="pack-order">
<div class="row-1">
#{
double discount = (Model.Packages[i].Packages[j].DiscountPercent / 100 * Model.Packages[i].Packages[j].TotalPrice);
double PriceAfterDiscount = Model.Packages[i].Packages[j].TotalPrice - discount;
}
<p>SPECIAL DISCOUNT</p>
<P>-$#discount</P>
</div>
<div class="row-2">
<p>FINAL PRICE FOR LIMITED TIME</p>
</div>
<div class="row-3">
<a asp-controller="Packages" asp-action="Detail" asp-route-id="#Model.Packages[i].Packages[j].id" target="_blank">view details</a>
<h1>$#PriceAfterDiscount</h1>
<input asp-for="#Model.Packages[i].Packages[j].DiscountPercent" hidden />
</div>
<div class="row-4">
<h4>Add To Buying List</h4>
<div class="input-group mb-3 order-btn-pack">
<div class="input-group-prepend">
<div class="input-group-text">
<input asp-for="#Model.Packages[i].Packages[j].is_selected" aria-label="Checkbox for following text input">
</div>
</div>
<input asp-for="#Model.Packages[i].Packages[j].Quantity" value="1" min="1" required placeholder="Quantity" class="form-control" aria-label="Text input with checkbox">
</div>
</div>
<div class="row-5">
<p>Discuss this offer with expert</p>
<div class="discuss">
<p>12345467889</p>
<p id="chat-btn_10">Live Chat</p>
</div>
</div>
</div>
</div>
}
<input asp-for="HoldPackage" hidden/>
</div>
</div>
}
<div>
<button type="button" class="btn btn-danger px-2 btn-lg" onclick="PageBack(this.parentElement.parentElement)">Back</button>
<button type="button" class="btn btn-danger px-2 btn-lg" id="summary">Summary!</button>
</div>
</div>
Edit!!!!
i have changed this line in ajax now it start hitting my controller action but still all form values are empty seems model binding is unable to recognize my fields
**const model= $("form").serialize()**
console.log(model); //data is there thats fine
and in ajax
data: { model },
my model have field called packages which is list of some fields and inside it there is one another list.
One thing is clear. $("form").serialize() is not working in my case its giving me 415 Unsupported Media Type client error response. I think problem is due to nested List
I have checked your code on my side with a simple OrderViewModel object, it works well. I suggest you could try to add '[FromBody]' in the action method, Like this:
....
Edit
According your description, it seems that you are using the Nested List and meet 415 error when using the [FromBody]. I have modified the sample code, in the action method, it's not using the [FromBody] attribute and in the JQuery Ajax method, there is no need to use the JSON.stringify method to change the JavaScript object. More detail information, please check the following code:
Model Class (Suppose the OrderViewModel contain a List):
public class OrderViewModel
{
public int OrderId { get; set; }
public string OrderName { get; set; }
public List<Package> Packages { get; set; }
}
public class Package
{
public int Pid { get; set; }
public string PackageTitle { get; set; }
}
Code in the Controller:
/// <summary>
/// //display the order
/// </summary>
/// <returns></returns>
public IActionResult ShowOrder()
{
OrderViewModel ovm = new OrderViewModel()
{
OrderId = 1001,
OrderName = "order 1",
Packages = new List<Package>()
{
new Package(){ Pid=101, PackageTitle="first Package"},
new Package(){ Pid=102, PackageTitle="second package"}
}
};
return View(ovm);
}
/// <summary>
/// JQuery ajax post method
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
[HttpPost]
public PartialViewResult Showsummary(OrderViewModel model)
{
try
{
//...
return PartialView("OrderSummary", model);
}
catch
{
return PartialView("OrderSummary", model);
}
}
ShowOrder.cshtml:
#model MVCSample.Models.OrderViewModel
#{
ViewData["Title"] = "ShowOrder";
}
<div class="row">
<div class="col-md-4">
<form asp-action="Showsummary" asp-controller="Home" method="post" class="signup-form">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="OrderId" class="control-label"></label>
<input asp-for="OrderId" class="form-control" />
<span asp-validation-for="OrderId" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="OrderName" class="control-label"></label>
<input asp-for="OrderName" class="form-control" />
<span asp-validation-for="OrderName" class="text-danger"></span>
</div>
<div id="packages">
#for (int i = 0; i < Model.Packages.Count; i++)
{
<div class="form-group">
<label asp-for="#Model.Packages[i].Pid" class="control-label"></label>
<input asp-for="#Model.Packages[i].Pid" class="form-control" />
<span asp-validation-for="#Model.Packages[i].Pid" class="text-danger"></span>
<br />
<label asp-for="#Model.Packages[i].PackageTitle" class="control-label"></label>
<input asp-for="#Model.Packages[i].PackageTitle" class="form-control" />
<span asp-validation-for="#Model.Packages[i].PackageTitle" class="text-danger"></span>
</div>
}
</div>
#*<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>*#
</form>
</div>
</div>
<div>
<input type="button" id="summary" value="Summary" />
<div id="page_3">
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
$(function () {
$("#summary").click(function () {
console.log("calling summary");
event.preventDefault();
//create a object to store the entered value.
var OrderViewModel = {};
//using jquery to get the entered value.
OrderViewModel.OrderId = $("input[name='OrderId']").val();
OrderViewModel.OrderName = $("input[name='OrderName']").val();
var packages = [];
//var count = $("#packages>.form-group").length; //you could use it to check the package count
$("#packages>.form-group").each(function (index, item) {
var package = {}
package.Pid = $(item).find("input[name='Packages[" + index + "].Pid']").val();
package.PackageTitle = $(item).find("input[name='Packages[" + index + "].PackageTitle']").val();
packages.push(package);
});
OrderViewModel.Packages = packages;
$.ajax({
type: "POST",
url: "/Home/Showsummary", //remember change the controller to your owns.
data: OrderViewModel,
success: function (data) {
console.log(data)
$('#page_3').html(data);
},
failure: function (response) {
console.log(response.responseText);
},
error: function (response) {
console.log(response.responseText);
}
});
});
});
</script>
Then the output as below:
Edit:
Besides, I also found that by using the above sample, if I just change the data: OrderViewModel to data: $("form.signup-form").serialize() (in the Ajax method), I could also get the OrderViewModel and the Packages in the action method.

Delete Action does not activate/trigger ASP.NET Core MVC

HttpPost on delete action does not trigger, however HttpGet seems working fine as i get the content displayed. However I have little confusion in the following route address generated when I click on HttpGet on delete action:-
https://localhost:44394/9
shouldn't it generates link like this: https://localhost:44394/Post/DeletePost/9
Controller:-
[HttpPost, ActionName("DeletePost")]
public async Task<IActionResult> ConfirmDelete(int id)
{
await _repository.DeletePostAsync(id);
return RedirectToAction(nameof(GetAllPosts));
}
[HttpGet("{id}")]
public async Task<IActionResult> DeletePost(int id)
{
var post = await _repository.GetPostById(id);
if(post == null)
{
return NotFound();
}
return View(post);
}
Razor View for HttpGet:-
<div class="btn btn-outline-danger delete">
<a href="#Url.Action("DeletePost", "Post", new { id = p.Id })">Delete
</a>
</div>
Razor Page HttpPost:-
<div class="container">
<div class="row">
<div class="col-9">
<p>
#Model.Topic
</p>
<p class="timeStampValue" data-value="#Model.Published">
#Model.Published
</p>
<p>
#Model.Author
</p>
<section>
<markdown markdown="#Model.Content" />
</section>
</div>
</div>
<form asp-action="DeletePostAsync">
<input type="hidden" asp-for="Id" />
<button type="submit" class="btn btn-outline-danger">Delete</button>
</form>
Cancel
</div>
Routing:-
app.UseMvc(routes =>
{ routes.MapRoute(
name: "KtsPost",
template: "{controller}/{action}/{id?}",
defaults: new { controller = "Post", action = "Index" },
constraints: new { id = "[0-9]+" });
});
Your action name is wrong in the form. Your code should be instead:
<form asp-action="DeletePost">
<input type="hidden" asp-for="Id" />
<button type="submit" class="btn btn-outline-danger">Delete</button>
</form>
The default method of an HTML form is GET not POST. You need to tell your form to POST. Also, the action name should be ConfirmDelete:
<form asp-action="ConfirmDelete" method="post">
<input type="hidden" asp-for="Id" />
<button type="submit" class="btn btn-outline-danger">Delete</button>
</form>

How can I return text to my current mvc view?

I have a login page within an ASP.NET Core App with a pop up to send a password reset email to the users email (Just using Identity):
<div class="login-body">
<div class="container">
<form asp-controller="Account" asp-action="Login" asp-route-returnurl="#ViewData["ReturnUrl"]" method="post" class="form-signin">
<h2 class="form-signin-heading">#Localizer["Sign In"]</h2>
<div class="login-wrap">
<div class="user-login-info">
<input asp-for="Email" type="email" class="form-control" placeholder="#Localizer["Email"]" autofocus/>
<span asp-validation-for="Email" class="text-danger"></span>
<input asp-for="Password" type="password" class="form-control" placeholder="#Localizer["Password"]"/>
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<label class="checkbox">
<input asp-for="RememberMe"/> Remember me
<span class="pull-right">
<a data-toggle="modal" href="#forgotPassModal">Reset Password</a>
</span>
</label>
<button class="btn btn-lg btn-login btn-block" type="submit">#Localizer["Sign In"]</button>
<div class="registration">
#Localizer["No Account"]
<a asp-area="" asp-controller="Account" asp-action="Register">#Localizer["Create Account"]</a>
</div>
</div>
<!-- Modal -->
<div aria-hidden="true" aria-labelledby="myModalLabel" role="dialog" tabindex="-1" id="forgotPassModal" class="modal fade">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4 class="modal-title">Reset Password</h4>
</div>
<div class="modal-body">
<p>Enter your e-mail address below to reset your password.</p>
<input type="text" name="email" placeholder="Email" autocomplete="off" class="form-control placeholder-no-fix">
</div>
<div class="modal-footer">
<button data-dismiss="modal" class="btn btn-default" type="button">Cancel</button>
<button class="btn btn-success" asp-controller="Account" asp-action="ForgotPassword" method="post">Submit</button>
#*<input type="button" class="btn btn-success" asp-controller="Account" asp-action="ForgotPassword" formmethod="post">Submit<input>*#
</div>
</div>
</div>
</div>
<!-- modal -->
</form>
</div>
When I input the email address it does the right thing, sends an email with a code, but it redirects me to a separate view stating to go check your emails.
Is it possible to just return a message either to the popup window or login page saying the same thing?
This is the ForgotPassword Action:
public async Task<IActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
if (ModelState.IsValid)
{
var user = await UserManager.FindByEmailAsync(model.Email);
if (user == null || !(await UserManager.IsEmailConfirmedAsync(user)))
{
return View("ForgotPasswordConfirmation");
}
var code = await UserManager.GeneratePasswordResetTokenAsync(user);
var callbackUrl = Url.Action(nameof(ResetPassword), "Account", new { userId = user.Id, code = code }, protocol: HttpContext.Request.Scheme);
await _emailSender.SendEmailAsync(model.Email, "Reset Password",
$"Please reset your password by clicking here: <a href='{callbackUrl}'>link</a>");
return View("ForgotPasswordConfirmation");
}
return View(model);
}
What you can use is TempData. TempData can hold data for one more additional request and only for one additional request. So you can store this information in the TempData. It is meant for things like your use case. It could look something like this.
public IActionResult Login()
{
var loginModel = new LoginModel();
loginModel.ForgotPassWordModel = (ForgotPassWordModel) TempData["ForgotPassword"];
return View(loginModel);
}
public IActionResult ForgotPassword()
{
if (valid)
{
TempData["ForgotPassword"] = new ForgotPassWordModel() {callbackUrl = "Go to this"};
return RedirectToAction("Login");
}
return View();
}
Here is my example LoginModel and my ForgottonPasswordModel. Ofcourse, yours will be more complicated.
public class ForgotPassWordModel
{
public string callbackUrl;
}
public class LoginModel
{
public ForgotPassWordModel ForgotPassWordModel { get; set; }
}
When someone makes a request to ForgotPassword, if it is valid, store the result in TempData and Redirect back to Login. Now you can read the TempData["ForgotPassword"] that you just set in the previous request. In my example, I put the data in my LoginModel. If there is no tempData, it would be null. Now in your view you can check for this ForgotPassword property.
#if (Model.ForgotPassWordModel != null)
{
// Show message/modal
}
The great thing about TempData is that when you set data, it is only held for one more additional request so it works for situations like these.

Bad Request MVC HttpPost

I have the following cshtml file.
#model Models.AuthorizeUser
#{
ViewBag.Title = "Authorize";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<div class="container">
<div class="card card-container">
<img class="profile-img-card" src="~/Content/images/rblogo_reverse-pms348_000.gif" />
<p> </p>
<form method="POST">
<p>Hello, #Model.Name</p>
<p>A third party application want to do the following on your behalf:</p>
<ul>
#foreach (var scope in Model.Scopes)
{
<li>#scope.ScopeDescription</li>
}
</ul>
<div class ="row">
<div class="col-md-4">
<button class="btn btn-block btn-primary btn-signin" name="submit" type="submit" value="authorize">Grant</button>
</div>
<div class="col-md-8">
<button class="btn btn-block btn-primary btn-signin btn-small-text" name="submit" type="submit" value="logout">Sign in as different user</button>
</div>
</div>
</form>
</div>
</div>
I have my controller file as follows:
public class OAuthController : Controller
{
[HttpGet]
public ActionResult Authorize()
{
logger.Trace("Authorize method entered");
AuthorizeUser authorizeUser;
......
return View(authorizeUser);
}
[HttpPost]
public ActionResult Authorize(AuthorizeUser authorizeUser, string submit)
{
logger.Trace("Authorize with object");
if (Response.StatusCode != 200)
{
logger.Trace("status code " + Response.StatusCode);
logger.Trace("status description " + Response.StatusDescription);
return View("AuthorizeError");
}
..............
}
When the form is displayed, the info is displayed correctly. After I click Grant button, I got Response.StatusCode == 400. Both authorizeUser and submit are null. I am expecting StatuCode == 200 with values of authrizeUser and submit.
Have you tried using Html.BeginForm()?
Instead of using <form method="POST"> you could use Html.BeginForm("Action", "Controller")

Resources