Multiple submit buttons with 2 actions which share the same information - c#

I have a problem about how to use 2 actions which must share an value in a view which contains 2 submit buttons. In a "Delete" view, I want to have to action : delete the person or desactivate the person (desactivate means assigning an end date to his contract).
Here is my submit buttons :
#using (Html.BeginForm()) {
<p>
<input type="submit" value="Delete"/>
<input type="submit" value="Desactivate" />
</p>
#Html.ActionLink("Back to List", "Index")
}
And there are my actions :
public ActionResult Delete(long id = 0)
{
Person person = db.Persons.Single(p => p.Id_Person == id);
if (person == null)
{
return HttpNotFound();
}
return View(person);
}
//
// POST: /Person/Delete/5
[HttpPost, ActionName("Delete")]
public ActionResult DeleteConfirmed(long id)
{
Person person = db.Persons.Single(p => p.Id_Person == id);
db.Persons.DeleteObject(person);
db.SaveChanges();
return RedirectToAction("Index");
}
[HttpPost]
public ActionResult Desactivate(long id)
{
Person person = db.Persons.Single(p => p.Id_Person == id);
person.EndDate = DateTime.Now;
db.Persons.Attach(person);
db.ObjectStateManager.ChangeObjectState(person, EntityState.Modified);
db.SaveChanges();
return RedirectToAction("Index", "Person");
}
I tried to separate my submit button into different forms but it didn't work and it's normal because I need to use the same id for the delete action and the desactivate action.
Any idea?

Try this
#using (Html.BeginForm()) {
<p>
<input type="submit" class="delete" value="Delete"/>
<input type="submit" class="deactivate" value="Desactivate" />
</p>
#Html.ActionLink("Back to List", "Index")
}
<scirpt type="text/javascript">
$(function(){
$(".delete").click(function (){
$(this).parents("form").action = "ControllerName/DeleteConfirmed";
return true;
});
$(".deactivate").click(function (){
$(this).parents("form").action = "ControllerName/Desactivate";
return true;
});
});
</script>

Related

How do i make an update to my database in ASP.NET MVC

