I am trying to do an edit without scarffolding and it is working the problem is that it cannot pick up the id or primary key of a record i am editing. Below is my code from an service repository until my view as i am using repository pattern.
here is my declaration in INTERFACE SERVICE REPOSITORY
void Edit(DonataryKind model);
here is my code/method in SERVICE REPOSITORY
public void Edit(DonataryKind model)
{
_donataryKindyRepository.Update(model);
}
here is my declaration in INTERFACE BUSINESS LOGIC
void Edit(DonataryKindView model);
here is my code/method in BUSINESS LOGIC
public void Edit(DonataryKindView model)
{
using (var donRepo = new DonataryKindRepository())
{
var objDon = donRepo.GetById(model.KindID);
if (objDon != null)
{
objDon.KindName = model.KindName;
donRepo.Edit(objDon);
donRepo.Save();
}
}
}
My code of edit in my CONTROLLER
[HttpGet]
public ActionResult Edit(int id)
{
var objdonk= new DonataryKindLogic().GetById(id);
return View(objdonk);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(int id, DonataryKindView model)
{
if (ModelState.IsValid)
{
try
{
var objDonK = new DonataryKindLogic();
objDonK.Edit(model);
return RedirectToAction("Index");
}
catch (Exception e)
{
ModelState.AddModelError(string.Empty, "Error! " + e.Message);
}
}
return View(model);
}
My code VIEW of edit
#model Sunday.Model.DonataryKindView
#{
ViewBag.Title = "Edit";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Edit</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>DonataryKindView</h4>
<hr />
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.KindID)
<div class="form-group">
#Html.LabelFor(model => model.KindName, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.KindName)
#Html.ValidationMessageFor(model => model.KindName)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Why don`t you just pick it from the model in the edit method ?
model.KindID
Related
I know this question has been asked so many times but checked all the answers with my code and I have no naming conflict and I pass the ID as a hidden field and yet the ViewModel passes to the controller as null.
This question is the link of my project idea and snippet of the code. What might I be missing?
I created a new project and copied your code into the project. I, too, recieved a null model when trying to create. It took a few changes to fix it for me. I had to add 'Model.AccountOrOU' as the passed in model to the partial. I had to initialize the model in the controller for the Create Index. I also had to change the name attribute on the properties of the AccountOrOU model in the view. Here is my code ->
Index.cshtml
#model AccountVM
#{
ViewData["Title"] = "Home Page";
}
<h2>Create</h2>
#using (Html.BeginForm("SaveAccount", "Home", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
<div>
#{await Html.RenderPartialAsync("_Account.cshtml", Model.AccountOrOU);}
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" class="btn btn-default" />
</div>
</div>
</div>
}
_Account.cshtml
#model AccountOrOU
<div class="form-horizontal" id="ViewData">
<h4>Account Partial</h4>
<hr />
<div class="form-group">
#Html.LabelFor(model => model.Name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<input type="text" name="AccountOrOU.Name" class="form-control" />
</div>
</div>
</div>
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Model
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace Test2.Models
{
public class AccountVM
{
public AccountVM()
{
AccountOrOU = new AccountOrOU();
}
public AccountOrOU AccountOrOU { get; set; }
}
public class AccountOrOU
{
public string Name { get; set; }
}
}
Controller
public IActionResult Index()
{
AccountVM blah = new AccountVM();
return View(blah);
}
public IActionResult SaveAccount(AccountVM input)
{
return View("Index", input);
}
See if this helps:
#{await Html.RenderPartialAsync("_Account.cshtml"}
Then change this line:
<input type="text" name="AccountOrOU.Name" class="form-control" />
to this:
#Html.TextBoxFor(x => x.AccountOrOU.Name, new { #class="form-control" })
I am using a custom exception filer to handle the exceptions at one place. As per my application requirement, the same view will display the error(business/generic) message on top of the view, but when I use below code to show the exception it shows blank page it does not return view after error. Here I am not getting how to return same view with currently bound model.
here is my ExceptionFilter class
public class AutoExceptionHandler : ActionFilterAttribute, IExceptionFilter
{
public void OnException(ExceptionContext filterContext)
{
Exception e = filterContext.Exception;
ModelStateDictionary modelState = ((Controller)filterContext.Controller).ModelState;
filterContext.ExceptionHandled = true;
Handle(e, modelState);
}
public void Handle(Exception ex, ModelStateDictionary modelState)
{
string message = "";
Int32? auditLogID;
Type typ = ex.GetType();
if (typ == typeof(IE.Factory.Http.HttpResponseException))
{
message = ex.Message;
}
else
{
message = MessageChannel.Instance.GetMessageDescription("GENERIC_ERR_MSG");
}
//auditLogID = Logger.SaveException(ex);
if (modelState != null)
{
modelState.AddModelError("", message);
}
}
}
This is my view.
#model MyApp.Model.User
#{
ViewBag.Title = "User";
Layout = "~/Views/Shared/_LayoutEmpty.cshtml";
}
<div>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="msg">
<div class="#(Html.ViewData.ModelState.IsValid ? "validation-summary-valid" : "validation-summary-errors") msg-pnl"
data-valmsg-summary="true">
<div class="msg-body">
<div class="text-danger">
The following error(s) occurred:
<ul class="validation-error">
#foreach (var modelError in Model.SelectMany(keyValuePair => keyValuePair.Value.Errors))
{
<li>#modelError.ErrorMessage</li>
}
</ul>
</div>
</div>
</div>
</div>
<div>
<div>
#Html.LabelFor(model => model.Name, "User Name:", new { #class = "m-0 p-0" })
</div>
<div>
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control-required" } })
</div>
<div>
#Html.ValidationMessageFor(model => model.Name, "*", new { #class = "text-danger" })
</div>
</div>
<div>
<div>
#Html.LabelFor(model => model.Address, "Address :", new { #class = "m-0 p-0" })
</div>
<div>
#Html.EditorFor(model => model.Address, new { htmlAttributes = new { #class = "form-control-required" } })
</div>
<div>
#Html.ValidationMessageFor(model => model.Address, "*", new { #class = "text-danger" })
</div>
</div>
<div>
<div >
<input type="submit" value="Save" class="btn-submit" id="btnSave" />
</div>
</div>
}
</div>
This is Controller
public class UserController : BaseController
{
public ActionResult User()
{
return View();
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult User(User model)
{
//Doing some db call that may throw error
throw new Exception("test exception");
return View(model);
}
}
You have to add the exception filter attribute to your controller method.
[AutoExceptionHandler]
public ActionResult User(User model)
{
//Doing some db call that may throw error
throw new Exception("test exception");
return View(model);
}
Have a look at this page how to create and use filter attributes:
https://www.c-sharpcorner.com/article/create-user-defined-filters-in-asp-net-mvc-5-in-step-by-step-process/
Also make sure you are passing data to your view in the request, otherwise under certain setups (where no exceptions are returned to the user view and error pages aren't setup), you might get a blank page.
I hope this helps.
Here is my view:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using NHibernate.AspNet.Identity;
namespace MyProject.Web.Models
{
public class IdentityRoleView
{
public virtual string Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<IdentityUser> Users { get; set; }
}
}
Here is my controller:
[HttpGet]
public ActionResult Edit(string roleId)
{
IdentityRole role = Service.Find(roleId);
return View("Edit", AutoMapper.Mapper.Map<IdentityRoleView>(role));
}
[HttpPost]
public ActionResult Edit(IdentityRoleView role)
{
Service.Update(role);
TempData["Comment"] = "The record was updated";
return RedirectToAction("Index");
}
}
Here is my view:
#model MyProject.Web.Models.IdentityRoleView
#{
ViewBag.Title = "Edit";
Layout = "~/Areas/Administration/Views/Shared/_Layout.cshtml";
}
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.Id);
<div>
Role name
</div>
<p>
#Html.TextBoxFor(model => model.Name)
</p>
for (int items = 0; items < Model.Users.Count; items++)
{
#Html.DisplayFor(m => m.Users[items].Id);
#Html.DisplayFor(m => m.Users[items].UserName);
}
<input type="submit" value="Save" />
}
IdentityRoleView.Users is always null when it is passed back to the controller. What is the problem? I can see IdentityRoleView.Users on the view - they are all deleted when posted back to the server as IdentityRoleView.Users is always null.
This is a bit of a quirk of MVC.
What you have is mostly correct.
Your controller is just fine:
[HttpGet]
public ActionResult Index()
{
Person p1 = new Person();
p1.name = "Ian";
List<Sport> sports = new List<Sport>();
Sport s1 = new Sport();
s1.description = "Football";
sports.Add(s1);
Sport s2 = new Sport();
//s1.description = "Netball"; I'm guessing this is a typo?
s2.description = "Netball";
sports.Add(s2);
p1.sports = sports;
return View("Person", p1);
}
[HttpPost]
public ActionResult Index(Person p1)
{
return View();
}
However, its the view that has the issue:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Person</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.name, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.name, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.name, "", new { #class = "text-danger" })
</div>
</div>
#* This is the key difference here *#
#for (int items = 0; items < Model.sports.Count; items++)
{
<div>
#Html.DisplayFor(sport => Model.sports[items].description)
#Html.HiddenFor(sport => Model.sports[items].description)
<hr />
</div>
}
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default"/>
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
So let me explain.
What I did was moved your sports list into the Form(){}. This needs to be done in order to actually have the form pass the objects back. Whatever is in that Form is what it is going to post back.
Another thing you have to do is give the form something to actually bind the model object to.
The #Html.DisplayFor helper won't do it, and since it appears you don't want users changing the sports, you can simply create a #Html.HiddenFor. This will pass back the values to your [HttpPost] Index(Person p1) method.
What you do after that, is up to you.
Hope this helps.
Here is the viewmodel
public class IdentityRoleView
{
public IdentityRoleView()
{
Users = new List<IdentityUser>();
}
public virtual string Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<IdentityUser> Users { get; set; }
}
The Controller Methods
public ActionResult Edit(string roleId)
{
IdentityRoleView model = new IdentityRoleView();
model.Name = "IdentityRoleViewUser";
model.Id = "2";
model.Users.Add(new IdentityUser {
UserName = "testuser",
Id = "1",
Email = "test#test.com"
});
model.Users.Add(new IdentityUser
{
UserName = "testuser2",
Id = "2",
Email = "test#test2.com"
});
return View("Edit", model);
}
[HttpPost]
public ActionResult Edit(IdentityRoleView model)
{
//Your logic
return RedirectToAction("Index");
}
Then View
#model MyProject.Web.Models.IdentityRoleView
#{
ViewBag.Title = "Edit";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.Id);
<div>
Role name
</div>
<p>
#Html.TextBoxFor(model => model.Name)
</p>
for (int i = 0; i < Model.Users.Count(); i++)
{
#Html.TextBoxFor(model => model.Users[i].Email)
#Html.TextBoxFor(model => model.Users[i].UserName)
}
<input type="submit" value="Save" />
}
If you want that user can not edit value then make TextBoxFor readonly.
The way i want a user to be determined to be logged in, is by putting his Username in session storage and then finding the User object that corresponds to that Username in the database.
this is how Login and SignUp look in the controller:
public ActionResult SignUp()
{
return View();
}
[HttpPost]
public ActionResult SignUp(User _user)
{
_user.Authorize = CustomRoles.RegisteredUser;
int lastuserid = entities.Users.Last().UserID;
_user.UserID = lastuserid + 1;
if (ModelState.IsValid)
{
Roles.AddUserToRole(_user.UserName, CustomRoles.RegisteredUser);
entities.Users.Add(_user);
entities.SaveChanges();
RedirectToAction("Index");
}
return View(_user);
}
public ActionResult Login()
{
LoginViewModel LVM = new LoginViewModel();
HttpCookie existingCookie = Request.Cookies["UserName"];
if (existingCookie != null)
{
LVM.UserName = existingCookie.Value;
}
Session["UserName"] = LVM.UserName;
return View(LVM);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginViewModel u)
{
if (ModelState.IsValid)
{
if (u.RememberMe)
{
HttpCookie existingCookie = Request.Cookies["UserName"];
if (existingCookie != null)
{
existingCookie.Value = u.UserName;
existingCookie.Expires = DateTime.Now.AddHours(-20);
}
HttpCookie newCookie = new HttpCookie("UserName", u.UserName);
newCookie.Expires = DateTime.Today.AddMonths(12);
Response.Cookies.Add(newCookie);
}
var v = entities.Users.Where(a => a.UserName.Equals(u.UserName) && a.Password.Equals(u.Password)).FirstOrDefault();
if (v != null)
{
return RedirectToAction("Index");
}
}
return View(u);
}
this is the actual Login page:
#{
ViewBag.Title = "Login";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#model Car_Dealership.Models.LoginViewModel
#using (Html.BeginForm("Login", "Home", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
if (#ViewBag.Message != null)
{
<div style="border:1px solid red">
#ViewBag.Message
</div>
}
<div>
<fieldset>
<legend>Login</legend>
<div class="editor-label">
#Html.LabelFor(u => u.UserName)
</div>
<div class="editor-field">
#Html.TextBoxFor(u => u.UserName)
#Html.ValidationMessageFor(u => u.UserName)
</div>
<div class="editor-label">
#Html.LabelFor(u => u.Password)
</div>
<div class="editor-field">
#Html.PasswordFor(u => u.Password)
#Html.ValidationMessageFor(u => u.Password)
</div>
<div class="editor-label">
#Html.LabelFor(u => u.RememberMe)
</div>
<div class="editor-field">
#Html.CheckBoxFor(u=>u.RememberMe)
#Html.ValidationMessageFor(u=>u.RememberMe)
</div>
<input type="submit" value="Login" />
Sign Up
</fieldset>
</div>
}
and to change the layouts i put this in _ViewStart:
#{
if (Session["UserName"]!=null)
{
Layout = "~/Views/Shared/UserLayout.cshtml";
}
else { Layout = "~/Views/Shared/_Layout.cshtml"; }
}
What happens is that Session["UserName"] gets the username in the first Login actionmethod from LVM which gets it from an already existing cookie, then i think everything just does what i think it's supposed to do but i still don't get the different layout page. i think it gets screwed up in the LINQ thing where i try to get a User object into v but i'm not really sure.
I have absolutely no idea why my model is null when I'm trying to submit this form with only ONE field, a dropdownlistfor selected value.
The Get works just fine, and the Model is defintively not null. But everytime I try to submit the form, model is always null and I have no idea why at the moment:
Model:
[Required]
public string SelectedOrderStatus { get; set; }
public List<SelectListItem> OrderStatuses { get; set; }
View:
#model Webstore.Models.OrderViewModel
#using (Html.BeginForm("Edit", "Order", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(m => m.OrderId)
<div class="form-horizontal">
<h4>Change Order Status for order: #Model.OrderId</h4>
<div class="form-group">
#Html.LabelFor(model => model.Orderstatus, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.SelectedOrderStatus, new SelectList(Model.OrderStatuses, "Value", "Text"))
#Html.ValidationMessageFor(model => model.SelectedOrderStatus)
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save Order Status" class="btn btn-default" />
</div>
</div>
</div>
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
Controller
[HttpPost]
public ActionResult Edit(Guid id, OrderViewModel model)
{
try
{
Order orderToEdit = _context.Orders.Find(id);
orderToEdit.Orderstatus = (Orderstatus)Enum.Parse(typeof(Orderstatus), model.SelectedOrderStatus);
_context.Entry(orderToEdit).State = EntityState.Modified;
_context.SaveChanges();
return RedirectToAction("Index");
}
catch
{
return View();
}
}
I would appreaciate a lot if you would help me out here!
Regards!
Try to check if FormCollection collection have value which you need. So your Edit methods will looks like:
public ActionResult Edit(Guid id, FormCollection collection)
{
// rest of logic here
}
Optionally, check in Request[..], like here:
public ActionResult Edit(Guid id)
{
var value1 = Request["SelectedOrderStatus"];
}
Of course this is not as beatifull solution as it should be, but there is some problem with model blinding which I cannot resolve without rest of code.