MVC Transfer Data Between Views - model-view-controller

I just started to learn MVC and am trying to understand how it works.
I don't want to send users to different views for all edit, insert and list operations.
In my sample application a View contains a list of items and below the list there is a form (for inserting new items) with action "{Controller}/Create" but there is no Create View.
When a user inserts a new item it posts to the Create action with httpverb post and creates the item and returns back to the List action with RedirectToAction method.
But I can not show any message(error, information etc) to the user in this style because I can not pass data between Create action and List action. How can I do that?

You need to use Post Redirect Get PRG pattern.
Please read this Use PRG Pattern for Data Modification section in this blog post by Kazi Manzur Rashid.
http://weblogs.asp.net/rashid/archive/2009/04/01/asp-net-mvc-best-practices-part-1.aspx
This approach uses TempData to maintain ModelState data between redirects.
[HttpPost, ValidateAntiForgeryToken, ExportModelStateToTempData]
public ActionResult Create(FormCollection form)
{
Product p = new Product();
if (TryUpdateModel<IProductModel>(p))
{
productRepository.CreateProduct( p );
}
else
{
// add additional validation messages as needed
ModelState.AddModelError("_generic", "Error Msg");
}
return RedirectToAction("Index");
}
And here is your Index action method.
[ImportModelStateFromTempData]
public ActionResult Index()
{
IList<Product> products = productRepository.GetAll();
return View("Index", products);
}
And here is your Index view.
<%# Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<IList<Product>>" %>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Index
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>Products</h2>
<% foreach (var p in Model) { %>
<div><%= Html.Encode( p.ProductName ) %></div>
<% } %>
<%= Html.ValidationSummary("Please correct the errors", new { id = "valSumCreateForm" }) %>
<% using (Html.BeginForm("Create", "Product")) { %>
Product Name: <%= Html.TextBox("ProductName") %>
<%= Html.AntiForgeryToken() %>
<% ViewContext.FormContext.ValidationSummaryId = "valSumCreateForm"; %>
<% } %>
</asp:Content>
The ImportModelStateFromTempData
and ExportModelStateToTempData
attributes helps transfer model
state errors between redirects. This
<% ViewContext.FormContext.ValidationSummaryId = "valSumCreateForm"; %> associates the MVC Form with its corresponding Validation Summary.
You can check another answer by me on this here as well.
ViewModel with SelectList binding in ASP.NET MVC2
Let me know if you have any question.
-Soe

Most MVC frameworks have the ability to temporarily store a small bit of data just through the next request, for just this purpose. In ASP.NET MVC its called TempData, in Rails it's called :flash, etc.

This article explains how to use TempData:
One of the more annoying things to
deal with in Web programming is errors
on a form. More specifically, you want
to display error messages, but you
want to keep the previously entered
data. We've all had the experience of
making a mistake on a form that has 35
fields, only to be presented with a
bunch of error messages and a new,
blank form. The MVC Framework offers TempData as a place to store the previously entered information so that the form can be repopulated. This is
something that ViewState actually made
very easy in Web Forms, since saving
the contents of controls was pretty
much automatic. ... TempData is a dictionary,
much like the untyped ViewData.
However, the contents of TempData only
live for a single request and then
they're deleted.

Related

Updating result from ajax calls in partial views in asp.net mvc2

I have a ASP.Net MVC 2 partial view like the one below,
FORM 1:
<div id="d1">
<% using (Ajax.BeginForm("ManageSources", "Sources",
saveAjaxOptions))
{ %>
... all this form's html markup goes here
<div id="src_Parameters"></div>
<% } %>
</div>
Form 2
<% using (Ajax.BeginForm("FetchParameters", "Sources",
fetchAjaxOptions))
{ %>
hidden fields to send values to the action method go here
.. button to post this form
<% } %>
Now, in the fetchAjaxOptions, i have given the target div to be src_Parameters, which resides inside the form1, when i post the second form, i am being returned a partial view as the only view page, instead of populating the results in the src_Parameters div.
How do i accomplish this. Actually the results of the FetchParameters ajax call should be able to be posted for the ManageSources ajax call.
Where is the problem or will nesting the forms workout since this is using ajax forms.. Kindly suggest me the right procedure to do this task.
You haven't posted your server side code, but I suspect you have forgotten to have set the return type to be a partial view.
public PartialViewResult FetchParameters()
{
//do some stuff
return PartialView(“_ViewName”, viewModel)
}
It could also be that you fotgot to add a reference to the Microsoft Ajax
<script src="../../Scripts/MicrosoftMvcAjax.js" type="text/javascript"></script>