i'm making a webbapplication with ASP.NET MVC and im trying to edit my list of objects. If i for example add a product to the site and then click on edit for that product to change the prize i just get a new object with the new prize instead of changing the prize to the product.
So the problem is that instead of updating the products it just adds a new one.
this is how my controller for the products looks like:
using auktioner_MarcusR91.Data;
using auktioner_MarcusR91.Models;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace auktioner_MarcusR91.Controllers
{
public class InventoryController : Controller
{
private readonly AppDbContext _db;
public InventoryController(AppDbContext db)
{
_db = db;
}
public IActionResult Index()
{
IEnumerable<Inventory> objInventoryList = _db.Inventories;
return View(objInventoryList);
}
//GET
public IActionResult Create()
{
return View();
}
//Post
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Create(Inventory inventory)
{
_db.Inventories.Add(inventory);
_db.SaveChanges();
return RedirectToAction("index");
}
//GET
public IActionResult Edit(int? id)
{
if (id == 0 || id == 5)
{
return NotFound();
}
var inventoryFromDb = _db.Inventories.Find(id);
if (inventoryFromDb == null)
{
return NotFound();
}
return View(inventoryFromDb);
}
//Post
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Edit(Inventory inventory)
{
if (ModelState.IsValid)
{
_db.Inventories.Update(inventory);
_db.SaveChanges();
return RedirectToAction("index");
}
return View(inventory);
}
}
}
I think there is something wrong in my controller.
However here is also my view for when i edit a product:
#model Inventory
<form method = "post" asp-action = "Edit">
<div class = "border p-3 mt-4">
<div class = "row pb-2">
<h2 class = "text-primary">Edit Inventory</h2>
<hr />
</div>
<div class = "mb-3">
<label asp-for ="inventoryName"></label>
<input asp-for = "inventoryName" />
<label asp-for ="finalPrize"></label>
<input asp-for = "finalPrize" />
<label asp-for ="inventoryDesc"></label>
<input asp-for = "inventoryDesc" />
<p>1 för "Transport</p>
<p>2 för "Smycken"</p>
<p>3 för "Hushåll"</p>
<p>4 för "Dekoration"</p>
<select asp-for = "categoryId">
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
</select>
</div>
<button type = "submit" class = "btn btn-primary" width = "100px">Update</button>
<a asp-controller = "Inventory" asp-action = "index" class = "btn btn-secondary" style = "width: 100px">Back to products</a>
</div>
</form>
You have to add a primary key inventoryId as a hidden input, without this key , you inventory instance looks like a new one for EF.
And since you are using [ValidateAntiForgeryToken] in the action, add this to view with another form syntax
#using (Html.BeginForm("Edit", "Inventory", FormMethod.Post))
{
#Html.AntiForgeryToken()
<input type="hidden" asp-for="inventoryId" value="#Model.inventoryId" />
....
<button type = "submit" class = "btn btn-primary" width = "100px">Update</button>
<a asp-controller = "Inventory" asp-action = "index" class = "btn btn-secondary" style = "width: 100px">Back to products</a>
</div>
}
and IMHO your update code could be like this
if (ModelState.IsValid)
{
var inventoryFromDb = _db.Inventories.Find(inventory.inventoryId);
if (inventoryFromDb == null)
{
return NotFound();
}
_db.Entry(inventoryFromDb).CurrentValues.SetValues(inventory);
var result= _db.SaveChanges();
}
You have to send your record id to the controller by clicking update button of the record . something like this :
<a class="btn btn-warning btn-sm btn-margin" asp-controller="ContextController" asp-action="UpdateAction" ***asp-route-id="#item.Id***">Update</a>
which #item is the object of the model sent to the view .
And the action would be :
[HttpGet]
public IActionResult UpdateAction(int id)
{
Model record = _Context.GetById(id);
return View("UpdateFormPageOrModal",record);
}
And after updating the form and clicking the submit button of the view data will send to action :
[HttpPost]
public IActionResult UpdateAction(Model record)
{
var result = _Context.UpdateBy(record);
ViewData["Result"] = result.Message;
if (result.IsSucceeded)
{
_UnitOfWork.Save();
return RedirectToAction("TheGridView");
}
return View("UpdateView",record);
}
where UpdateBy() method should be like this :
public void UpdateBy(T entity)//entity is an object of the DbSet<Model>
{
var state = _Context.Entry(entity).State;
if (state == EntityState.Detached)
{
_Context.Attach(entity);
}
_Context.Entry(entity).State = EntityState.Modified;
}

ASP.NET MVC: Method for HttpPost is not executed

