Model values no shown updated in the view after a POST - asp.net-mvc

I have the following simple (for the purpose of the question) controller/action:
public ActionResult Edit(int id)
{
User user = repository.GetUser(id);
return View(user);
}
[HttpPost]
public ActionResult Edit(User user)
{
user.Name = user.Name.ToUpper();
return View(user);
}
and the view was generated as Edit for User using regular steps in VS
The problem I'm facing is that whatever has been entered in the Name text-box will be showed back to the user but without uppercase. I have checked and the Edit action for POST is executed and user.Name value become UPPER CASE but .TextBoxFor is still using the value entered by the user which is in lower case.
I have checked also that if I print (without the use of any Html Helper) the value of Model.Name after the POST, it will be in UPPER CASE as I would like the Html.TextBoxFor to behave.
Is that a bug of MVC?
Is there a way of making this work in the way I want, meaning that the action/controller is the piece in the puzzle to decide data values for the View and not the view using the ones in the Request object and ignoring the changes that the controller might have done?
Thanks in advance

Use RedirectToAction and pass the Id for the user, it's not a bug, is the POST Behaviour
Update: You need to persist the information before RedirectToAction as Dismissile said, or use TempData if you do not want to persist.
[HttpPost]
public ActionResult Edit(User user)
{
user.Name = user.Name.ToUpper();
return RedirectToAction("Edit", new { id = user.id });
}

You can use ModelState.Remove(nameOfProperty) like:
[HttpPost]
public ActionResult Edit(User user)
{
ModelState.Remove("Name");
user.Name = user.Name.ToUpper();
return View(user);
}
It's will work.

Related

MVC: post, catching form values into property of page model

Alright...this may be a bit backwards but, I only need to do it in one spot.
I have a Model
public class LoginModel : xxx.Models.PageVars
{
public Item.LoginAttempt LoginAttempt { get; set; }
public LoginModel()
{
// does a bunch of stuff here...mainly to set the layout properties from PageVar
this.LoginAttempt = new Item.LoginAttempt();
}
}
Login Attempt is a simple obj (for now)
// login attempt
public class LoginAttempt
{
public string Email { get; set; }
public string Password { get; set; }
}
My controller
public ActionResult Login()
{
return View("Login", new Models.LoginModel());
}
[HttpPost]
public ActionResult LoginAttempt(LoginAttempt model)
{
return View("Login", model);
}
In my view
#model xxx.Models.LoginModel
Is there a way to use the property of the obj/model from LoginModel for the #model.
I can get the values from FormCollection or request but...that's not optimal.
thoughts???
tnx
The model for your GET should match the model for your POST. Otherwise, you're not playing on the same field. In order to allow the binding of data from a POST to a model, the HTML Helpers will generate a name that matches the access path of the property in the view's model. In other words, in your form, based on the model being LoginModel, your field names will be LoginAttempt.Email and LoginAttempt.Password. But, in the POST action, you're accepting just LoginAttempt, so the modelbinder is expecting to see data for Email and Password, which it won't find.
There's actually not even any need for this nested class. Just put your Email and Password fields directly on LoginModel and use that for both your view and your POST parameter. Then, you won't have any issues because everything will match up.
Why don't you have the form post controller action accept the parent model LoginModel instead of LoginAttempt? That way, the default MVC model binding should automatically parse the submitted values into the LoginModel and you'll have acces to LoginAttempt.
If it isn't then your form needs to use the prefix values in the names of the properties on the form. This is done automatically when you use TextboxFor, DropdownListFor etc.
In your example, the names of the form fields should start with LoginAttempt.Email etc
I've seen it work 2 ways. First way would be to rename your LoginAttempt model parameter to be
[HttpPost]
public ActionResult LoginAttempt(LoginAttempt loginModel)
{
return View("Login", model);
}
But i would use the Bind(Prefix) option
[HttpPost]
public ActionResult LoginAttempt([Bind(Prefix="LoginModel")] LoginAttempt model)
{
return View("Login", model);
}
you can't really return model of type LoginAttempt to the view though so you'd have to do even more work to get it to work if you're set on doing it this way. You should probably be redirecting to a different page instead of returning the Login view if it succeeds. Other wise return new LoginModel() {LoginAttempt = model}

Can I pass an alternative return link to an MVC controller action?

