I'll explain a generic scenario. To explain my problem, I'd request some patience until I explain how I've implemented my Search page. I've a [Users] table and an [Address] table based on which I've derived a VIEW in SQL - [vw_User_Addr]. I'm using L2S so that I'll get a strongly typed vw_User_Addr class.
Now I've a search page so apparently I'll derive it from this class like:
<%# Page Title="User Search" ... Inherits="System.Web.Mvc.ViewPage<MyDAL.vw_User_Addr>" %>
My List action returns a List which I use in the View to form the Grid table. Everything is fine till here. Now I want to implement search on this page. So this is what I found -
In Controller during the List Action I create ViewData["Usr"] = new vw_User_Addr();
In my View I do:
<% MyDAL.vw_User_Addr usr =
((MyDAL.vw_User_Addr)(ViewData["Usr"]));
%>
Then I use this "usr" object to bind my search control like:
<%= Html.TextBox("FirstName",
usr.FirstName)%>
Finally in my HttpPost handler action I object vw_User_Addr like:
[HttpPost] public ActionResult
List(vw_User_Addr searchObj){...}
I use this searchObj to extract the values user might have populated in the search controls and then I perform search.
So, I hope I explained well. This is how I do my search in MVC2. Here're my concerns/questions:
When I click on the search image-button I get a postback but it fires a ModelValidation which gives error. Not with all but atleast those which are int (i.e. like Roles dropdown search control) - how to handle that?
After a lot of R&D I've settled that I've to either do a ModelState.Clear(); or a more complex way to handle this. Is there a better option?
Is there a better way to achieve the search-implementation explained above? Am I using the standard\best way to do search in MVC?
At core-level, can I make L2S understand that this is an SQL-View so its Readonly and all the fields can be NULL - so that the Mr.DefaultModelValidator doesn't perform such illogical validations?
If 4. is viable can I set the properties of all the fields in the L2S view to be readonly to give it a try?
Thank you for your precious time and review. Pls guide me if I'm not on the track. I believe this is the simplest search-scenario and so it has to be easy. Just need to find the missing links.
I implemented an ActionFilterAttribute [SkipModelValidation] which avoids/eliminates unwanted model validation for search pages.
Ref: How to disable validation in a HttpPost action in ASP.NET MVC 3?
public class SkipModelValidationAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
//Get ModelState
ModelStateDictionary dict = ((Controller)filterContext.Controller).ModelState;
if (dict != null && !dict.IsValid)
dict.Clear();
base.OnActionExecuting(filterContext);
}
}
Related
I'm new to MVC and I'm trying to make my first Ajax page, PartialView.
What I have is an action that looks like this
[HttpGet]
public PartialViewResult MainContent()
{
return PartialView(new ClockingModel());
}
[HttpPost]
public PartialViewResult MainContent(SingleClocking SingleClocking)
{
// update logic
return PartialView(myClockingModel);
}
What I'm finding is that the view renders, I enter some data and it posts back correctly. My partial view includes a select:
#Html.DropDownListFor(x => x.SingleClocking.InWhen, new SelectList(Model.DropDownValues, Model.DefaultValue))
The select shows the value that I changed on the GUI, not the new DefaultValue?
If this was plain Asp.Net I would assume this was because of postback data or viewstate but MVC doesn't use either of these?
I can see the view being returned via fiddler, it shows the select with the value as per the client, not the value I expected to see via the server.
I'm sure I'm just missing something simple here, any hints?
Also, could I look at the value of the View on the server? This won't work but something like: string viewText = PartialView(myClockingModel);
p.s. I don't think it's caching, I tried switching that off with an attribute
And even though I searched for a while before asking this I finally found the answer.
I feel a bit better knowing that Rick Strahl had the same confusion.
Posting as an answer for others to find
Long story short, this is by design, the blog is worth reading
http://weblog.west-wind.com/posts/2012/Apr/20/ASPNET-MVC-Postbacks-and-HtmlHelper-Controls-ignoring-Model-Changes
In the process of updating a C# MVC 2.0 application!
I have a view “Signup” and another view “ForgotPassword”.
Each view have a with a submit button.
Each form is submitted to the same Controller but to two different ActionResult:
[HttpPost]
public ActionResult Signup(SignupModel signupModel)
{…}
[HttpPost]
public ActionResult ForgotPwd(ForgotPasswordModel forgotPasswordModel)
{…}
Upon completion my goal is to redirect the user to a “thankyou” page but based on where the user is coming from (either Signup or ForgotPassword) I wish to display a particular message (or a different UI).
Inside the same Controller, I created a “Thankyou” ActionResult:
public ViewResult Thankyou()
{
return View();
}
I was thinking of adding a parameter to my Thankyou() method which would allow me to know where the user is coming from (Signup or ForgotPwd). From there, make the “thankyou” page display the appropriate UI/message.
I’m looking for a clean and simple solution.
Should I create two View User Controls and show the appropriate one based on the parameter being passed?
In addition, instead of having an “ActionResult” for my Thankyou() method couldn’t I use a “PartialViewResult” ?
EDIT:
I was actually considering something along those lines…
Where ThankyouType is an Enum.
[HttpPost]
public ActionResult Signup(SignupModel signupModel)
{
//Validation code...
return View("Thankyou", ThankyouType.SignupDone);
}
[HttpPost]
public ActionResult ForgotPassword(ForgotPasswordModel forgotPasswordModel)
{
//Validation code...
return View("Thankyou", ThankyouType.ForgotPasswordDone);
}
And then have my “Thankyou” ViewResult like this:
public ViewResult Thankyou(ThankyouType type)
{
return View(type);
}
Doesn’t seem like I can create a strongly typed view based on Enum (unless I’m wrong).
Perhaps I’ll read more on PartialViewResults and/or find examples…but then again, I could be completely wrong.
I would personally give the ThankYou view a model that has the message you want to display, and have your two controller actions render the ThankYou view directly on success rather than calling a ThankYou action.
However, if you're sure you want a redirect, you may consider using the TempData collection to store a message or a key of some kind. The ThankYou controller can then retrieve this value and pass it to the View. This situation is what TempData was made for.
Edit
There's no reason you shouldn't be able to use an enum value as your model type, but if that gives you trouble you should at least be able to create a model type that has an enum property on it.
The strategy of sending the ThankYouType as part of the redirect request would work just fine, if that's what you prefer. The only potential downside is that it would look like this in the URL:
http://domain.com/controller/ThankYou?type=ForgotPasswordDone
I have no real arguments against it. There are lots of options. Use the one that feels best to you.
In my ASP.NET MVC 3 app I have a page where, after its form is submitted I need to change a value from the form. If I do it in the ViewModel it has no effect, I have to do it use ModelState["Field"].Value.
The thing is that I actually have a lot of work I have to do on the ViewModel when the pages loads AND after the POST. So I have a method which receives the ViewModel and do all that work. But, as I said, after the POST, modifying the ViewModel object is useless. Why is that? The method on the controller is returning View(viewModel);. Shouldn't the view use the ViewModel values?
I'd like to understand why is useless to modify the ViewModel and also know if there is some method in the ModelState which "refresh" the fields values throw a ViewModel object.
Here is my controllers code:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(MyViewModel viewModel)
{
try
{
if (ModelState.IsValid)
//Do stuff and redirect
}
catch(Exception ex)
{
//Log and add error to the ModelState
}
someMethodWhichDoesALotOfThingsInTheViewModel(viewModel);
return View(viewModel);
}
I think the rest of the code is unnecessary, if anyone think it would help let me know!
Update
I don't want to lose any errors, not even those of the fields changed in the ViewModel. A good example would be a captcha, if the users makes any error in the form (only with the captcha field or only with any other fields and not with the captcha) I want to show him all his errors and also update the captcha. It makes no sense to render the form with the old captcha value inserted so I want to blank that field.
Update 2
I've opted to put #Value = Model.PropertyInViewModel in the HTML Attributes of each Html.TextBoxFor. But I'd prefer to avoid that nasty work so if someone comes out with any better solution please let me know.
The following will work:
ModelState.Remove("MyProperty");
viewModel.MyProperty = "new value";
You need to remove it from the model state because HTML helpers first look in there when binding their value.
All I could came out with is to add #Value = Model.PropertyInViewModel to each Html.TextBoxFor that is going to be modified in the ViewModel.
Another way, as #Darin Dimitrov said, would be to make my own helpers.
If you decide to remove modelstate values to prevent round-tripping take a look at this RemoveFor() extension method I made to remove ModelState errors without magic strings.
I have an MVC application. Say for example if we have a dropdown Cars makes which when selected posts to itself and gets Car Models. Now on post, the field Car makes loses it value. I am using Form.Get("combo_box_field_name") and if it has a value I am prepopulating the car make dropdown. As lot of controls on my form do this sort of post and i have to prepopulate the fields , I was wondering if this is the right way of doing stuff. I have done loads of asp classic works and also asp.net and looks like mvc is very similar in approach tp classic asp. If someone can guide in in the right way this can be handled it would be greatly appreciated.
I am not looking to use AJAX so pls dont tell me with regard to cascading dropdowns and I have a host of other controls in the form which will need updating on the form being posted to itself before I leave the page to save the data
I don't think I've ever used Form.get in my MVC application.
I post back to the controller which looks like;
[AcceptVerbs(HttpVerbs.Post), Authorize]
public ActionResult MyAction(FormCollection collection)
{
MyClass myClass = new MyClass();
UpdateModel(myClass);
//do stuff with data
return View(myClass);
}
So basically you're letting MVC grab the data from the view for you.
You may need to reload your dropdown lists with this but you can get around that by using JSON to do partial postbacks.
Situation: In some project management software written in asp.net I have a create project page (working fine). I need to add to this the ability to add tasks from a list of templates to this project pre-creation BUT the list of available tasks is dependent on some values sitting in the create form.
My abstract solution is this:
I have a "Create" view and an "Add Tasks" View - both strongly typed to a composite viewModel defined in the controller
My Create method checks which button was used to call it - if the
button was "Add Tasks" it then renders the AddTasks view, passing the model in from the create view, again all in the same controller.
The AddTasks View posts to the Create view with one of two buttons, one loads the view and the other causes an actually DB save.
My Problem is this:
The different views use different properties of the same model, but in passing this model between them, the data is reset (in any case reload or save).
I am guessing this is happening from auto binding of data - though I thought fields not present on the form would not overwrite existing model data passed down.
There is hardly any code in the controller manipulating the model at present - It is only passed from view to view in these cases.
This is the controller code:
// POST: /Project/Create/<viewModel>
[Authorize, AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Exclude = "Id,id")] ProjectViewModel model)
{
if (model.SubmitValue == "Create")
{
try
{
model.Project.Id = Guid.NewGuid();
model.Save(this.User.Identity.Name);
return this.RedirectToAction("Details", new {id = model.Project.Id});
}
catch (Exception e)
{
this.ModelState.AddModelError(e.ToString(), e.ToString());
}
return View(model);
}
if(model.SubmitValue == "AddTasks")
{
return this.View("AddTasks",model);
}
return this.View(model);
}
//POST: /Project/AddTasks/ + model
[Authorize, AcceptVerbs(HttpVerbs.Post)]
public ActionResult AddTasks([Bind(Include = SelectedCarrierTasks")]ProjectViewModel model)
{
return View(model);
}
The Question is: How do I maintain the state of the model across these views until it finally save it?
I would prefer to avoid any hackish (TempData) or JS dependant solutions, but I am not closed to these if they are really the best solution.
Thanks,
Adam Tolley
One simple solution is to persist the ViewModel object in a Session variable and bind the View from this source.I ts certainly not the most elegant solution. Another option, and probably less elegant one is persist this model data in the database, with some temporary/unsaved flag.
The problem is that when you display the add tasks view you're not providing fields for your "Project" object therefore the ModelState loses the data related to the project, you will need to provide this fields to ensure you're not loosing that data.
You don't need to display this fields they can be of type hidden and they will preserve the value. Just make sure that if you will be binding to a view model you will need to name this fields correctly like this Model.Project.Property.
Perhaps I am trying to solve the wrong problem (ala Bruce Eckel). I am going to try to move to a structure that needs this sort of fuzzy boundary less. I don't want to adopt a REST paradigm only to shoe-horn it into a stateful application.
Possibly these controls belong on the same page, and I can use some JQuery goodness to put in a tab pane for easiness on the eyes.
Thanks to those who answered, I found each useful and will try to remember to up-vote them as soon as I have some more rep.
I can't comment on other peoples questions at the moment, but the only real option is the session if you want to persist an objects state during web requests, or serializing it and placing it in a hidden field.
Or a final option would be to change the way your pages work so you can save the object after each request...
If your using nHibernate then you might want look into the Conversations pattern, but this just essentially saves the nHibernate session into the asp.net session anyway...