In an ASP.NET MVC application, I have a file MessageController.cs where I define EditMessage for access via HttpGet and HttpPost. Usually the user first accesses it via HttpGet, then a form pops up where he can edit the message, and then he clicks on the Save button, by which HttpPost will be invoked.
My problem is that HttpPost is not invoked. Instead, an error message is displayed. I have analogous code for modifying other parts of the database and with that analogous code, HttpPost works. My question is why it does not work here and what I can do to make it work.
/// <summary>
/// used by admin only
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[CustomAuthorization(new[] { GlobalStaticFunc.SecurityOptions.isSUser, GlobalStaticFunc.SecurityOptions.isAdmin })]
[HttpGet]
public async Task<ActionResult> EditMessage(int id)
{
if (PopupController.AnyPopupsInline(User))
{
return RedirectToAction("index", "popup");
}
if (id > 0)
{
BLogic_Entity dbContext = new VMIEntityCreator(true);
var msg = dbContext.GetDbSet<MSG_MESSAGES>().Where(x => x.id == id).FirstOrDefault();
if (msg != null) return View(msg);
}
else if (id == -1)
{
return View(new MSG_MESSAGES() { id = -1 });
}
return View("Messages");
}
[CustomAuthorization(new[] { GlobalStaticFunc.SecurityOptions.isCarrier, GlobalStaticFunc.SecurityOptions.isAdmin, GlobalStaticFunc.SecurityOptions.isSUser })]
[HttpPost]
// [ValidateAntiForgeryToken]
public async Task<ActionResult> EditMessage(MSG_MESSAGES model)
{
if (PopupController.AnyPopupsInline(User))
{
return RedirectToAction("index", "popup");
}
if (!App_Tools.RightsHandler.IdentityWatcher.CheckUserRights(User.Identity, GlobalStaticFunc.SecurityOptions.isAdmin) && App_Tools.RightsHandler.IdentityWatcher.CheckUserRights(User.Identity, GlobalStaticFunc.SecurityOptions.isEndUser))
{
return RedirectToAction("Messages", "Message");
}
bool isOk = false;
if (model != null)
{
if (!ModelState.IsValid)
{
return View(model);
}
if (model.id > 0)
{
using (TED_BLOGIC.Abstractions.BLogic_Entity usr = new VMIEntityCreator(true))
{
isOk = await usr.UpdateSecurely(usr.GetDbSet<MSG_MESSAGES>().Where(x => x.id == model.id).FirstOrDefault(), model, ModelState);
}
}
}
return View(model);
}
The code of EditMessage.cshtml:
#model TED_BLOGIC.DataBase.DB_MODEL.MSG_MESSAGES
#{
if (Model != null && Model.id > 0)
{
ViewBag.Title = "Message bearbeiten";
}
else
{
ViewBag.Title = "Neue Message anlegen";
}
ViewBag.Ico = "fa-plus";
Layout = "~/Views/Shared/_standardBoxView.cshtml";
}
#using (Html.BeginForm("EditMessage", "Message", new { id = Model.id }, FormMethod.Post, new { data_goback = true }))
{
#Html.AntiForgeryToken()
<div class="panel-body">
<div class="row">
<div class="col-md-12 table-responsive">
#Html.ValidationSummary(false, "", new { #class = "text-danger" })
#Html.EditorForModel("Add/MGV")
<div class="section row mb10">
<div class="form-group">
<div class="col-md-offset-2 col-lg-3 col-md-4 col-sm-5">
<input type="submit" value="Save" class="btn btn-default" onclick=";" /> #*mCust.postToSite(#Url.Action("User", "Admin"));mCust.sendMeBack()*#
</div>
</div>
</div>
</div>
</div>
</div>
}
<script src="~/Scripts/Core/PostbackHandling/OverwritePostbacks.js?v=#GlobalStaticFunc.Version"></script>
<script>
$(document).on("EverythingIsReady", function () {
document.title = 'Cloud - #ViewBag.Title';
})
</script>
<input type="submit" value="Save" class="btn btn-default" onclick=";" />
Could you please try removing onclick attribute of the button?
please check by commenting CustomAuthorization attribute above that post method, the post-event will be fired. I tested your code.

Url seems to be repeating

