passing a view-model with a controller around multiple Views - c#

So I have 3 views, a controller and one model. (just an example) The first view sets the user first name and last name. Which gets posted back to the controller, and I can see the data in the view-model. The controller then calls the second view sets the email (I can call the data from view 1). The third view shows all of the data (the original stuff from view 1 is no longer there)
#Html.DisplayFor(m => m.FirstName)
#Html.DisplayFor(m => m.LastName)
#Html.DisplayFor(m => m.Email)
Do you think creating a static singleton model would work in the controller? or should I be using TempData
EDIT: sorry I forgot about my controller
Would my GET methods in my controller need a parameter?
[HttpGet]
public virtual ActionResult SignUp1(model m)
{
return View(m)
}

you can call into another view using #Html.Partial("view name", object) if you want to preform logic you can call another controller action with #Html.Action("action", "controller", object). then it's just like any other controller action. typically calling actions from a view are decorated with [ChildActionOnly]

Static is a bad idea for web pages, because it is not inherently thread-safe (see here). That means that you'll get really bizarre behavior if you have two or more people using it at once.
I'm not sure why you're even considering doing it this way - is there some specific reason you're thinking about it? The proper way to do it would be to post the model back to each controller action from each view, populating more data each time. Alternatively, you could post back to the same action, and then return the appropriate view based on which fields are missing from the model (and the display if none are).

Related

MVC ViewModel that lives in Session - how to update?

I have done that the "standard" way:
public ActionResult Respondent()
{
return View(Session["Respondent"]); //passing the model
}
[HttpPost]
public ActionResult Respondent(Respondent resp)
{
repository.UpdateRespondent(Respondent resp);
Session["Respondent"] = respondent; //put back into session
return View(respondent); //redraw view, passing in respondent
}
And it works. I am passing the respondent model only for MVC to collect FORM values automatically, in the POST action, where inside the view I have these for all the properties:
#using (Html.BeginForm())
{
#Html.LabelFor(model => model.FirstName)
#Html.EditorFor(model => model.FirstName)
#Html.ValidationMessageFor(model => model.FirstName)
// and so on...
}
My question is - if I am already using a Session object (that lives in Session),
Is there any way I can use the Session object as a Model inside the view, so that HttpPost works including all the validation. How then, the values will get collected and put back into the Session?
Thank you.
If I understand your question, best practices preaching aside, you CAN pass a session object as a Model.
2 Caveats:
Session objects should be cast when passed
return View((RespondentObject)Session["Respondent"])
On the View, remember to bind to object type
#model perseus.Models.RespondentObject
I recommend you read from and write to Session in your Controllers.
You pointed out that you will be using multiple partials to create your form. You have 2 choice:
Create a Model to be received by Action which includes all objects.
Pass every parameter individually to your Action.
You asked why it is bad form to use object:
Because they're untypes
Because Sessions are flaky (unreliable)
That said, you know your app and architecture best. You have to make your decisions and support them. As much as I'm a purist, I believe best practices are a guide not a religion. Circumstances vary.

ViewBag as a holder of session models

