I have a view that displays a list of user roles (e.g., administrator, operator, etc) and an "Add" button that pops up a modal window, allowing the user to add a new role.
In my controller, I have this as my HttpPost method
[HttpPost]
public ActionResult Create(RoleModel model)
{
if (ModelState.IsValid)
{
var role = new RoleDto
{
Name = model.Name,
Description = model.Description
};
var roleAdded = _rolePermissionsRepository.AddRole(role);
if (roleAdded != null)
{
//CLOSE WINDOW
}
else
{
//PRINT ERROR MSG TO WINDOW
}
}
return View();
}
If the add to the DB is successful, I would like to close the modal window, and then refresh the list on my main index page.
If there's some error while persisting to the DB, the modal window should remain open, and show some error.
How do I achieve that?
This is what I'm using on my Index page to pop up the window
$("#open").click(function (e) {
wnd.center();
wnd.open();
});
You should return a JsonResult to tell the browser what happened.
[HttpPost]
public ActionResult Create(RoleModel model)
{
if (ModelState.IsValid)
{
var role = new RoleDto
{
Name = model.Name,
Description = model.Description
};
var roleAdded = _rolePermissionsRepository.AddRole(role);
if (roleAdded != null)
{
//CLOSE WINDOW
return Json(new { success = true });
}
else
{
return Json(new { error = "Error! Can't Save Data!" });
}
}
return Json(new { error = "Generic Error Message!" });
}
Here is the javascript that should run in your wnd page, you show the error message if there is any, otherwise you close the window.
$('form').submit(function(e) {
e.preventDefault();
$.post(this.action, $(this).serialize(), function(response) {
if(response.error) {
alert(response.error);
}
else {
wnd.close();
}
}, 'json');
});
Related
I'm fairly new to MVC, I need to create a custom error that would fire if the user does not select a category. However the Html.ValidationSummary is not populating when a product without categories is created. Instead the view is returned and shown on the browser without the validation summary being populated. Please see below, I've have copied the relevant code over.
CSHTML CODE
#Html.ValidationSummary(false, "", new { #class = "text-danger" })
CONTROLLER CODE
if (!model.HasCategories)
{
ModelState.AddModelError(string.Empty, "A category is required.");
}
if(!ModelState.IsValid()) {
return RedirectToAction("addEditProduct", new { id = model.P.ID});
}
when you are using ModelState errors you should use return View() instead of Redirect
public ActionResult addEditProduct()
{
return View();
}
[HttpPost]
public ActionResult addEditProduct(EditProductModel model)
{
if (!model.HasCategories)
{
ModelState.AddModelError(string.Empty, "A category is required.");
return View(new { id = model.P.ID });
}
if (!ModelState.IsValid())
{
return View(new { id = model.P.ID });
}
}
//I Have a Action Method
[HttpGet]
public ActionResult Login()
{
return View();
}
[HttpPost]
public ActionResult Login(VmUser_User VmUser_User)
{
if (VmUser_User.User_User.UserName == null ||
VmUser_User.User_User.Password == null)
{
VmUser_User.LblError = "Please enter Username and Password";
return View(VmUser_User);
}
//Return valid user
if (VmUser_User.LoginUser() > 0)
{
Session["One"] = VmUser_User;
return RedirectToAction("Index", "Home");
}
else
{
VmUser_User.LblError = "User/Password does not match!";
}
return View(VmUser_User);
}
//And another Action Method
public async Task<ActionResult> Common_Unit()
{
Oss.Romo.ViewModels.User.VmUser_User user =
(Oss.Romo.ViewModels.User.VmUser_User)Session["One"];
if (user == null)
{
return RedirectToAction("Login", "Home");
}
vmCommon_Unit = new VmCommon_Unit();
await Task.Run(() => vmCommon_Unit.InitialDataLoad());
return View(vmCommon_Unit);
}
When a valid user login application, it redirect to Home/Index page, then he request for Common/Common_Unit page. After expire the session and user relogin the application I want to redirect in last requested page like Common/Common_Unit, please someone help me to solve this problem.
My Question : When a authorized user browse a specific page then he inactive some time. In the min time session out occurred and user go to login page. After login I want to redirect user on this specific page. Sorry for my Bad English
Try to use ReturnUrl parameter, like this:
[HttpGet]
public ActionResult Login()
{
return View();
}
[HttpPost]
public ActionResult Login(VmUser_User VmUser_User)
{
if (VmUser_User.User_User.UserName == null ||
VmUser_User.User_User.Password == null)
{
VmUser_User.LblError = "Please enter Username and Password";
return View(VmUser_User);
}
//Return valid user
if (VmUser_User.LoginUser() > 0)
{
Session["One"] = VmUser_User;
if (Request.QueryString["ReturnUrl"] != null & Request.QueryString["ReturnUrl"] != "")
{
Response.Redirect(Request.QueryString["ReturnUrl"]);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
VmUser_User.LblError = "User/Password does not match!";
}
return View(VmUser_User);
}
//And another Action Method
public async Task<ActionResult> Common_Unit()
{
Oss.Romo.ViewModels.User.VmUser_User user =
(Oss.Romo.ViewModels.User.VmUser_User)Session["One"];
if (user == null)
{
return RedirectToAction("Login", "Home", new { ReturnUrl = "/Common/Common_Unit" });
}
vmCommon_Unit = new VmCommon_Unit();
await Task.Run(() => vmCommon_Unit.InitialDataLoad());
return View(vmCommon_Unit);
}
I have this controller:
[Authorize]
public class CheckoutController : Controller
{
ShoppingCartContext storeDB = new ShoppingCartContext();
const string PromoCode = "FREE";
[HttpPost]
public ActionResult AddressAndPayment(FormCollection values)
{
var order = new Order();
TryUpdateModel(order);
try
{
if (string.Equals(values["PromoCode"], PromoCode,
StringComparison.OrdinalIgnoreCase) == false)
{
return View(order);
}
else
{
order.Username = User.Identity.Name;
order.OrderDate = DateTime.Now;
//Save Order
storeDB.Orders.Add(order);
storeDB.SaveChanges();
//Process the order
var cart = Models.ShoppingCart.GetCart(this.HttpContext);
cart.CreateOrder(order);
return RedirectToAction("Complete",
new { id = order.OrderId });
}
}
catch
{
//Invalid - redisplay with errors
return View(order);
}
}
public ActionResult Complete(int id)
{
// Validate customer owns this order
bool isValid = storeDB.Orders.Any(
o => o.OrderId == id &&
o.Username == User.Identity.Name);
if (isValid)
{
return View(id);
}
else
{
return View("Error");
}
}
}
And I have created a View called AddressAndPayment under Checkout, so it goes to localhost/Checkout/AddressAndPayment but I only get a 404 error, even if I right click on the View and click on view in Page Inspector. I don't know why its not even showing the view when it is created.
You need a corresponding HttpGet method, as your current one only accepts a HttpPost request. Add the following:
[HttpGet]
public ActionResult AddressAndPayment()
{
return View();
}
I'm currently working on a ASP.NET MVC 4 project as a trainee and I'm trying to implement an admin panel. The goal is to show all the users on a grid (MVC.GRID) and edit them on the same page.
I've managed to show all the users on the grid and once a user is selected it shows the info below the grid and puts it in a form (via ajax/jquery).
The problem is: the form validation is being displayed on a new page and not on the page where the grid is at. And I've no idea why..
Below is my code.
This is where the form is placed:
<div id="order-content">
<p class="muted">
Select a user to see his or her information
</p>
</div>
The form itself (partial view "_UserInfo):
#using (Ajax.BeginForm("EditUser", "Admin", FormMethod.Post,
new AjaxOptions
{
InsertionMode = InsertionMode.Replace,
HttpMethod = "POST",
UpdateTargetId = "order-content"
}))
{
#Html.Bootstrap().ValidationSummary()
#Html.Bootstrap().ControlGroup().TextBoxFor(x => x.Id)
#Html.Bootstrap().ControlGroup().TextBoxFor(x => x.Name)
#Html.Bootstrap().ControlGroup().TextBoxFor(x => x.Password)
#Html.Bootstrap().SubmitButton().Text("Opslaan").Style(ButtonStyle.Primary)
}
JQuery to show the user info once a row is selected:
$(function () {
pageGrids.usersGrid.onRowSelect(function (e) {
$.post("/Admin/GetUser?id=" + e.row.Id, function (data) {
if (data.Status <= 0) {
alert(data.Message);
return;
}
$("#order-content").html(data.Content);
});
});
});
My AdminController:
[HttpPost]
public JsonResult GetUser(int id)
{
var user = _UserService.Get(id);
var input = _EditInputMapper.MapToInput(user);
if (user == null)
return Json(new { Status = 0, Message = "Not found" });
return Json(new { Content = RenderPartialViewToString("_UserInfo", input) });
}
[HttpPost]
public ActionResult EditUser(AdminUserEditInput input)
{
if (ModelState.IsValid)
{
// todo: update the user
return View();
}
// This is where it probably goes wrong..
return PartialView("_UserInfo",input);
}
Can anyone see what is wrong with my code?
Thank you.
When the ModelState is valid and you return View(), does this full view gets embedded in order-content? I suspect not, and if so it would be because the ajax request is not being sent . You may not have included the jquery.unobtrusive-ajax js file
I've got it working now.. With the use of an jsonvalidator. I don't know if it's a good solution but it does the job for now..
This is what I've changed in my AdminController
[HttpPost]
public ActionResult EditUser(AdminUserEditInput input)
{
int id = (int)TempData["UserID"];
if (ModelState.IsValid)
{
_UserService.ChangeMail(id, input.Mail);
_UserService.ChangeName(id, input.Firstname, input.Name);
return new RedirectResult(Url.Action("Users", "Admin") + "#id=" + id);
}
else
{
return new JsonResult { Data = new { Valid = false, Errors = Validator.CheckModelErrors(ModelState) } };
}
}
Added a JsonValidator class:
public static class Validator // todo: doesn't belong in the controllers directory ?
{
public static List<JsonError> CheckModelErrors(System.Web.Mvc.ModelStateDictionary modelstate)
{
List<JsonError> errorlist = new List<JsonError>();
if (modelstate != null && modelstate.Values != null)
{
foreach (var m in modelstate.Values)
{
if (m.Errors != null)
{
foreach (var e in m.Errors)
{
errorlist.Add(new JsonError() { ErrorMessage = e.ErrorMessage ?? "" });
}
}
}
}
return errorlist;
}
}
And a JsonError class..
public class JsonError // todo: doesn't belong in the controllers directory ?
{
public string ErrorMessage { get; set; }
public bool HasError { get; set; }
public bool CanPerform { get; set; }
}
Last but not least, js:
$(document).on('submit', '#UserForm', function (e) {
e.defaultPrevented();
$('#UserForm').validate();
$.post($(this).attr("action"), $(this).serialize(), function (json) {
if (json.Valid) {
$("#order-content").html('<p class="muted"> Select a user.</p>');
}
else {
$("#ShowValidation").empty();
var list = $("#ShowValidation").append('<ul></ul>').find('ul');
$.each(json.Errors, function (index, optionData) {
list.append('<li>' + optionData.ErrorMessage + '</li>');
})
}
}, "json");
});
I was thinking about another way to manage this because this is just temporary..
Should it be a good idea to store the input with the validation messages in a session and let js put it in the _UserInfo view?
Currently I'm using a standard Manage page which looks like:
public ActionResult Manage(string type = "")
{
ViewBag.Type = type;
switch (type)
{
case "EmailAddress":
ViewBag.EmailAddress = lol.UserProfiles.Find((int)Membership.GetUser().ProviderUserKey).EmailAddress;
break;
case "Password":
break;
default:
break;
}
//ViewBag.ReturnUrl = Url.Action("Manage/" + type);
return View();
}
Now my Manage Model page looks like:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Manage(ManageViewModel model)
{
if (ModelState.IsValid)
{
if (model.EmailAddressModel != null)
{
try
{
int userID = (int)Membership.GetUser().ProviderUserKey;
var User = lol.UserProfiles.First(f => f.UserId == userID);
User.EmailAddress = model.EmailAddressModel.EmailAddress;
int saveStatus = lol.SaveChanges();
if (saveStatus == 1)
{
ViewBag.StatusMessage = MessagesEnum.ChangeEmailSuccess;
return RedirectToAction("Manage/EmailAddress", "Account");
}
else
{
ModelState.AddModelError("", MessagesEnum.ChangeEmailFailed);
}
}
catch { }
}
else
{
// ChangePassword will throw an exception rather than return false in certain failure scenarios.
bool changePasswordSucceeded;
try
{
changePasswordSucceeded = WebSecurity.ChangePassword(User.Identity.Name, model.PasswordModel.OldPassword, model.PasswordModel.NewPassword);
}
catch (Exception)
{
changePasswordSucceeded = false;
}
if (changePasswordSucceeded)
{
ViewBag.StatusMessage = MessagesEnum.ChangePasswordSuccess;
return RedirectToAction("Manage/Password", "Account");
}
else
{
ModelState.AddModelError("", MessagesEnum.ChangePasswordFailed);
}
}
}
else
{
}
return View(model);
}
My ManageViewModel class:
public class ManageViewModel
{
public LocalPasswordModel PasswordModel { get; set; }
public LocalEmailAddressModel EmailAddressModel { get; set; }
}
What I was trying to do is make it where if they access multiple pages on Manage such as /Manage/Password and /Manage/EmailAddress and according to those links, it will post a different page. Now first of all, is this the proper way of doing it / is it fine? Second, if it is, I was trying to pass a Sucess message after they change their email to the /Manage/EmailAddress page, but the ViewBag.StatusMessage is not outputting anything on my HTML page. Why is that?
I did a little more research and here are my findings (tell me if it's correct or not). So I should edit the route settings and so something like:
routes.MapRoute(
name: "Manage",
url: "Account/{controller}/{action}/{id}",
defaults: new { controller = "Manage", action = "Index", id = UrlParameter.Optional}
);
And just make a new controller called Manage and create new Functions inside the controller for different pages like ChangeEmail and ChangePassword?
ViewBag and ViewData are only valid for the current request, they wont survive if you do a redirect RedirectToAction. Use TempData instead.
Check the following: ViewBag, ViewData and TempData
If you want your URLs to be /Manage/Password and /Manage/EmailAddress, you dont need to add a new MapRoute, the default one will work. Just use ManageController for you controller class name and "Password" and "EmailAddress" for your function names:
class ManageController
{
public ActionResult Password(...)
{