I am using asp.net razor engine. I have a delete button and the first time I press it, it works. The second time the url repeats the function and does not work.
This is the first time I use the Delete button
This is the second time I use Delete. Notice the URL is del/del. Trying to avoid that second del.
Here is my controller
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using DapperApp.Factory;
using login.Models;
using login.Controllers;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Http;
namespace login.Controllers
{
public class HomeController : Controller
{
private readonly UserFactory userFactory;
public HomeController(UserFactory user) {
userFactory = user;
}
// GET: /Home/
[HttpGet]
[Route("")]
public IActionResult Index()
{
return View();
}
[HttpPost]
[Route("")]
public IActionResult Register(Home model)
{
if(!ModelState.IsValid)
{
return View("Index", model);
}
PasswordHasher<Home> Hasher = new PasswordHasher<Home>();
model.Password = Hasher.HashPassword(model, model.Password);
userFactory.Add(model);
TempData["message"] = false;
return RedirectToAction("Index");
}
[HttpPost]
[Route("login")]
public IActionResult Login(Home model)
{
if(model.Password == null || model.Email == null){
TempData["login"] = false;
return RedirectToAction("Index");
}
var pass = userFactory.FindByEmail(model);
var Hasher = new PasswordHasher<Home>();
if(pass == null)
{
TempData["login"] = false;
return RedirectToAction("Index");
}
// Pass the user object, the hashed password, and the PasswordToCheck
if(0 != Hasher.VerifyHashedPassword(model, pass.Password, model.Password))
{
TempData["first_name"] = pass.First_Name;
TempData["last_name"] = pass.Last_Name;
TempData["id"] = pass.Id;
HttpContext.Session.SetString("Id", pass.Id.ToString());
ViewBag.Quotes = userFactory.FindAll();
return View();
}
TempData["login"] = false;
return RedirectToAction("Index");
}
[HttpPost]
[Route("addQuote")]
public IActionResult AddQuote(Quotes model)
{
var test = HttpContext.Session.GetString("Id");
if(!ModelState.IsValid)
{
TempData["id"] = test;
model.Users_id = Convert.ToInt32(test.ToString());
var user2 = userFactory.FindById(model.Users_id);
TempData["first_name"] = user2.First_Name;
TempData["last_name"] = user2.Last_Name;
ViewBag.Quotes= userFactory.FindAll();
return View("Login", model);
}
if(test == null){
return RedirectToAction("Index");
}
model.Users_id = Convert.ToInt32(test.ToString());
userFactory.addQuote(model);
var user = userFactory.FindById(model.Users_id);
TempData["id"] = test;
TempData["first_name"] = user.First_Name;
TempData["last_name"] = user.Last_Name;
ViewBag.Quotes = userFactory.FindAll();
return View("Login", model);
}
[HttpGet]
[Route("logout")]
public IActionResult Logout()
{
return RedirectToAction("Index");
}
[HttpGet]
[Route("del/{id}")]
public IActionResult Del(int Id) // This is my delete method
{
userFactory.DeleteByID(Id);
ViewBag.Quotes2= userFactory.FindAll();
var test = HttpContext.Session.GetString("Id");
var user = userFactory.FindById(Convert.ToInt32(test));
TempData["first_name"] = user.First_Name;
TempData["last_name"] = user.Last_Name;
TempData["id"] = test;
return View("Login");
}
}
}
Here is my cshtml page
<h1>Hello #TempData["first_name"] #TempData["last_name"]</h1>
#if(TempData["first_name"]!= null)
{
}
#model login.Models.Quotes
<h1>Add Your Quote</h1>
#using(Html.BeginForm("AddQuote","Home"))
{
<p>
<label>Your Quote</label>
#Html.TextAreaFor(d=>d.quotes)
#Html.ValidationMessageFor(d => d.quotes)
</p>
<input type="submit" name="submit" value="Add my quote!"/>
}
<form action="logout" method="get">
<input type="submit" name="submit" value="Log Out"/>
</form>
<div >
#{
if(ViewBag.Quotes != null)
{
foreach(var quotes in ViewBag.Quotes)
{
//If there are any errors for a field...
<p><q>#quotes.quotes</q></p>
<p class="wrapper">-#quotes.First_Name #quotes.Last_Name at #quotes.Created_At.ToString("hh"):#quotes.Created_At.ToString("mm")
#quotes.Created_At.ToString("tt") #quotes.Created_At.ToString("MMM") #quotes.Created_At.ToString("dd")
#quotes.Created_At.ToString("yyyy")</p>
if(#quotes.Users_id == Convert.ToInt32(TempData["id"].ToString()))
{
<form action="del/#quotes.Id_Quotes" method="get">
<input type="submit" name="submit" value="Delete"/>
</form>
}
}
}
if(ViewBag.Quotes2 != null)
{
foreach(var quotes in ViewBag.Quotes2)
{
//If there are any errors for a field...
<p><q>#quotes.quotes</q></p>
<p class="wrapper">-#quotes.First_Name #quotes.Last_Name at #quotes.Created_At.ToString("hh"):#quotes.Created_At.ToString("mm")
#quotes.Created_At.ToString("tt") #quotes.Created_At.ToString("MMM") #quotes.Created_At.ToString("dd")
#quotes.Created_At.ToString("yyyy")</p>
if(#quotes.Users_id == Convert.ToInt32(TempData["id"].ToString()))
{
<form action="del/#quotes.Id_Quotes" method="get">
<input type="submit" name="submit" value="Delete"/>
</form>
}
}
}
}
</div>
Your form action is using a relative (not a concrete) reference, meaning it will append the action to the end of the current url each time you submit the form. Try making the action the absolute url of your get request. In this case that would mean:
<form action="del/#quotes.Id_Quotes" method="get">
<input type="submit" name="submit" value="Delete"/>
</form>
becomes
<form action="/del/#quotes.Id_Quotes" method="get">
<input type="submit" name="submit" value="Delete"/>
</form>
Also, just to nitpick, when deleting things (or adding and editing things) to a database you should use a POST request. They provide an additional level of security.

How do i pull user entered text data from a view for http POST?

I have written a basic GET method for my CheckoutController that returns the view, the view will have text boxes for my user to enter payment information and promo code. How do I access this data in the POST method?
// GET: Checkout
public ActionResult PaymentsAndPromotions()
{
return View();
}
[HttpPost]
public ActionResult PaymentsAndPromotions()
{
var order = new Order();
try
{
if (db.Promotions !== PromoCode)
{
return View(order);
}
else
{
//save the order
db.Orders.Add(order);
db.SaveChanges();
//process the order
var cart = ShoppingCart.GetCart(this.HttpContext);
cart.CreateOrder(order);
return RedirectToAction("Complete", new object{id = order.OrderID});
}
}
catch
{
//invalid - redisplay with error
return View(order);
}
}
View code:
<form>
Payment Method:<br /><br />
Credit Card #: <br />
<input type="text" name="CreditCardNum" /><br />
Credit Card Type: <br />
<input type="text" name="CreditCardType" /><br/>
Promo Code: <br />
<input type="text" name="PromoCode"/> <br />
I suggest you to create a model something like this:
public class Order
{
public string CreditCardNum { get;set; }
public string CreditCardType { get;set; }
public string PromoCode { get;set; }
}
And from your controller, do this:
public ActionResult PaymentsAndPromotions()
{
var order = new Order();
return View(order);
}
[HttpPost]
public ActionResult PaymentsAndPromotions(Order order)
{
//you can get all your order's property here.
//example:
if (order.CreditCardNum != "test123")
{
}
return View(order);
}
In your view, you can do something like this (assuming you are using razor syntax):
#model Models.Order
#using (Html.BeginForm())
{
#Html.TextBoxFor(m => m.CreditCardNum)
#Html.TextBoxFor(m => m.CreditCardType)
#Html.TextBoxFor(m => m.PromoCode)
<input type="submit" value="Submit"/>
}
adirks95, in controller you can fetch form data using such code: Request.Form["CreditCardNum"]. Please let me know if you still have any issues.

MVC3 Wrong form submits

From every page of a website I'm making, you're able to sign in. On pages with other forms, I get the following error after submitting: "Child actions are not allowed to perform redirect actions." All forms worked fine until I added the Sign In. In addition, the Sign In works correctly when you use it. The error only appears if I submit a different form. I've set a breakpoint and I've watched what happens when I hit the other submit. For some reason, the SignIn Post ActionResult is trying to run.
Any help would be appreciated.
Error:
Child actions are not allowed to perform redirect actions. Line 67: #{Html.RenderAction("SignIn", "Account");}
Sign In View
#using (Html.BeginForm("SignIn", "Account", FormMethod.Post))
{
<input class="signInSubmit" type="submit" name="submitButton" value="" />
}
Another Form in View
#using (Html.BeginForm("Confirm", "Cart", null, FormMethod.Post, new { #id = "productDetailsForm" }))
{
<input class="addToCart" type="submit" name="submit" value="" />
}
Sign In Controller
// GET: /Account/SignIn
public ActionResult SignIn()
{
return PartialView();
}
// POST: /Account/SignIn
[HttpPost]
public ActionResult SignIn(Customer customer)
{
try
{
//Stuff is here
return RedirectToAction("Index", "Home");
}
catch
{
return RedirectToAction("Registration");
}
}
Other form Controller
// GET: /Cart/
public ActionResult Index()
{
CartViewModel cart = getCart();
return View(cart);
}
//POST: Cart/Confirm
[HttpPost]
public ActionResult Confirm(int productID, bool certs, int quantity)
{
CartItemViewModel viewModel = new CartItemViewModel
{
Item = productRep.GetProductByID(productID),
Certs = certs,
Quantity = quantity
};
return View(viewModel);
}
HTML Source Code
<form action="/Account/SignIn" method="post">
<input class="signInSubmit" type="submit" value="" />
</form>
<form action="/Cart/Confirm" id="productDetailsForm" method="post">
<input class="addToCart" type="submit" value="" />
</form>
My solution
I renamed the POST ActionResult to anything other than SignIn. Does anyone understand why this works?
AccountController
// POST: /Account/Signed
[HttpPost]
public ActionResult Signed(Customer customer)
{
try
{
//Stuff is here
return RedirectToAction("Index", "Home");
}
catch
{
return RedirectToAction("Registration");
}
}

Categories