This is more of a high-level question than anything else.
I have a MVC project which, among other things, manages users.
I am currently sticking to a more strict approach that on my view pages I only use a model which is declared, meaning that I have a:
#model MVCApp.Models.SomeModel
At the top of each View.cshtml
There are some pages that require more than 1 model. At this point I consolidate the 2 models into 1, and send it to the view as one model.
Here comes the tricky part. Let's say I have some model which holds the user data. That user data is stored in the session cookies (typical Forms Authentication). It seems weird to me that I now have to wrap each and every model I use with my own model that holds both the User Model and the model which I want to use inside that View.
The question that I ask myself is why not pass the User Model to the ViewBag and use it inside the View. Why is that considered to be bad practice? It allows me to attach that model to every page without having to ultimately duplicate all my models.
I'd love to get some guidance. I might be looking at this the wrong way. Any help will be much obliged.
Thanks,
There are a couple of reasons why ViewBag should be avoided:
ViewBag is weakly typed
You don't get Intellisense in the view
Your view is a mixture of ViewBag and a view model and your view gets information from different places instead of centralizing everything into a strongly typed view model
ViewBag doesn't work with strongly typed helpers and expressions because dynamic types cannot be used with extension methods (that's not a limitation of ASP.NET MVC, it's .NET => you cannot dispatch extension methods on dynamic types)
Due to this weak typing nature of ViewBag you will have to cast in your views turning them into spaghetti code because HTML helpers expect specific types to be passed as arguments
... this list goes on (I really don't have time to fill it but it is very large list)
So now that we know that ViewBag is bad and shouldn't be used there are different ways you could solve this requirement by using view models.
One possibility is to use the Html.Action helper which allows you to insert some portion of HTML in the view by going through a full Controller/Model/View lifecycle. This way you could have a commonly used widget without interfering with your main view model.
Another possibility is to have a base view model which will contain a property representing user details and which will be populated by a custom global action filter that could executed everytime a controller action has finished executing and returned a view result. The action filter could intercept this view result, read the authentication cookie information and set the view model property. This assumes that all your view models derive from the common base view model. This obviously makes sense if you need to display this user information on each page.
For example:
public class UserInfoInjectorAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var result = filterContext.Result as ViewResultBase;
if (result == null)
{
// the controller action didn't return any view result => no need to continue
return;
}
var model = result.Model as BaseViewModel;
if (model == null)
{
// the controller action didn't pass a model or the model passed to the view
// doesn't derive from the common base view model that will contain
// the user info property => no need to continbue any further
return;
}
model.UserInfo = ... go ahead and read the forms authentication cookie
userData portion and extract the information
you are looking for
}
}
Now all that's left is to register this action filter as a global action filter and it will be applied on all controller actions.
Now if all your view models derive from this BaseViewModel you will know that once you arrive in the view the UserInfo property will be populated with the relevant information without polluting all your controller actions with code that does the fetching of this property. And still you get strong typing in the view => no ViewBag (youpeeee).
Of course depending on your specific scenario there might be other ways to achieve that (obviously not involving any ViewBag).
You can an ActionFilterAttribute and in those Actions that load views which needs specific ViewBag items, you initialize them.
I don't recommend it, as it will be harder to mantains, but will be a generic approach which may solve your problems.

ASP.net MVC - One ViewModel per View or per Action?

