Sharing Viewdata across Actions in ASP.NET MVC - asp.net-mvc

In mu MVC application page I've a Product page (Controller: ProductController, Action: Index) which lists all the products from DB. Now I've a "Add Product" link which offers a form to fill Product details. On submitting the from AddProduct action is called which calls for a StoredProcedure (modelled in my model class). On succesful addition I use to RedirectAction("Index").Now my StoredProcedure returns me a message indicating the reslult of addition.I want this message to be stored in ViewData and show that on Index page. How can I do that? Any help would be much appreciated.

Use TempData instead of ViewData:
public ActionResult Index()
{
ViewData["message"] = TempData["message"];
return View();
}
public ActionResult AddProduct()
{
TempData["message"] = "product created";
return RedirectToAction("Index");
}
And in the Index view:
<% if (ViewData["message"] != null) { %>
<div><%= Html.Encode((string)ViewData["message"]) %></div>
<% } %>

Related

Best practice for rendering data after posting a form in mvc 4.0

Example:
I have a 'Contact Us' view and controller.
My view renders a contact us form as well as the rest of the page containing postal, telephone and email information.
When the form is submitted I want to render the same data, just minus the contact us form and display a 'message sent' instead.
I have a 'Send' method on the controller and can create a 'Send' view with all the data from the contact us view, minus the contact us form and with the 'message sent' string. But obviously having the code now duplicated in two places is far from ideal.
Is there a better way to do this?
I would suggest you to use Ajax.BeginForm instead of using BeginForm. The reason is you don't need to create another action, Ajax.BeginForm will update the display partial view for you.
Below is an example:
Action
[HttpGet]
public ActionResult Contact()
{
return View(new Contact());
}
[HttpPost]
public ActionResult Contact(Contact contact)
{
if (ModelState.IsValid)
{
//
}
return PartialView("_messagePartialView", contact);
}
View
#model Demo.Models.Contact
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script src="~/Scripts/jquery.unobtrusive-ajax.js"></script>
<div id="result">
#using (Ajax.BeginForm(new AjaxOptions { UpdateTargetId = "result" }))
{
#Html.EditorFor(x => x.Email)
<input type="submit" value="OK" />
}
</div>
Partial View: _messagePartialView
#model Demo.Models.Contact
<h1>
#Model.Email
</h1>
I will assume you have a Contact Model that you use to get the data from user.
And I assume you have the Send Method as follows:
[HttpPost]
public ActionResult Send(Contact contact){
// process your model. ie : send email etc.
TempData["contactData"] = contact;
return RedirectToAction("Sent");
}
public Actionresult Sent(){
return View();
}
In sent view you can use TempData and access Contact model properties.

TextBox Value gets blank on Submit

I created a application in MVC2 by choosing (ASP.Net MVC 2 Web Application). This provided some Home/About Controllers/Models/Views.
I additionally created a Model with the Name of Index like below...
namespace MvcApplication1.Models
{
public class Index
{
[DataType(DataType.Text)]
public String Name { get; set; }
}
}
Following is my Index View
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
Index
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<% using (Html.BeginForm())
{%>
<%:Html.TextBoxFor(x=> x.Name) %>
<input type="submit" name="Click here" />
<%} %>
</asp:Content>
Following is my Controller
[HttpPost]
public ActionResult Index(Index Model)
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View();
}
Question
When I keep the Index controller like below. and If I click the submit button. This is clearing the TextBox COntrols. Like below
public ActionResult Index()
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View();
}
TextBox will not be cleared in case in incorporating the Model as Parameter in the Action method...
What's the reason for this behaviour ?
MVC doesn't maintain state between postbacks like WebForms does.
Fields are repopulated from values in the ModelState which are only added there if they are seen by the modelbinder on postback (and potentially only if there is a validation error?). Honestly i would almost prefer if it didn't do it automatically. However if you postback an invalid value (eg a string to an integer field) you need an somewhere which can store the invalid value so it can be repopulated along with a validation error.
Other than that automatic method, you need to manually pass the model back to the view for it to be populated
[HttpPost]
public ActionResult Index(Index Model)
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View(Model);
}
Your controller should look like this for the user input to persit in the view after the submit button is clicked '
public ActionResult Index( )
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
var model = new Index();
return View( model );
}
[HttpPost]
public ActionResult Index(Index model )
{
return View(model);
}