I have an MVC5 application which has several controllers, scaffolded with EF6 CRUD actions and associated views. One of these controller/view sets is used for managing a table of patient identifiers, and on completion of an edit or delete, the controller returns an action link to the identifiers index view, as expected.
However, the patient identifiers are also displayed on the various views of the patients controller, and from the Patient.Edit view I have Html.ActionLink calls to the identifier controller's edit or delete actions. When the latter are called from the Patient.Edit view, I would like them to return to that on completion.
Is there any way I can accomplish this?
Yes, but this is always a manual process. There's nothing built into MVC specifically for return URLs.
Essentially, your links to edit/delete will need to include a GET param, usually called returnUrl, though the name doesn't matter, which will be set to the current page URL. For example:
#Html.ActionLink("Edit", new { id = patient.Id, returnUrl = Request.RawUrl })
Then, your edit/delete GET action should accept this parameter, and set a ViewBag member:
public ActionResult Edit(int id, string returnUrl = null)
{
ViewBag.ReturnUrl = returnUrl;
return View();
}
In your edit form, add a hidden field:
#Html.Hidden("returnUrl", ViewBag.ReturnUrl)
In your POST edit action, again, accept the param:
[HttpPost]
public ActionResult Edit(int id, Patient model, string returnUrl = null)
But inside this action is where you'll do something different now. Typically, when you've got a successful post and have saved the object or whatever, you then do something like:
return RedirectToAction("Index");
However, instead, you should now check to see if returnUrl has a value, and if it does, redirect to that instead:
if (!string.IsNullOrEmpty(returnUrl))
{
return Redirect(returnUrl);
}
return RedirectToAction("Index");
The MVC5 with Identity sample project has a nice helper method that it uses:
private ActionResult RedirectToLocal(string returnUrl)
{
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
That would just go into your controller and basically does the same as I've already described with two notable differences:
It uses Url.IsLocalUrl to check that the return url is actually a URL on this site. That's a smart check, as since this is initially passed in the query string of the URL, it's open to be manipulated by a user.
It encapsulates the logic, so you don't have to remember how to this should be handled. When you have a successful POST, you simply return RedirectToLocal(returnUrl), and if there's a return URL set, it will be used. Otherwise, the fallback redirect will used.
This is how I did it in one of my projects:
public ActionResult Edit(int id, string returnUrl)
{
// find the model (code not shown)
return View(model);
}
In the Edit view you don't need to do anything special, in the Post Action you have
[HttpPost]
public ActionResult Edit(Model model)
{
if (ModelState.IsValid)
{
// save Model...
return Redirect(Request.Params["returnUrl"]);
// Request.Query will work as well since it is in the querystring
// of course you should check and validate it as well...
}
// else return the View as usual, not shown
}
To use it, when creating the "Edit" link from your pages you simply need to specify the extra returnUrl parameter:
#Html.ActionLink("Edit", "Edit",
new { controller = "YourController",
returnUrl = Url.Action("Index", "ThisController",)
})
Hope it helps.

MVC (MVC3) checking is a Model exists or has values

I have created a working DropDownListFor which gets the data from a selectlist which is Model.IssueSocialSec and then setting the value coming from the database is Model.SocialDBValue
However, when I click a Edit link which with query a repository passing the Model back to the page, that works, but if I do a needed redirect route to the page and nothing is there to bind the Model, then the page fails. I'm going to try having it pass back an empty Model, but I figured I would post this as I always like to hear feedback on "best practices" and lessons learned.
#Html.DropDownListFor(m => m.SelectedSocial, new SelectList(Model.IssueSocialSec, "Value", "Text", Model.SocialDBValue), "")
It sounds like you just need to wrap the DropDownListFor in a <form> with a url pointing to an action that will allow you to edit. The form can use a GET request if it's an idempotent operation and you could use JavaScript to submit the form when the value of the <select> is changed, falling back to rendering a button for submission for when JavaScript is disabled.
Generally, I structure MVC controllers and actions as so
public class ProfilesController : Controller
{
public IProfileRepository Profiles { get; private set; }
public ProfilesController(IProfilesRepository profiles)
{
Profiles = profiles;
}
[HttpGet]
public ActionResult Index()
{
var profiles = Profiles.All();
return View(new ProfilesModel { Profiles = profiles });
}
[HttpGet]
public ActionResult Edit(int id)
{
var profile = Profiles.GetById(id);
return View(new ProfileModel { Profile = profile });
}
[HttpPost]
public ActionResult Edit(ProfileModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
var profile = Profiles.GetById(id);
// update the profile
Mapper.Map(model, profile);
if (Profiles.Update(profile))
{
TempData["message"] = "Profile updated successfully";
}
return RedirectToAction("Edit");
}
}
Index will render all the profiles. Against each profile, an <a> will be rendered with a URL pointing to Edit and the URL will include the id for the profile to edit. Edit view will post a form to Edit and the profile will be updated with changes from the model.
I recommend looking at something like NerdDinner or MVC Music store to get an idea of how they structure their code.
I ended up fixing it like this:
ChildInfoModel childviewmodel = new ChildInfoModel();
return View(childviewmodel);
before I was trying to just do:
return View()

