Merging Create and Edit ActionResults into one using ASP.NET MVC - c#

I currently have 4 action methods:
UserCreate - Get
UserCreate - Post
UserEdit - Get
UserEdit - Post
My goal is to somehow combine them do just be UserEdit. The only difference between the two is that UserCreate asks for Password and ConfirmPassword in the form. UserEdit does not. I ended up having to create 2 separate ViewModels for this: UserCreateViewModel, UserEditViewModel.
// GET: Users/Create
[Route("users/create")]
public IActionResult UserCreate()
{
return View();
}
// POST: Users/Create
[Route("users/create")]
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> UserCreate(UserCreateViewModel vm)
{
}
// GET: Users/Edit/5
[Route("users/edit/{id}")]
public async Task<IActionResult> UserEdit(string id)
{
return View(vm);
}
// POST: Users/Edit/5
[Route("users/edit/{id}")]
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> UserEdit(UserEditViewModel vm)
{
return View(vm);
}
If keeping them separate is better and more MVC convention that is fine, but I just want to make sure there isn't some feature of MVC that I am not utilizing that can help me have only 2 Action methods, One view model for this.

Keep them separate. You aren't missing anything in MVC that is meant for working 'around' this so to say. Sure you could make it work but it strays from common api implementation for the sake of thinking they are similar actions. If it feels really bad you could use the same view model and code validations into page and server side (you couldn't rely on it being on the model as validation would now require two scenarios for one model) but if also split it out into two. It's cleaner.

Yes you can certainly have only 2methods using mvc feature:
You can create a method for create or edit user for GET type
[HttpGet]
[Route("users/useraddoredit/{id?}")]
public async Task<IActionResult> UserEdit(string id)
{
if(string.isNullorEmpty(id))
{
return View();
}
return View(vm);
}
You can create a method for POST type. Here create a new model UserViewModel which has all the attributes.
[Route("users/edit/{id}/{isUserEdit?}")]
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> UserEdit(UserViewModel vm, bool isUserEdit)
{
if(isUserEdit)
{
return View(vm);
}
else
{
//logic for add user
}
}
PS:But its not recommended to use this approach.

Related

Use one controller CREATE method for multiple pages ASP.NET Core MVC