ASP.NET Error: "Unable to cast object of type '<>f__AnonymousType1`2"

I very new to .NET and Entity Framework, and I have a problem with my code (below). I am getting the following error:
Unable to cast object of type '<>f__AnonymousType1`2[
SamWinInterface.Models.tbl_interface_category,
SamWinInterface.Models.tbl_interface_menu]' to type
'SamWinInterface.Models.tbl_interface_menu'.
This is my code:
public ActionResult Index(int id=-1)
{
ViewBag.Menus = from menu in _db.tbl_interface_menu
join cat in _db.tbl_interface_category on
menu.fld_category_id equals cat.id where
cat.fld_customer_id == id select new { cat, menu };
return View();
}
I'm trying to get menus depending on which category is chosen.
Something like:
<% foreach (tbl_interface_menu m in (IEnumerable)ViewBag.Menus)
{ %>
<%= m.fld_section2_title %>
<% } %>
but I'm getting the above error. How can I get the menus?
You cannot pass anonymous objects to views. This doesn't work because anonymous types are emitted as internal. And because ASP.NET views are compiled into a separate assembly at runtime they cannot access those times because they reside in a different assembly. This basically means that an anonymous object that you have defined in your controller action cannot be accessed in your view.
So as always in an ASP.NET MVC application start by defining view a model:
public class MyViewModel
{
public Category Category { get; set; }
public Menu Menu { get; set; }
}
then have your controller action fill this view model and pass it to the view:
public ActionResult Index(int id=-1)
{
var model =
from menu in _db.tbl_interface_menu
join cat in _db.tbl_interface_category
on menu.fld_category_id equals cat.id
where cat.fld_customer_id == id
select new MyViewModel { Category = cat, Menu = menu };
return View(model);
}
and finally have a strongly typed view:
<%# Page
Language="C#"
MasterPageFile="~/Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<IEnumerable<AppName.Models.MyViewModel>>"
%>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<% foreach (var item in Model) { %>
<%= item.Menu.fld_section2_title %>
<% } %>
</asp:Content>
As Darin said, you cannot pass anonymous types to views, but you could convert them to Expando objects, and that would prevent you from having to define viewmodels.
Personally I would probably just define viewmodels, but this option is handy in a pinch.

MVC 3 StackOverflowException w/ #Html.Action()