What is the best way to access a model from a view when the model is optional?

I have a single view that is used by two Controller Actions.
My Controller:
AccountController {
...
ActionResult LogOn() {
return View()
}
[HttpPost]
ActionResult LogOn(LogOnModel model) {
return View(model)
}
}
My View:
<% if (!String.IsNullOrEmpty(Model.UserName)) { %>
<div class="username"><% Response.Write(Model.UserName); %></div>
<% } %>
Of course, the problem with the above code is that I get an error "Object Reference not set to an instance of an object" on the line that references Model.UserName.
What is the best way to my view to support both Actions? I can replace the HttpGet LogOn method with:
ActionResult LogOn() {
result View(new LogOnModel());
}
That works, but it doesn't seem very elegant and I'm sure an empty model will break some more complex models that I will need to create later on. Is there a better way? Thanks!
What is the best way to my view to support both Actions?
If a view is strongly typed to a view model (which is what absolutely all your views should be) you should make sure that all your controller actions serving those views (which is pretty much all your controller actions in your application) are passing a view model to those views.
Which is pretty much bla-bla which translated into source code pretty basically means this:
ActionResult LogOn() {
return View(new LogOnModel())
}
That works, but it doesn't seem very elegant and I'm sure an empty
model will break some more complex models that I will need to create
later on.
That's the correct way. Don't worry. You ain't gonna break anything. It's by not providing a view model to your views that you are breaking stuff.
Also as a side note you should absolutely never use Response.Write in a view. So instead of:
<% Response.Write(Model.UserName); %>
you totally want:
<%= Html.DisplayFor(x => x.UserName) %>
or:
<%: Model.UserName %>
but personally I prefer the DisplayFor helper since it will take care of any metadata.
You can either return a different view or as you have done, return a new model.
As noted by other commenters, your Controller should support the strong-typeness of your view. But if you insist on proceeding with the anti-pattern. Replace this:
<% if (!String.IsNullOrEmpty(Model.UserName)) { %>
<div class="username"><% Response.Write(Model.UserName); %></div>
<% } %>
with this:
<% if (Model!= null && !String.IsNullOrEmpty(Model.UserName)) { %>
<div class="username"><% Response.Write(Model.UserName); %></div>
<% } %>

Is the sentence formatting I do here view logic or not?

