This question already has answers here:
Reset the value of textarea after form submission
(9 answers)
Closed 8 years ago.
I have an MVC application that displays a value. This is the controller:
public ActionResult Index(DataSites DataSiteList)
{
if (DataSiteList.Latitude != null)
{
DataSites test = new DataSites();
test.Latitude = "LATITUDE";
return View(test);
}
return View(DataSiteList);
}
public ActionResult SomeInformation()
{
DataSites test1 = new DataSites();
test1.Latitude = "LATITUDE2";
return RedirectToAction("Index", test1);
}
The View:
#model miniproj2.Models.DataSites
<p>
#Html.TextBoxFor(x => x.Latitude)
</p>
And the Model:
public class DataSites
{
public string Latitude { get; set; }
}
When I go to /Home/SomeInformation, the DataSites' Latitude property is set to "LATITUDE2". Then redirects to the Index() action in the controler, sets the property to "LATITUDE" and returns the view.
When it shows the view, it displays the value "LATITUDE2" as set in the redirect. Shouldn't "LATITUDE" be displayed?
Your problem is (step by step)
Your SomeInformation() method sets the value of test1.Latitude
to "LATITUDE2".
You then pass that model to your Index() method using the overload
of RedirectToAction that accepts an object. Internally this uses
reflection to build a RouteValueDictionary based on the properties
of your model (in this case its simply latitude="LATITUDE2").
When you hit the Index method the model is bound by the DefaultModelBinder and now the value of DataSiteList.Latitude is "LATITUDE2" (which is why you enter
the if block)
In the process of binding, the DefaultModelBinder sets the
ModelStatevalue of Latitude to "LATITUDE2". Any attempts to set
the value of Latitude are now ignored because the view uses
ModelState value to render the control.
It not clear what your trying to do here. You can make it work as you expect by adding ModelState.Clear(); as the first line of your Index() method. This clears all existing ModelState values an you can now set the value to "LATITUDE".
But your if block makes no sense. Perhaps you were just doing some kind of test, but you may as well remove the parameter from the Index() method and just initialize a new instance of DataSites in the method.
Edit
To give a bit more information as to why updating a model property has no affect once ModelState has been set.
Imagine you have a form to collect user information where the model contains int Age. The user is asked to enter their age and someone enters "I'm five next week!". Of course this wont bind to an int so the DefaultModelBinder adds the value (the attemptedValue) and adds a ModelStateError.
When the view is returned it will typically display an error message such as "The field Age must be a number". If the html helper rendering the control used the model value, then it would display "0" (the default value for int). It would be somewhat confusing for the user to see "0" in the textbox and next it a message saying it must be a number (What! but zero is a number and what the heck happened to what I entered?). So instead, the helper uses the value from ModelState and now the users sees "I'm five next week!" and an associated error message that makes sense for the value.
So even though you thoughts were that "its not logical", there is actually some logic to this behavior.
You are not setting Altitude. It will be null so the code will never go into this block and set Lattitude to "LATTITUDE"
if (DataSiteList.Altitude != null)
{
DataSites test = new DataSites();
test.Latitude = "LATITUDE";
return View(test);
}
Related
I have this in my controller:
string PONumber = Request.QueryString["PONumber"];
return View();
My question is how would I use the string in my view ?
I have tried:
#{
ViewBag.PONumber
}
but I get this error:
This operation will be resolved at runtime. Am I not doing this right ?
I have also tried:
<%= PONumber %>
and that also did not work
You need to put the result from your querystring into the ViewBag before you can pull it out of the ViewBag in your view:
// Assign directly to ViewBag
ViewBag.PONumber = Request.QueryString["PONumber"];
return View();
In your view, don't try to print the value by writing it in a code block (#{ }); simply use it where you'd like:
<h2>The PO Number is #ViewBag.PONumber.</h2>
You have a misnomer, when you have a Controller with:
[HttpGet]
public ActionResult GetExample(string PONumber)
{
}
The PONumber parameter, would represent said equivalent for your Query String. So you honestly wouldn't need to manually call Request.QueryString[...].
So you would assign parameter, directly to your ViewBag. However, you could add it directly to a model and display through your model also.
If your wanting to use a ViewBag, you would simply do:
// Front End:
#ViewBag.PONumber
// Back End:
ViewBag.PONumber = PONumber;
Also, make sure when you request your data that it isn't actually a null or empty value.
ViewBag.PONumber = Request.QueryString["PONumber"];
return View();
and then in view
#ViewBag.PONumber
This question already has answers here:
How can I get the name of a variable passed into a function?
(23 answers)
Closed 8 years ago.
One thing to note is: I need to get the variable's name, not the property's name.
I have a scenario where I need to pass in either List<int> or List<double> to a view. So in the partial view I bind the model to dynamic:
#model dynamic
var nameOfParameter = Web.Controllers.MemberInfoGetting.GetMemberName(() => Model);
#foreach (var value in list)
{
<td>#Html.Editor(value, "DoubleTemplate", new { Column = count, Switcher = (YearOfProgram >= count)})</td>
sum += (double)value;
++count;
}
And this is how I call the partial view from the main view:
#Html.Partial("_myPartial", Model.CategoryList)
Then I found that I have to know the name(CategoryList) of the list which is passed into the partial view.
Here I found many posts talking about using something like this:
public static class MemberInfoGetting
{
public static string GetMemberName<T>(Expression<Func<T>> memberExpression)
{
MemberExpression expressionBody = (MemberExpression)memberExpression.Body;
return expressionBody.Member.Name;
}
}
}
This code doesn't work for me as the result of (nameOfParameter )calling it will always be "Model", rather than "CategoryList".
So, is it possible to achieve what I want?
Update1
The reason why I need to pass in the name of the list is because I need the name of the list to form the name on the html element into something like: CategoryList_1. So Razor knows I am trying to bind the value of the textbox into the 1st element of a property in my view model.
class MyViewModel
{
................
public List<double> CategoryList {get; set;}
................
}
Update2
#Html.RenderPartial("_VerificationSummarySection",new { ListName = "PracticeEvaluationCreditsVerifiedList", List = Model.PracticeEvaluationCreditsVerifiedList})
I'm now trying to pass in the name of the list by using RenderPartial. But, I cannot find the right way to use it.
Names are relative. The list itself doesn't have a name.
One thing to notice is : I need to get the variable's name, not the property's name.
In your use of the method:
var nameOfParameter = Web.Controllers.MemberInfoGetting.GetMemberName(() => Model);
The name here is Model, and Model is the property; it isn't a variable. You have obtained the name of the property: "Model". You cannot, however, obtain the name as it would have been in a calling context (even in regular C# this is hard; between views, however, it is essentially impossible).
As an aside, with that GetMemberName method, if you did use it to obtain the name of a variable, in IL terms it would actually cease being a variable, and would instead become a field - because that is how "captured variables" are implemented by the compiler.
You should instead make it possible to pass down the name you want; either as additional context on the view, or as a view-model that encapsulates a list and a name, or by creating a "named list" concept (perhaps by subclassing List<T> or similar, or by adding an INamedList interface).
I have a model that has a PersonId int, and also, a List<SelectListItems> People for people. The UI allows me to select a person from the drop down model, and save the value into PersonId.
That works, but if there is an error in my ModelState, caused by another field, I do this:
if (ModelState.IsValid == false)
{
return View(model);
}
Problem is, the object holding the list of people is NULL in the returned model. Do I really need to repopulate it from the database again, or can this somehow be 'stored', and only populated when I initially create the view?
Yes. You need to load the SelectView again.
Only values in your inputs are posted (hidden or normal ones)... so anything to be posted must be in those inputs.
If you want to avoid going to the database... you should cache that list.
Here you can see an example for the caching logic: https://stackoverflow.com/a/349111/7720
Yes, you need to repopulate it. Only the selected value of a dropdown list is sent via a form. It's perfectly normal to do this. In general, if you have data available on the server, it always makes sense to cache it/query for it again, rather than trusting any input from a user.
To conveniently build SelectLists, rather than using SelectListItem, I use a method on a base controller:
[NonAction]
public SelectList BuildSelectList<TSource>(IEnumerable<TSource> source,
Expression<Func<TSource, int>> valueKey, Expression<Func<TSource, string>> textKey,
object selectedValue = null)
{
var selectedValueKey = ((MemberExpression)(MemberExpression)valueKey.Body).Member.Name;
var selectedTextKey = ((MemberExpression)(MemberExpression)textKey.Body).Member.Name;
return new SelectList(source, selectedValueKey, selectedTextKey, selectedValue);
}
Note the use of NonActionAttribute, which is used to indicate that a public controller method is not an action method.
Then in the controller, it's easy to build any list again, without polluting the actions too much. For example:
[HttpPost]
public ActionResult Index(SomeViewModel model)
{
if (ModelState.IsValid)
return RedirectToAction("Success");
// The model wasn't valid, so repopulate the dropdown
model.People = BuildSelectList(db.People, m => m.Id,
m => m.Name, model.PersonId);
return View(model);
}
You could do something similar for SelectListItem, rather than manually rebuilding your lists each time.
I have two partialViews in my ASP.NET MVC4 Application-
[HttpGet]
public ActionResult Autocomplete_Search(string accountHead, List<LedgerModel> ledge)
{
if (!String.IsNullOrEmpty(accountHead)) {
ledge = (from u in db.LedgerTables
where u.AccountHead.Contains(accountHead) && u.FKRegisteredRecord == this.LoggedInUser.RegisterID
select new LedgerModel {
AccID = u.AccID,
Place = u.Place,
AccountHead = u.AccountHead,
DateAccountHead = Convert.ToDateTime(u.DateAccountHead) != null ? Convert.ToDateTime(u.DateAccountHead) : DateTime.Now
}).ToList();
return RedirectToAction("_ProductSearchList", ledge);
}
return View();
//return Json(ledge, JsonRequestBehavior.AllowGet);
}
And-
public ActionResult _ProductSearchList(List<LedgerModel> ledge) {
List<LedgerModel> ledger = null;
if (ledge != null) {
ledger = (from u in ledge
select new LedgerModel {
AccID = u.AccID,
Place = u.Place,
AccountHead = u.AccountHead,
DateAccountHead = Convert.ToDateTime(u.DateAccountHead) != null ? Convert.ToDateTime(u.DateAccountHead) : DateTime.Now
}).ToList();
return PartialView(ledge);
}
else {
return PartialView(ledge);
}
}
Okay now when I send string through a textbox, Action AutoComplete_Search is called. At the time of redirection to another method named _ProductSearchList I am sending an object ledge of listType to This method. But It says ledge null in _ProductSearchList action's parameters.
However this object is a list type and contains records. How do I get this object ledge which is redirected to action _ProductSearchList?
The second parameter taken by RedirectToAction is not a model, it is a route.
That's why you are not receiving what you expect in your _ProductSearchList action.
I'm not quite sure that something like this would work because i don't know how a list of complex objects could be serialized in the url (or even if this is recommanded), but here is what would be expected :
return RedirectToAction("_ProductSearchList", new { ledge = ledge });
To pass your list, you have the TempData option (quote from MSDN) :
An action method can store data in the controller's TempDataDictionary
object before it calls the controller's RedirectToAction method to
invoke the next action. The TempData property value is stored in
session state. Any action method that is called after the
TempDataDictionary value is set can get values from the object and
then process or display them. The value of TempData persists until it
is read or until the session times out. Persisting TempData in this
way enables scenarios such as redirection, because the values in
TempData are available beyond a single request.
Don't forget to take a look at Using Tempdata in ASP.NET MVC - Best practice before using it.
In the first you can`t get List ledge in get request in Autocomplete_Search.
You can`t pass complex object in redirecting. You can only pass a simple scalar value.
Check answer in this thread:
send data between actions with redirectAction and prg pattern
Thanks all for giving time on this issue.
As #Damian S described about complex object redirecting is notable advice for me.
However I was able to find simplest solution to this problem to use DataDictionary in C#.
I managed it with using TempData[] to store details in easiest way I guess because it is most Precise and trivial technique.
Using TempData[]
Storing record in TempData[] in AutoComplete_Search() controller-
TempData["Records"]= ledge;
Usage in ProductSearchList controller
List<ledgerModel> ledge= (List<ledgerModel>)TempData["Records"];
Solved my problem and headache of playing with objects methods to methods.!
I think I have a very noob question but there it is:
I have a link to another controller and I want to pass a list of strings.
Controller:
[HttpPost]
public ActionResult Index(DateTime? dateFilter, int? Provider, int? middleOffice)
{
...
ViewBag.ReasonGroupName = new List<string>() { "Faults" };
...
}
View:
#Html.ActionLink(item.username, "Edit", "Hours", new { IdUser = item.IdUser, ReasonGroupNames = (List<string>)ViewBag.ReasonGroupName }, new { #class = "iframeFull" })
In my controller the ViewBag.ReasonGroupName is created properly: a list with the index 0 item ([0]) with the string "Faults"but when I receive it in my other controller the 0 index element of my list comes with "System.Collections.Generic.List1[System.String]" instead of "Faults"
I also try changing ViewBag for ViewData object but same problem appear.
Any ideia on what I'm doing wrong?
Thanks
The text "System.Collections.Generic.List1[System.String]" is what you get if you call ToString() on an instance of the List type. When you generate the link the url has to be a string, hence ToString() is called on each argument.
It is not clear what information you are trying to send via the action link. If you want the first value then you could change the link to be the following:
#Html.ActionLink(item.username, "Edit", "Hours", new { IdUser = item.IdUser, ReasonGroupNames = ((List<string>)ViewBag.ReasonGroupName)[0] }, new { #class = "iframeFull" })
This will set the ReasonGroupName to the first value in the list.
But you probably want to send all the values in the list and not just the first. In that case you could join all the values of the list into a string and pass that as the argument instead.
So in your controller you could do:
ViewBag.ReasonGroupName = string.Join(",", yourListVariableHere);
This will convert the list into a comma separated string that can be included in the url properly.
The #Html.ActionLink() method is used generate a url link to the given Controller/Action. Thus, it can only contain parameters that can be passed in the url of the link.
So I don't think you can pass an object through on the url.
You are trying to send through get-request some complex data. As first, that is bad idea anyway, because in one good moment you can overcome limit for link.
You have solutions:
Stay on sending get-request: serialize list to string - use special serializers or use string.Join(",", list); your edit method must accept string, which you deserialize to List by special deserializer or by string.Split method. Using of string.Join and string.Split is bad idea if your strings can contain char ','
Don't send data. You can store it in db/session and then send id of saved data. You must be accurately: you must delete old data from db/session, but not new data
Use post-request. Make form with #Html.BeginForm(...); in form with for add hidden input (#Html.HiddenFor(...)), and add submit button <input type="submit">, style it as link.
Your problem is due to the fact that you're trying to pass a List of strings but have assigned it to a string property in the ActionLink(). You'll need to create a small helper to explode this List out to a single formatted string of your choice. This will then be passed to the url as a (for example) comma separated string. You'd then do some magic in the controller action to rehydrate this as required back to the List.