Passing ViewModel Object From ActionMethod to Another

I have a registration form in the side bar of my web application. When the user submits the entered data, the user should be redirected to another page with a more complete registration form when he can fill the rest of the data. The data that was entered by the user in the first form should be already there in the second form, but that's not happening... I checked to see the value of the view model I'm passing to the second action method and it was null and in the browser's address bar I get:
http://localhost:2732/User/RegisterPage?model=Sharwe.MVC.ViewModels.RegisterPageViewModel
Here's the code:
public ActionResult Register()
{
return PartialView(new RegisterViewModel());
}
[HttpPost]
public ActionResult Register(RegisterViewModel dto)
{
var model = Mapper.Map<RegisterViewModel, RegisterPageViewModel>(dto);
return RedirectToAction("RegisterPage", "User", new { viewModel = model });
}
public ActionResult RegisterPage(RegisterPageViewModel viewModel)
{
return View(viewModel);
}
Isn't that the way to do this? Or am I missing something here...?
The Dictionary passed to RedirectToAction() is the Route Value not the View Model. And RedirectToAction() is basically telling the browser to go to a certain URL. Browser by default makes the GET request and obviously you lose your data.
For this, you need to use TempData dictionary. You can store view model in TempData and then RedirectToAction() to RegisterPage. TempData saves the data for only 1 request span and would delete it automatically. It's ideal for this scenario.
See this for more details > The value for a object inside a viewmodel lost on redirect to action in asp.net mvc 2.0?
In this particular case you don't need to use RedirectToAction, you can simply call the RegisterPage action directly:
[HttpPost]
public ActionResult Register(RegisterViewModel dto)
{
var model = Mapper.Map<RegisterViewModel, RegisterPageViewModel>(dto);
return RegisterPage(model);
}
public ActionResult RegisterPage(RegisterPageViewModel viewModel)
{
return View(viewModel);
}

asp.net-mvc / linq to sql - do i always need an HTML.TextBox to do a Edit Save?

I have a UserController and an Edit.aspx. There is one field that is my primary key so i dont want to allow users to edit this field.
The issue is that if i remove the
<%= Html.TextBox("Email", Model.Email) %>
then when the asp.net-mvc magic calls my Controller code:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, tblMailingList user_)
{
try
{
repo.UpdateUser(user_);
return RedirectToAction("Index");
}
catch
{
return View();
the email field of the tblMailingList is null. The issue is that i need this as the lookup in the table to retrieve the current record and obviously if its null i get an exception.
When i put the textbox back for this field, it works fine. It seems crazy that i would have to have a textbox and allow editing to pass this field over to the controller. i tried putting it in a label and it still shows up as null in the controller.
any suggestions?
My first question would be why are you doing the lookup on the Email field and not the Id field?
You can pass parameters in your Form declaration to be passed through to your Controller.
<% using (Html.BeginForm(
"MethodName", "Controller", FormMethod.Post,
new { id = Model.Id, email = Model.Email)) { %>
I'm not sure if I got the method declaration correct so please check.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, string email, tblMailingList user_)
{
try
{
repo.UpdateUser(user_);
return RedirectToAction("Index");
}
catch
{
return View();
I would recommend updating slightly differently as your tblMailingList user will not be valid to be updated in your Repository.
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection form)
{
tblMailingList user = repo.GetUser(id); // get the user using the id
// so we can update in the same
// context
UpdateModel(user); // this will automatically update
// your user model with values
// from the form
try
{
repo.UpdateUser(user);
return RedirectToAction("Index");
}
catch
{
return View();
If you just want a field that could passed to controller which needs to be invisible in the form, Html.HiddenField could work for your case.
Do I get wrong?

Resources