Is it a better idea to have a single ViewModel per view or one per controller action?
Example:
public ProjectController : Controller
{
public ActionResult Edit(int id)
{
var project = ...;
return View(new ProjectEditViewModel(project));
}
[HttpPost]
public ActionResult Edit(ProjectEditViewModel model)
{
}
**OR**
[HttpPost]
public ActionResult Edit(Project model)
{
}
[HttpPost]
public ActionResult Edit(ProjectEditPostViewModel model)
{
}
}
Here are the three options, which is best?
Use the same ViewModel for my POST/GET actions.
Use a ViewModel for my GET action and my domain model for my POST action.
Use a different ViewModel for GET and a different ViewModel for POST.
Using a different view model for the GET and POST actions is the best and most flexible design. But using the same view model for the GET and POST actions also works in 90% of the cases and it is fine a good design. So if using the same view model works in your scenario don't hesitate to reuse it like this.
In the case where different view models are used for the GET and POST actions there is still some relation between those classes: inheritance or composition.
The correct answer
Neither. There's no silver bullet and shouldn't be.
The correct answer is therefore: use as many view models as your user interface process demands. That's regardless of views or controller actions.
Sometimes an action demands a view, other a view. But don't follow some strict guidelines that would hinder your development. View models will come naturally as you develop your application. And should. Otherwise you may end up with unreasonable views that are based on some guideline you've set in stone.
This is actually a similar answer as #DarinDimitrov's, but with a direct conclusion.
Use different model to receive input parameters in Post action (I don't even call it ViewModel in that case) than to pass output parameters to the view.
That way you can customize exactly what input parameters do you accept.
I follow this approach for basic forms:
One view model for the GET
One view model for the POST
The GET model inherits the POST model.
I will often pass a domain object to the GET model's constructor, and do 2 things with it:
Populate the POST model properties with data from the domain object.
Encapsulate the domain object as a local variable in the GET model. I use this for displaying some (read-only) data from the domain object. Saves a bit of effort. Some people will tell you not to do this.

MVC2 - Possible to invoke action from a DIFFERENT controller?

In a controller, is it possible to return the view of an action from ANOTHER controller? The other option is to return a partial view, which uses Html.Action(...) to return the view from the other controller, but I was wondering if there's anything cleaner. Thanks.
If it's just the view you want to reuse, you can pass in the path to the view. For example:
public ActionResult MyAction()
{
// do your model magic here
return View( "~/Views/OtherController/View.aspx", model );
}
Or you can move the view to Views/Shared like Kyle already suggested.
Yes, if that view is a Shared view. Place the view in the Views/Shared folder in your MVC Project, then both controllers will be able to return it.
If you want to invoke an action on another controller, you can use Controller.RedirectToAction() and pass in the action and controller name.
However, this adds an additional server round trip. If you want to avoid that, you can use the TransferResult class shown here:
How to simulate Server.Transfer in ASP.NET MVC?
I ended up using my original solution, which was having a shared view that invokes an action. It was much less code than I needed. Thanks.
Here's a strategy I use to invoke another action without having to create a special view just for that purpose:
Create a shared view which takes a model that defines an Action, Controller, and RouteValues, and whose sole responsibility is to call RenderAction with the values on that model.
Next create a helper method on your base controller class that takes an Action, Controller, and RouteValues as parameters, and returns the ViewResult for this shared view. That way, you can reuse this helper method and shared view on all your controllers any time you want to render some other action from another action's context.
Of course, if it's just the view and not the action that you want to invoke, Marnix's answer is correct.

How to handle shared data between the controller and the view in ASP.NET MVC?

We have a model (say, List<string>). The function that builds the list is non-deterministic and the output is needed to be referenced in both controller and the view during the lifetime of the request. Since it's per-request, it cannot be static or singleton.
It's a common structure and it can be referenced from any view or controller.
Since we can't access controller from the view (by principle, and we agree), we cannot keep it in the controller. We're currently keeping it in the ViewData dictionary and initialize it in the controller, or the view (if the controller didn't need it).
We think that using ViewData for this purpose may not be ideal since it's not created to be consumed by a controller in the first place. Is there a better way to share common per-request data between Controller and the View? If not we'll stick with ViewData.
There is HttpContext.Items dictionary but I'm not sure if it fits to this purpose.
the output is needed to be referenced in both controller and the view during the lifetime of the request
The way MVC works, the Action code in the Controller is executed, and the resulting data is passed to the view engine that draws the page using the info you passed either with the call to View(data) or in the ViewData dictionary.
I don't know what you are trying to do, but it sounds like it's more a problem of a bad approach than a technical one (I might be wrong, though).
Could you explain why you need the controller while the View is rendered? If you need any logic associated with the List (to process it or do anything with it) I would just create a new class that extends List<T>, add the logic to that class instead of the controller, and pass an object of that class to the View, either using View() or ViewData[].
What is exact thing you're trying to do?
Seems like you just asking about the way to pass some data from the Controller to the View which is rather trivial task. Just use ViewData, yes, or ViewBag in MVC3 case or use ViewModels.
Or there is somewhat special case? What does "referencing from Controller and from View" mean? Where the data is coming from? Usually the case is that Controller prepares data for the View and passes it as an ActionResult (or better, as a ViewModel). View should never take some data on its own bypassing the controller.
Controller action should always be called be first. If you have multiple controllers calling the same view/partial view then you should be refactoring the code to one method and call that.
ViewData is the solution to do this, if your really wanting "once access" type information then maybe TempData but ViewData is designed for this.

Categories