I have multiple models that get update in my database. Each model, like tasks, vendors, and invoicing is tied to a single job. When I create and edit one of these models (using the corresponding CRUD page), I want it to open up a create/edit page for all available models. For example, if I create a new job site, I also want the option to add in vendors, tasks, etc. If I edit a job site, I also want the option to edit all of the other models. As it stands, getting the details of these models to display as one page was simple. Getting the create function to work in the same way is more difficult. Is it possible to use one of my controller's create method to serve as a create method for another controller/model? Is it possible to do the same thing for the Edit and Delete functions? Here is what my controller looks like for the Create method.
// GET: Towers/Create
public IActionResult Create()
{
return View();
}
// POST: Towers/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,SiteId,SiteName,TowerHeight,TowerType,TaskOrder,Street,City,State,Zip,CrossStreet,Latitude,Longitude,Notes,StartDate,EndDate,ParsonsPm,ParsonsRe,DecomCrewNum,ActualStartDate,ActualEndDate")] Tower tower)
{
if (ModelState.IsValid)
{
_context.Add(tower);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(tower);
}
And the CREATE method for the Vendor controller (I haven't edited it from what dotnet scaffolds)
// GET: Vendors/Create
public IActionResult Create()
{
ViewData["TowerId"] = new SelectList(_context.Tower, "Id", "Id");
return View();
}
// POST: Vendors/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("VendorId,CraneVendor,CranePhone,PortableToiletVendor,PortableToiletPhone,ForkliftVendor,ForkliftPhone,SkidSteerVendor,SkidSteerPhone,ExcavatorVendor,ExcavatorPhone,LoaderVendor,LoaderPhone,SteelDumpstersVendor,SteelDumpstersPhone,ConcreteDumpstersVendor,ConcreteDumpstersPhone,CraneCallOffNum,CraneOnRent,CraneOffRent,PortableToiletOnRent,PortableToiletOffRent,ForkliftOnRent,ForkliftOffRent,SkidSteerOnRent,SkidSteerOffRent,ExcavatorOnRent,ExcavatorOffRent,LoaderOnRent,LoaderOffRent,SteelDumpstersOnRent,SteelDumpstersOffRent,ConcreteDumpstersOnRent,ConcreteDumpstersOffRent,ForkliftCallOffNum,SkidSteerCallOffNum,ExcavatorCallOffNum,LoaderCallOffNum,SteelDumpsterCallOffNum,ConcreteDumpsterCallOffNum,TowerId")] Vendor vendor)
{
if (ModelState.IsValid)
{
_context.Add(vendor);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
ViewData["TowerId"] = new SelectList(_context.Tower, "Id", "Id", vendor.TowerId);
return View(vendor);
}
For reference, this my DETAILS method in my TowerController
// GET: Towers/Details/5
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
var tower = await _context.Tower
.Include(v => v.Vendor)
.FirstOrDefaultAsync(m => m.Id == id);
if (tower == null)
{
return NotFound();
}
return View(tower);
}
You can see that it also includes the details of the relevant vendors for the particular site.

How do I create a GET form with validation in MVC

I'm having a real problem trying to articulate this seemly simple problem.
I have a single view that contains a FORM with a few search fields at the top of the view and the results of that search get shown on the same view after submitting the form.
I have a single HTTPGET controller method that takes the form fields as parameters and IF it was submitted by a user it will pass the model back to the view with the results to be shown and pre-populate the search form with what they filled out.
How can I tell if the page was loaded with default parameters vs. someone actually submitted the form.
What's the best way to accomplish this?
If I am understanding your question correctly then I think you need to consider the HttpGet attribute:
https://msdn.microsoft.com/en-us/library/system.web.mvc.httpgetattribute(v=vs.118).aspx
and the HttpPost attribute:
https://msdn.microsoft.com/en-us/library/system.web.mvc.httppostattribute(v=vs.118).aspx
Lets say you have a create method. The Http method would look like this:
[HttpGet]
public ActionResult Create()
{
}
and the post method would look like this:
[HttpPost]
public ActionResult Create(Person p)
{
//Logic to insert p into database. Could call an application service/repository to do this
}
RedirectToAction solve the problem.
you can go back to the get method after submited data and populate the view with default values
[HttpGet]
public ActionResult Create()
{
// fill model to default data
return view(model);
}
[HttpPost]
public ActionResult Create(Person p)
{
//do your stuff save data
return RedirectToAction("Create");
}
or
[HttpPost]
public ActionResult Create(Person p)
{
if(...)
{
//do your stuff any logic
return RedirectToAction("Create");
}
//do your stuff
return view(...);
}

How to Prevent Validators from Running on HttpGet?

After reading this very helpful answer I modified a pair of methods to allow them both to accept the same view model:
[ActionName("AddressCorrection"), HttpGet]
public IActionResult AddressCorrectionGet(AddressCorrectionViewModel model)
{
return View(model); // was return View();
}
[ActionName("AddressCorrection"), HttpPost]
[ValidateAntiForgeryToken]
public IActionResult AddressCorrectionPost(AddressCorrectionViewModel model)
{
if (ModelState.IsValid)
{
return View("Index", new ApplicationViewModel { SuccessMessage = "That worked." });
}
model.ErrorMessage = "Something went wrong";
return View(model);
}
Problem is, calling return View(model); in AddressCorrectionGet now treats the invocation as a POST of sorts. Specifically, the validators on AddressCorrection.cshtml run. Instead of seeing a blank form ready for input, I see a form with a bunch of “required fields missing” messages.
How do I prevent the View from running the validators in this case? Or in the more general case, how does the View know that it should vs. should not run validators (I was thinking this was simply based on whether the Request method was GET vs POST. Clearly wrong.), and how can I explicitly tell the view to run or not run the validators?
Use ModelState.Clear(); before return View(model);

All Views are not Loading in Browser

When I load the default page (http://localhost/MVCP/Home/index) it loads correctly, whereas when I load another view (http://localhost/MVCP/Home/Create) it doesn't load. How can I fix this?
My Create action in HomeController:
[HttpGet] [ActionName("Create")] public void Create() { }
Q: Do you have an action in your HomeController called Create?
A: Yes, [HttpGet] [ActionName("Create")] public void Create() { }
Your action return value is void and probably you even didn't write anything in response. change the signature of action to have an ActionResult as return a View.
public ActionResult Create()
{
return View();
}
To learn more:
Adding a View
in Getting Started with ASP.NET MVC 5 Series.
May be there is no view you have created so far and it seems your controller are inside area folder so have u checked your routeing too.
I think your Action should return ActionResult or ViewResult but certainly not "void" as you have written currently.
and also you should write
return view();
in Create action

MVC4 Ignoring [HttpGet] and [HttpPost] attributes

I am attempting to make a simple test website to allow me to list, create, edit and delete customer objects using MVC4.
Inside my controller I have 2 create methods, a Get for when the form loads with the controls, and a Post that actually saves the data.
//
// GET: /Customer/Create
[HttpGet]
public ActionResult Create()
{
return View();
}
//
// POST: /Customer/Create
[HttpPost]
public ActionResult Create(Customer cust)
{
if (ModelState.IsValid)
{
_repository.Add(cust);
return RedirectToAction("GetAllCustomers");
}
return View(cust);
}
However when I run the project and attempt to access the create action I get an error that:
The current request for action 'Create' on controller type 'CustomerController' is ambiguous between the following action methods:
System.Web.Mvc.ActionResult Create() on type [Project].Controllers.CustomerController
System.Web.Mvc.ActionResult Create([Project].Models.Customer) on type [Project].Controllers.CustomerController
My I understand that it can't see the difference between my Get and Post methods, but I have added the attribues. What could be the cause of this and how can I make it work again?
MVC does not authorize you to have 2 action methods with the same name.
BUT you can have 2 action methods with the same URI when the http verb differs (GET, POST). Use the ActionName attribute to set the action name. Don't use the same methods names. You can use any name. A convention is to add the http verb as the method suffix.
[HttpPost]
[ActionName("Create")]
public ActionResult CreatePost(Customer cust)
{
if (ModelState.IsValid)
{
_repository.Add(cust);
return RedirectToAction("GetAllCustomers");
}
return View(cust);
}

Categories

Resources