I have a page containing a partial view with a list and a partial view with three filter options. If the list is empty, I show a special partial view instead of the list with some tips for the user. The tip will instruct the user to widen the filter options that are not already set to it's most generic form. I could just present a bulleted list of all the options the user could change, doing something like this (LocalizedText is generated from a resource file):
<%# Import Namespace="Resources" %>
<%# Import Namespace="System.Web.Mvc" %>
<%# Import Namespace="MyProject.Models" %>
<%# Control Language="C#" Inherits="ViewUserControl<EmptyResultViewModel>" %>
<div class="warning">
<ul>
<% if (!string.IsNullOrEmpty(Model.SelectedCustomerNumber)) %>
<% { %>
<li><%: LocalizedText.TipCustomerNumber %> </li>
<% } %>
<% if (Model.SelectedPeriodOption != PeriodOption.EntirePeriod) %>
<% { %>
<li><%: LocalizedText.TipPeriod %> </li>
<% } %>
<% if (Model.SelectedFilterOption != FilterOption.None) %>
<% { %>
<li><%: LocalizedText.TipFilter %> </li>
<% } %>
</ul>
</div>
I think this would be considered OK view logic. However, I want it to be one grammatically correct sentence. I now have four extra tip resources:"
Tip0 = "No results found";
Tip1 = "No results found, consider choosing {0}.";
Tip2 = "No results found, consider choosing {0} or {1}.";
Tip3 = "No results found, consider choosing {0}, {1} or {2}.";
TipCustomerNumber = "another customer number";
TipPeriod = "a different period";
TipFilter = "a more generic filter";
And my view becomes this:
<%# Import Namespace="Resources" %>
<%# Import Namespace="System.Web.Mvc" %>
<%# Import Namespace="MyProject.Models" %>
<%# Control Language="C#" Inherits="ViewUserControl<EmptyResultViewModel>" %>
<% var tips = new List<string>();
// Add the tips of the filter items that are not set to the most general search criteria to a list.
if (!string.IsNullOrEmpty(Model.SelectedCustomerNumber))
{
tips.Add(LocalizedText.TipCustomerNumber);
}
if (Model.SelectedPeriodOption != PeriodOption.EntirePeriod)
{
tips.Add(LocalizedText.TipPeriod);
}
if (Model.SelectedFilterOption != FilterOption.None)
{
tips.Add(LocalizedText.TipFilter);
}
// Depending on how many tips there are, choose the correct tip format.
string message;
switch (tips.Count)
{
case 0:
message = LocalizedText.Tip0;
break;
case 1:
message = string.Format(LocalizedText.Tip1, tips[0]);
break;
case 2:
message = string.Format(LocalizedText.Tip2, tips[0], tips[1]);
break;
case 3:
message = string.Format(LocalizedText.Tip3, tips[0], tips[1], tips[2]);
break;
default:
message = LocalizedText.Tip0;
break;
} %>
<div class="warning">
<%: message %>
</div>
Now I see so much code in my view compared to only a tiny bit of html. This made me worry about whether I'm putting too much logic in my view. However, in a sense all the logic is around formatting the message.
I just wanted to check whether I'm doing this in the right way or not.
Thanks in advance.
What stops you from passing a pair, [Model, Message] as an actual model to view? Or even create extra wrapper class to pass message together with model. Consider:
class ModelWithMessage
{
public object Model { get; set; }
public string Message { get; set; }
}
as your view model.
This way you can do all the formatting logic in controller and view can remain as simple as it gets.
My point being, all the logic you do in your view currently is view-independant - it rings a bell it should not be there.
Edit in regards to discussion in comments:
Before putting any logic in view, it's good to ask yourself a question: can this be done in my controller/model? Often the answer is yes. Having properly designed model/controller will result in very simple and lighweight views, purely for content display, which is what view should do - accepts necessary information from the controller and renders a user interface to display that (wiki).
Minimalistic view will benefit you later, for example when you realize you need to display same content in other places aswell, perhaps using new view. In your original approach, you'll have to copy logic from the old view into the new view. Copying stuff from one file to another might not sound like a big deal, but let's remember about DRY principle. What if you have 15 views? Do you want to have same formatting logic in all of them? What if your manager comes and tells you formatting has changed? Do you want to update 15 view files, or 1 controller...?
When approaching such problems, it's worth to take a minute and answer yourself questions like those.

Passing values between View and Controller in MVC 2

