I have an MVC app and I've run into a problem with modern multitab browsers.
I have a screen that creates a model and stores it in this.TempData["MyViewModel"]
I then open the same screen in a 2nd tab and as it calls the same controller methods this.TempData["MyViewModel"] is once again set.
If I then go back to Tab 1 and refresh the page, I am presented with the data I entered in Tab 2.
Is there a way to store data in TempData uniquely per browser tab ?
I have toyed with the idea of creating a unique TempData key and persist it between requests using either the querystring or a hidden field but this seems messy.
Any ideas / advice would be most welcome. :o)
What I have so far is a concept of a 'ProcessID'.
In my first partial view I have a form with a hidden field which is loosely bound to my Model.ProcessID. When the form is posted this hidden field ProcessID is used as the TempData key...
Example:
public sealed class MyViewModel
{
public MyViewModel()
{
this.ProcessID = Guid.NewGuid().ToString();
}
public string ProcessId { get; set; }
// other fields
}
Partial View 1:
#model Models.MyViewModel
#using (Ajax.BeginForm("PartialView1", "Home", new { }, new AjaxOptions() { UpdateTargetId = "myDivToUpdate" }, new { }))
{
#Html.HiddenFor(m => m.ProcessId);
// other fields...
<input type="submit" value="next" />
}
public ActionResult PartialView1(FormCollection form)
{
return this.PartialView("PartialView1", new Models.MyViewModel());
}
[HttpPost]
public ActionResult PartialView1(FormCollection form)
{
Models.MyViewModel vm = new Models.MyViewModel();
this.UpdateModel(vm, form);
this.TempData[vm.ProcessId] = vm;
return this.PartialView("PartialView2", vm);
}
In my 2nd partial view form I once again have a hidden field loosely bound to the same Model.ProcessID. When this form is posted I can use the Model.ProcessID to retrieve my viewmodel from TempData and update it:
Partial View 2:
#model Models.MyViewModel
#using (Ajax.BeginForm("PartialView2", "Home", new { }, new AjaxOptions() { UpdateTargetId = "myDivToUpdate" }, new { }))
{
#Html.HiddenFor(m => m.ProcessId);
// other fields (different to partial view 1)...
<input type="submit" value="finish" />
}
[HttpPost]
public ActionResult PartialView2(FormCollection form, Models.MyViewModel vm)
{
vm = (this.TempData[vm.ProcessId] as Models.MyViewModel);
this.UpdateModel(vm, form);
return this.Json(new { result = true, message = "success" }, JsonRequestBehavior.AllowGet);
}
By default I believe MVC uses session state to store TempData using the SessionStateTempDataProvider and therefore each TempData will be stored in the same place for that session. Using session the only around this that I can see is to use some identifier as you suggested.
However you can implement the ITempDataProvider interface to use your own provider that could then store this data in some other form of storage (such as cookies or sql).
Related
I have two Asp.Net MVC Websites. The first one is inside the root domain domain.com, and the second one is a subdomain sub.domain.com.
Everything works fine inside the root domain, but I'm having trouble submitting a form to a controller on the subdomain. Whatever I try to send seems never to get to the controller.
It may seem strange but this problem does not happen when I try to start the application locally in debug mode. (locally is not a subdomain, just plain localhost).
Just for testing purposes I've build a simple model:
public class TestModel
{
public string test_text { get; set; }
}
And this is the CSHTML partial view:
#using (Html.BeginForm("TryModel", "Home", new { }, FormMethod.Post, new { }))
{
<input type="text" name="test_text" />
<button type="submit">Test</button>
}
Even with this simple configuration, on my local copy I've no problem, but as soon as I start the application on the remote machine, the posted data is always empty.
What do you think could be the problem?
EDIT
This is the controller methods
[HttpGet]
public ActionResult TryModel()
{
return View();
}
[HttpPost]
public ActionResult TryModel(TestModel model)
{
// code removed
return View(model);
}
And yes, the action is called correctly. Even the Request.Form is empty.
Html.BeginForm takes only 4 Parameter,
Parameters: string actionName, string controllerName, FormMethod method, object htmlAttributes
you should change the your parameter
from:
#using (Html.BeginForm("TryModel", "Home", new { }, FormMethod.Post, new { }))
to:
#using (Html.BeginForm("TryModel", "Home", FormMethod.Post, new { }))
I hope it will helps.
I created a model and i am trying to build something like when i click on submit button the values should be passed to controller which looks like
[HttpPost]
// i am getting checked property but not phoneno to controller
public ActionResult Confirm(Entity s)
{
return view();
}
My View
#model MvcApplication3.Models.Entity
#using ( Html.BeginForm("Confirm", "Home", FormMethod.Post))
{
#Html.CheckBoxFor(m=>m.checkedprop,
new
{
#class = "myCheckBox",
phoneno= "1234" //tried different ways none looks working for me
})
<input type="submit" id="ConfirmButton" />
}
Model
public class Entity
{
public bool checkedprop { get; set; }
public int phoneno { get; set; }
}
Your markup
#Html.HiddenFor(m=>m.phoneno)
and in your controller you can get
[HttpPost]
public ActionResult Confirm(Entity s, string phoneno)
{
return view();
}
Change
#Html.CheckBoxFor(m=>m.checkedprop,new {
#class = "myCheckBox",
phoneno= "1234" //tried different ways none looks working for me
})
to
#Html.CheckBoxFor(m=>m.checkedprop,new {#class = "myCheckBox"})
#{Model.phoneno = 1234}
#Html.HiddenFor(m => m.phoneno)
The reason yours didn't work is you were trying to set the phone number as an attribute of the checkbox. You need to submit that phone number as a separate field on the form. Since it's hardcoded, and the user isn't inputing it, instead of submitting it from a textbox (or whatever), you send it as a hidden field on the form.
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?
I am using three partialview on a single view, I have a submit button on clicking of which I want to send information to database, I have to retrieve data from all the partialview.
Can You please provide me correct information to do it.
Darin I m using L2S so when I drag my stored procedure, I get code some thing like this in
[global::System.Data.Linq.Mapping.FunctionAttribute(Name="SP_Name")]
public int SP_Name(
[global::System.Data.Linq.Mapping.ParameterAttribute(Name="EmployeeID", DbType="Int")] System.Nullable<int> EmployeeID
{
IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), EmployeeID);
encounterID = ((System.Nullable<int>)(result.GetParameterValue(293)));
return ((int)(result.ReturnValue));
}
}
Updated
<script language="javascript" type="text/javascript">
$(function () {
$('#Form1').submit(function () {
$.ajax({
url: this.action,
type: this.method,
data: $(this).serialize(),
success: function (data) {
var message = data.Result;
$('#Result').html(message);
}
});
return false;
});
});
</script>
In my Controller I am using
public ActionResult Index(FormCollection frm)
{
My Code ---------------------
return Json(new { Result = "Success" });
}
When I return this I m getting a file in post back and it ask me to save it.
I have checked using flidder, in URL it shows me that the path as / only
where as If I fill any particular partialview It shows something like /Controller Name/Partialview
Can You help me with this problem
Well, sending data to a controller action is usually done by performing an HTTP request to this controller action. There are different ways of performing an HTTP request:
Use a <form> tag pointing to this action
Use AJAX
So if you go with the first approach you could have a single <form> wrapping all the partials which would have multiple submit buttons (with different names). Then when you click on one submit buttons all the input fields will be sent to the controller action and then inside the controller action you could process the data based on which submit button was clicked.
If you use the second option, well, then simply harvest the values you need to be sent uipon button click and send them along the AJAX request.
UPDATE:
As requested in the comments section here's how the first technique could be put into action. It uses two partials instead of three but it could be easily extrapolated.
As always you start by defining a view model which will represent the data you would like to work with on this particular view:
public class MyViewModel
{
public Partial1ViewModel Model1 { get; set; }
public Partial2ViewModel Model2 { get; set; }
}
public class Partial1ViewModel
{
public string Foo { get; set; }
}
public class Partial2ViewModel
{
public string Bar { get; set; }
}
Then a controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new MyViewModel
{
Model1 = new Partial1ViewModel { Foo = "foo" },
Model2 = new Partial2ViewModel { Bar = "bar" },
};
return View(model);
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
// Here you have access to model.Model1.Foo and model.Model2.Bar =>
var button = "";
if (!string.IsNullOrEmpty(Request["submit1"]))
{
// submit1 button was used
button = "submit1";
}
else if (!string.IsNullOrEmpty(Request["submit2"]))
{
// submit2 button was used
button = "submit2";
}
var result = string.Format("thanks for submitting using {0}", button);
return Content(result, "text/plain");
}
}
and then a main view (~/Views/Home/Index.cshtml):
#model MyViewModel
#using (Html.BeginForm())
{
#Html.EditorFor(x => x.Model1)
#Html.EditorFor(x => x.Model2)
}
and the two corresponding editor templates (or partials if you will):
~/Views/Home/EditorTemplates/Partial1ViewModel.cshtml:
#model Partial1ViewModel
<h2>Partial 1</h2>
<div>
#Html.LabelFor(x => x.Foo)
#Html.EditorFor(x => x.Foo)
<input type="submit" value="Submit me!" name="submit1" />
</div>
~/Views/Home/EditorTemplates/Partial2ViewModel.cshtml:
#model Partial2ViewModel
<h2>Partial 2</h2>
<div>
#Html.LabelFor(x => x.Bar)
#Html.EditorFor(x => x.Bar)
<input type="submit" value="Submit me!" name="submit2" />
</div>
How do I create a partial view that has a form with assigned id?
I got as far as:
using (Html.BeginForm(?action?,"Candidate",FormMethod.Post,new {id="blah"}))
Partial view is used for both Create and Edit so first parameter ?action? will be different. I can't figure out what value of ?action? supposed to be.
UPDATE:
I guess I was not clear enough with the question. What I ended up doing is splitting Request.RawUrl to get controller name and action name:
string[] actionUrlParts = ViewContext.HttpContext.Request.RawUrl.Split('/');
using (Html.BeginForm(actionUrlParts.Length >= 2? actionUrlParts[2] : "",
actionUrlParts.Length >= 1 ? actionUrlParts[1] : "", FormMethod.Post, new { id = "blah" }))
Kind of ugly but it works. Is there a better way to get an action name inside the partial view?
Pass in the action to be performed via ViewData.
In your action that renders the view, create a ViewData item for the postback action. In your form reference this ViewData item to fill in the action parameter. Alternatively, you can create a view-only model that includes the action and the actual model as properties and reference it from there.
Example using ViewData:
using (Html.BeginForm( (string)ViewData["PostBackAction"], "Candidate", ...
Rendering actions:
public ActionResult Create()
{
ViewData["PostBackAction"] = "New";
...
}
public ActionResult Edit( int id )
{
ViewData["PostBackAction'] = "Update";
...
}
Example using Model
public class UpdateModel
{
public string Action {get; set;}
public Candidate CandidateModel { get; set; }
}
using (Html.BeginForm( Model.Action, "Candidate", ...
Rendering actions:
public ActionResult Create()
{
var model = new UpdateModel { Action = "New" };
...
return View(model);
}
public ActionResult Edit( int id )
{
var model = new UpdateModel { Action = "Update" };
model.CandidateModel = ...find corresponding model from id...
return View(model);
}
EDIT: Based on your comment, if you feel that this should be done in the view (though I disagree), you could try some logic based off the ViewContext.RouteData
<%
var action = "Create";
if (this.ViewContext.RouteData.Values["action"] == "Edit")
{
action = "Update";
}
using (Html.BeginForm( action, "Candidate", ...
{
%>
Pass nulls as action and controller. Extension will use just current action and current controller
using (Html.BeginForm(null, null, FormMethod.Post, new { id="Model" }))
Action generated for form will be the same as parent view of this partial view.
It generates
<form action="/Orders/Edit/1" id="Model" method="post">
for url http://localhost:1214/Orders/Edit/1
... and this
<form action="/Orders/Create" id="Model" method="post">
for url http://localhost:1214/Orders/Create
<% html.RenderPartial("MyUserControl", Model.ID) %>