I've looked over a bunch of other reports of this, but mine seems to be behaving a bit differently. I am returning PartialViewResults for my child actions, so that's not the source of the recursion. Here's a dumbed down version of what I have.
// The Controller
[ChildActionOnly]
public ActionResult _EditBillingInfo()
{
// Generate model
return PartialView(model);
}
[HttpPost]
public ActionResult _EditBillingInfo(EditBillingInfoViewModel model)
{
// Update billing informatoin
var profileModel = new EditProfileViewModel()
{
PartialToLoad = "_EditBillingInfo"
};
return View("EditProfile", profileModel);
}
[ChildActionOnly]
public ActionResult _EditUserInfo()
{
// Generate model
return PartialView(model);
}
[HttpPost]
public ActionResult _EditUserInfo(EditUserInfoViewModel model)
{
// Update user informatoin
var profileModel = new EditProfileViewModel()
{
PartialToLoad = "_EditUserInfo"
};
return View("EditProfile", profileModel);
}
public ActionResult EditProfile(EditProfileViewModel model)
{
if (String.IsNullOrEmpty(model.PartialToLoad))
{
model.PartialToLoad = "_EditUserInfo";
}
return View(model);
}
// EditProfile View
#model UPLEX.Web.ViewModels.EditProfileViewModel
#{
ViewBag.Title = "Edit Profile";
Layout = "~/Views/Shared/_LoggedInLayout.cshtml";
}
<div>
<h2>Edit Profile</h2>
<ul>
<li class="up one"><span>#Ajax.ActionLink("Account Information", "_EditUserInfo",
new AjaxOptions { UpdateTargetId = "EditProfileDiv", LoadingElementId = "LoadingImage" })</span></li>
<li class="up two"><span>#Ajax.ActionLink("Billing Information", "_EditBillingInfo",
new AjaxOptions { UpdateTargetId = "EditProfileDiv", LoadingElementId = "LoadingImage" })</span></li>
</ul>
<img alt="Loading Image" id="LoadingImage" style="display: none;" src="../../Content/Images/Misc/ajax-loader.gif" />
<div id="EditProfileDiv">
#Html.Action(Model.PartialToLoad)
</div>
</div>
The partial views are both forms for updating either the user information or billing information.
I debugged through this and found what is happening, but cannot figure out why. When a user browses to EditProfile, it load up with the _EditUserInfo partial and the form is there for editing. When you change some info and submit the form it hangs and you get a StackOverflowException in the EditProfile view on the call to #Html.Action(). What happens is on the initial visit to EditProfile, the #Html.Action calls the HttpGet version of _EditUserInfo. You make some changes to the user info and click submit. Once the information is updated the EditProfile view is returned again, but this time #Html.Action calls the HttpPost version of _EditUserInfo which updates the user information again, returns the EditProfile view again and the #Html.Action calls the HttpPost version of _EditUserInfo... You get where this is going. Why after form submission does it call the post version and not the get version like it did for the initial visit to EditProfile?
Thanks for any help!
I might be getting this a bit wrong, it's been a long day so, but in EditProfile you set PartialToLoad (if it's empty) to "_EditUserInfo", then in _EditUserInfo you set it again to _EditUserInfo, won't this create a loop that behaves as what you are experiencing?

How to debug RedirectToAction

I am trying to debug a problem wherein RedirectToAction appears to be working properly (i.e., the redirected-to Action is executed) but the specified View is never rendered on the client side and the url is not rewritten. My question is really where can I be looking to debug this? Here's the code:
Form:
<%using (Html.BeginForm("Save", "Items", FormMethod.Post, new {id="ItemForm"})) {%>
<%=Html.AntiForgeryToken()%>
..form contents elided...
<%= Html.Button("btnSave", ViewData.Model.SaveMethod, HtmlButtonType.Submit) %>
<%= Html.Button("btnCancel", "Cancel", HtmlButtonType.Button,
"window.location.href = '" + Html.BuildUrlFromExpressionForAreas<ItemsController>(c => c.Index()) + "';") %>
<% } %>
Controller:
[ValidateAntiForgeryToken]
[Transaction]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Save(Item item)
{
if (item.Id == Guid.Empty)
return Create(item);
return Edit(item);
}
[ValidateAntiForgeryToken]
[Transaction]
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(Item item)
{
if (ViewData.ModelState.IsValid)
{
ActionConfirmation updateConfirmation =
_itemManagementService.UpdateWith(item, item.Id);
if (updateConfirmation.WasSuccessful)
{
TempData[ControllerEnums.GlobalViewDataProperty.PageMessage.ToString()] =
updateConfirmation.Message;
return RedirectToAction("Index");
}
}
ItemFormViewModel viewModel =
_itemManagementService.CreateFormViewModelFor(item, _indicatorManagementService, _strategyManagementService);
return View(viewModel);
}
[Transaction]
public ActionResult Index()
{
ItemsFormViewModel viewModel = _itemManagementService.CreateListFormViewModel(_indicatorManagementService,
_strategyManagementService);
return View(viewModel);
}
I am using a custom model binder, but model.IsValid returns true and the changes to the model save successfully. I am at a loss as to where to look to track this down.
Thanks for looking.
As always, as soon as I post, I finally figure it out. Had some left over jquery from a previous approach that was hijacking the submit.

Resources