I'm constantly confused about how to pass values between Views and Controllers in MVC. I know I can set ViewData in the Controller and use that in the View, but what about the other way around?
What I have found is I can use a hidden field and then access it through Request.Form["name"] like this:
<% using (Html.BeginForm("Upload", "Customers", FormMethod.Post, new { enctype = "multipart/form-data" }))
{%>
<br />
<input id="currentDir" type="hidden" name="currentDir" value="" />
<input type="file" name="fileTextbox" id="fileTextbox" />
<br />
<br />
<input type="submit" value="Send" />
<% } %>
What complicates it even more is that the value originally comes from a jquery script, so that's why the input field was the only way I could think of. But it still feels wrong... Maybe it isn't but I'd basically like to know if there are other more "proper" established ways to pass values between the View and Controller (both ways). Should one use querystrings instead? If so, how would they look in the html.beginform htmlhelper?
Also, what I'm trying to do here is enable upload possibilities for my application. And I'm trying to make the whole application as "Ajaxy" as possible. But this form will make a complete post. Is there another way to do this and not have to reload the entire page for this upload?
Let's ignore the "AJAX-y" aspects for a moment (because that's a different issue) and just look at passing data between views and controllers. I would first recommend that you check out the NerdDinner Tutorial which provides some good insights into how MVC works and how you use some of the features of MVC.
To address your specific question of how data is passed from View to Controller and back, there are a few ways to do this. However, the one that tends to make sense to most people is the idea of using strongly-typed views.
Let's say you have a model called Person. For now, don't worry about how we store Person data - we just have a Person class in the Models folder inside your MVC project.
public class Person {
public string FirstName;
public string LastName;
public Person() {
FirstName = "John";
LastName = "Doe";
}
}
When we want to display data about Person in a View, we make a request to a specific controller. In this case (and for clarity) we'll call this controller the MainController. This will go in the Controllers folder and will be called MainController. Let's call the Action (an action is really just a specialized method) we want to get data from Index. Due to how ASP.NET MVC routing works, the path to our server will be: http://localhost/Main/Index. Notice the Controller (minus the "Controller" name), and the Action make up the path. (The first part is your server name, of course.)
Let's look at your controller - I'm going to keep it very simple for now:
public class MainController : Controller {
public ActionResult Index() {
Person person = new Person();
return View(person);
}
}
What we have going on inside the Index Action is that it is returning a View (which, by default, has the same name as the Action) and a model to correspond with that view. Now, we have to create our view.
The important part here is that you want to strongly-type the model that is being returned in the controller to your view. You do that with this line (which is first in your aspx file).
<%# Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewUserControl<Project.Namespace.Person>" %>
Notice the "Inherits" attribute and notice that your Person model makes up that attribute.
Now, just code the rest of your view as normal. Let's say we want to display the current Person name, and allow someone to change the name. The page would look like this (I'm not making this pretty):
<% using (Html.BeginForm()) { %>
<%: Html.LabelFor(model => model.FirstName) %>
<%: Html.TextBoxFor(model => model.FirstName) %>
<%: Html.LabelFor(model => model.LastName) %>
<%: Html.TextBoxFor(model => model.LastName) %>
<input type="submit" value="Submit" name="submitButton" />
<% } %>
This is the important part about getting data back and forth between Controllers and Views. What we are doing here is that we are taking your strongly-typed view (which is typed with the Person class) and using helper methods (like LabelFor and TextBoxFor) to tie the model together with its data and, ultimately, together with the actions contained in the controller (which we have to finish developing here in one moment).
So, you can now see the data. But, if a user changes the name and clicks submit - we want the page to display the new name. This means we need to add one more action to MainController - the one that receives the data.
[HttpPost]
public ActionResult Index(Person person) {
// Do whatever you want with the Person model. Update a database, or whatever.
return View(person);
}
This action looks very similar to the other action we just developed. However, this one takes a person object (from the form that is being submitted) and it gives the controller an opportunity to do whatever needs to be done with that object. Once this is done, you can choose to redirect to a different page, redisplay the page (useful if there are errors), or do any number of other things.
Again, this is ALL covered (and much more) in the NerdDinner Tutorial. I highly recommend you read and follow through that.
As for the AJAX-y aspects you discussed, the premise is still the same (although there is a little bit of JavaScript/jQuery work that goes on in there). I won't go into it now, but the basics are also covered in the NerdDinner tutorial.
I hope this gets you started. I remember being a bit confused too when I first started working with web technologies, so I hope this helps you out!

MVC Multiple ViewPage items in aspx required

I need to pass in 2 objects to a page to access Model.NewsItems and Model.Links
(the first is a class of news item objects with heading, content etc and the Links are a set of strings for hyperlink depending on whether the system is up or down.
This is the page declaration
<%# Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<Tolling.Models.NewsItems>" %>
If I refer to Model.Items - I am fine.
However, if I refer to Model.HyperLink1, I am not.
How can you pass in multiple objects into the page?
I have tried importing both namespaces without success - i.e.
<%# Import Namespace="Tolling.Models.News" %>
<%# Import Namespace="Tolling.Models.HyperLinks" %>
Create a ViewModel class that contains both of your model collections and then pass that too the view:
Sample Controller:
var myNewModel = new MyNewModel()
{
NewsItems = new List<NewItem>(),
HyperLinks = new List<HyperLink>()
}
return View(myNewModel);
View page declaration:
<%# Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<MyNewModel>" %>
Then you can access them in your view with your new ViewModels properties:
<%= Model.NewsItems %>
<%= Model.Hyperlinks %>
You can pass extra data into the View by using ViewData, TempData, Session or Cache. I'd suggest you use ViewData. As described by MSDN:
Gets or sets a dictionary that
contains data to pass between the
controller and the view.

Resources