I have a problem with a form submit in Angular / .net Core.
I have a form, where the user can submit changes. The submit does a post on the .net core background. After that, the form is resetted.
So far so good, but after that it doesn't post anymore.
The problem seems to be with the form reset. If i exlude it, a second submit would work. Here is my (sample) code:
<form #frm="ngForm" id="frm" (ngSubmit)="onSubmit(frm.value, frm); frm.reset();">
<input [(ngModel)]="selectedItem" name="selectedItem" type="text" />
<button type="submit" class="mdc-button mdc-button--raised">
<span class="mdc-button__label"><i class="material-icons">done</i></span>
</button>
</form>
onSubmit(formData, form: NgForm) {
this.service.SetItem(formData, this.selectedItemTyp).subscribe(data => {
this.reload(id);
});
}
SetItem(param: object, table: string): Observable<any> {
var data = {
Item: param["selectedItem"]
};
return this.http.post('/api/item/SetItem', data).pipe(
catchError(
this.handleError('SetItem', [])
)
);
}
[HttpPost]
public IActionResult SetItem([FromBody]StatusBody parameter)
{
try
{
db.SetItem(parameter.Item);
db.SaveChanges();
return Ok("");
}
catch (Exception ex)
{
return Ok(ex.Message);
}
}
If i remove frm.reset(); in the component html, it works as expected.
The model in the server side StatusBody ,you need to send that model object correctly , otherwise it's return 400.
I found the problem, it was a parameter that was null after the first submit...
Thanks to all.
Related
I use ASP.NET Core 6 MVC and Entity Framework 6.0.11 and I have trouble when deleting data from SQL Server.
When I click delete button, it makes the deletePage show 0 values, when I back to the list the data I just deleted, it does not disappear (see screenshot).
My controller:
public IActionResult deletePackage (long ID)
{
ForDeletePackage = LQHVContext.Packages.Where(s => s.PackagesId == ID).FirstOrDefault();
if (ForDeletePackage == null)
{
return NotFound();
}
return View(ForDeletePackage);
}
[HttpPost]
public IActionResult Delete(long ID)
{
Package package = new Package() { PackagesId = ID };
LQHVContext.Packages.Attach(package);
LQHVContext.Packages.Remove(package);
if (LQHVContext.SaveChanges() == 1)
{
//redirect to package list
return RedirectToAction("packageList", "Packages");
}
return View("deletePackage", package);
}
My razor page:
<form asp-action="Delete">
<input type="submit" value="Delete" class="btn btn-danger" /> |
<a asp-action="listPackage">Back to List</a>
</form>
Screenshots:
My ConfirmDelete page
After I click delete btn
when I back to List, the data still there
I doubt HTML forms can do a DELETE HTTP operation against our API. The only two options are POST and GET
See this question as well Should PUT and DELETE be used in forms?
I'm trying to have one post route that takes care of multiple operations.
Here's the Controller:
[HttpPost("dish/{operation}")]
public IActionResult Dish(string operation, Dish dish)
{
if (operation == "RedEdit")
{
return RedirectToAction("EditDish", dish);
}
if (ModelState.IsValid)
{
if (operation == "Add")
{
_context.Add(dish);
_context.SaveChanges();
return RedirectToAction("Index");
}
else //Unused currently
{
Console.WriteLine("Oops");
_context.Add(dish);
_context.SaveChanges();
return RedirectToAction("Index");
}
}
else
{
return View("DishForm");
}
}
The POST route will take a string, which is the operation it'll do, and depending on the operation it'll run something different. Right now I don't have all the operations, and my else within the validation isn't what it's going to be. The problem I'm having currently is with the "RedEdit," which is just a method to redirect to the edit page. Here's my view and what I'd like to do:
#{
ViewData["Title"] = "Add a dish!";
ViewData["Header"] = "Add a new Dish!";
ViewData["Home"] = true;
ViewData["Add"] = false;
var parms = new Dictionary<string, string>
{
{"operation", ""}
};
}
#model RichCRUDelicious.Models.Dish
<div class="container d-flex flex-column text-center">
<h3><u>#Model.Name by #Model.Chef</u></h3>
<p>#Model.Description</p>
<p>Calories: #Model.Calories</p>
<p>Tastiness: #Model.Tastiness</p>
<footer>
#using (Html.BeginForm("Dish", "Home", "operation", FormMethod.Post)){
//Two Buttons with Edit and Delete
}
</footer>
</div>
I'd essentially like to have one form, which has two buttons, one for edit and delete. The button for edit will change my operation value in parms to "RedEdit," while delete will change it to "Delete" (which I don't have a route set up for currently but that's not the issue.) I've tried a couple different methods, and mostly the issue comes down to the parameters within the post method, I'm not sure how I can pass the model in AND the operation value. I don't mind if they're split up into two different forms, but I'd really like just one post method for this controller.
I've tried using a generic HTML Form with:
<form asp-action="Dish" asp-controller="Home" asp-all-route-data="parms" method="post">
But my issue wasn't resolved using this method either, I'm thinking a hidden input with two different forms will work, but if there's a better way I'd like to hear.
If you want to use a form with two buttons which go to the same action,you can try to add asp-route-operation to your buttons,here is a simple demo:
Dish:
public class Dish {
public int Id { get; set; }
public string Name { get; set; }
}
view:
<form method="post">
<input hidden name="Id" value="1" />
<input hidden name="Name" value="test" />
<button asp-controller="Home" asp-action="Dish" asp-route-operation="RedEdit">RedEdit</button>
<button asp-controller="Home" asp-action="Dish" asp-route-operation="Delete">Delete</button>
</form>
action:
[HttpPost("dish/{operation}")]
public IActionResult Dish(string operation, Dish dish)
{
...
}
}
Hidden inputs will help you pass the value of Dish to the action,asp-route-operation will help pass different operation values to the action.When clicking RedEdit button,the value of operation will be RedEdit.With Delete button,it will be Delete.
I am new to MVC and trying to pass the last created Id (once the save button has been clicked in the form).
Can anyone please tell me if it is possible to pass this value to the toastr display, and how this can be done, so once the save button is pressed it returns that Id number?
Additionally to my comment, here's a more complex answer.
Roughly it contains the following items:
Views: CreateItem, NewItemHandler
Controllers: ItemHandler
Javascript: site.js and jQuery
The CreateItem view is the dialog where the user enters their item values. In my case a simple form with two input fields and the mandatory submit button.
#{
ViewBag.Title = "CreateItem";
}
<h2>CreateItem</h2>
<form id="newItemForm">
Item name: <input id="itemname" type="text" name="fname"><br>
Item weight: <input id="itemweight" type="text" name="lname"><br>
<input type="submit" value="Submit">
</form>
The JavaScript should stop the redirection when clicking on submit, this is done by returning false within $("newItemForm").submit(...). Furthermore we no need to tell the server that it needs to create our item, so we have to create our own submit request, which I did with jQuery.post():
$('#newItemForm').submit(function () {
sendPostAndShowResult();
return false;
});
function sendPostAndShowResult() {
var name = $("#itemname").text();
var weight = $("#itemweight").text();
$.post("/Item/NewItemHandler",
{ "name": name, "weight": weight }
).done(function (data) {
alert("The ID of your new item is: " + $.trim(data)); //replace with toast
})
.fail(function () {
alert("Error while processing the request!");
});
}
Just a hint: I didn't use toast here, since I never used it, but I guess it shouldn't be too difficult to adapt.
The final piece of the puzzle is the NewItemHandler, which creates the item, figures out the ID and returns the value:
The View is quite easy. Since we don't need a Layout, it has been set to "".
#{
Layout = "";
}
#Html.Raw(Session["ItemID"])
As you see, we just need to get the "ItemID" into our Session object, this is done by the Controller.
[HttpPost]
public ActionResult NewItemHandler(string name, string weight)
{
int id = GenerateNewItem(name, weight);
Session["ItemID"] = id;
return View();
}
EDIT: I tried to adapt this approach to your solution:
You need to remove the return RedirectToAction() with return View(); in your Controller. This then returns (Save.cshtml) a response, with the ID in an ohterwise empty file (Layout = "").
Your Save.cshtml is empty I guess, so replace it with
#{
Layout = "";
}
#Html.Raw(Session["ItemID"])
In your controller the Save Method should look remotely like this.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Save(BidstonHwrc bidstonhwrc)
{
_context.BidstonHwrc.Add(bidstonhwrc);
try
{
_context.SaveChanges(); //either all changes are made or none at all
}
catch (Exception e)
{
Console.WriteLine(e);
}
int id = bidstonhwrc.Id;
Session["ItemID"] = id;
return View();
}
In your MCN Form you need to give your <form> tag an ID, via Razor:
#using (Html.BeginForm("Save", "BidstonHwrc",FormMethod.Post, new { id = "SaveBidstonHwrc" }))
The javascript code should look like this, simply adapt the IDs:
$('#SaveBidstonHwrc').submit(function () {
sendPostAndShowResult();
return false;
});
function sendPostAndShowResult() {
//foreach Model/ViewModel Property one line e.g.
var Id = $("Id").text();
var McnNumber = $("McnNumber").text();
$.post("/BidstonHwrc/Save",
{ "Id": Id, "McnNumber": McnNumber }
).done(function (data) {
alert("The ID of your new item is: " + $.trim(data)); //replace with toast
$(location).attr('href', '/Home/Index') //Redirect to Home
})
.fail(function () {
alert("Error while processing the request!");
});
}
I uploaded a project that should represent your solution a bit.
You can download it here (28MB): Project download
I'm trying to call an ActionResult method from a web page form but I can't seem get the two items to connect.
The desired result is for the page to refresh and filter the model to display the required results.
Method in serversController.cs
The internal code works as intended when placed in ActionResult Index
[HttpPost]
public ActionResult activeServers()
{
// load the servers into local variable
var servers = from s in db.Servers
select s;
// filter out the archived servers
servers = servers.Where(s => s.archive.Equals(0));
return View(servers.ToList());
}
Button making the call
#using (Html.BeginForm())
{
<button name="activeServers" type="submit" value="activeServers" class="btn btn-default">Active</button>
}
Thanks is advance
Try to specify the action method, controller name (without the controller suffix) and the http method (it defaults to GET) in the BeginForm:
#using (Html.BeginForm("activeServers", "Servers", FormMethod.POST))
{
<button name="activeServers" type="submit" value="activeServers" class="btn btn-default">Active</button>
}
I have to allow the user to move to the next or previous form, I just need to save the model on navigation. Is there another way to pass back the model to the controller besides using submit? Since I need to redirect to other possible pages.
You could put your model object in the TempData collection on submit, redirect, then read it back out again. For example:
[HttpPost]
public ActionResult FirstForm(FirstFormModel model) {
TempData["TempModelStorage"] = model;
return RedirectToAction("SecondForm");
}
public ActionResult SecondForm() {
var firstModel = TempData["TempModelStorage"] as FirstFormModel;
// check for null, use as appropriate, etc.
return View(...);
}
More details here: http://msdn.microsoft.com/en-us/library/dd394711(v=vs.100).aspx
You may save the data asynchronously using jQuery ajax on those button click events.
Assuming your View is something like this
#using(Html.BeginForm("Save","Items"))
{
<div>
Name : #Html.TextBoxFor(s=>s.Name)
<input type="button" class="navigBtns" value="Prev" />
<input type="button" class="navigBtns" value="Next" />
</div>
}
And your script is
$(function(){
$(document).on("click",".navigBtns",function() {
e.preventDefault();
var _this=$(this);
$.post(_this.closest("form").attr("action"), _this.closest("form").serialize(),
function(res){
//check res variable value and do something as needed
// (may be redirect to another page /show/hide some widgets)
});
});
});
Assuming you have an action method called Save in your controller to handle the saving part.
Was given a neat article about this.
MVC Wizard Example
Basically this, you literally pass the name of the html button.
In the view form
<input type="submit" name="btnPrev" />
<input type="submit" name="btnNext" />
In the Controller
Controller
public ActionResult DoStuff(ModelClass mc,string btnPrev,string btnNext)
{
string actionString = "previousPage";
if(btnNext != null)
actionString = "nextPage";
return RedirectToAction(actionString,"Controller")
}