I have read a ton about this problem, but I cannot figure it out.
I have a dropdownlist on my view that is used for paging. You choose the page you want to go to and the form gets submitted. When the view is returned, the requested page is shown. The dropdownlist changes to reflect the newly shown page. This is working fine.
My problem is this:
I have 2 submit buttons, one for next page and one for prev page.
In the controller, if one of the submits have been pressed, the page# should be incremented or decremented by 1. This is happening, and the correct page of data is being shown. But, the dropdown list will not, NO MATTER WHAT I DO, reflect the new page number when the view is shown.
Basically, if the dropdown is changed by the user, it will reflect those changes when it is sent back after a form submission. If I change the page # programmatically, the dropdown will not change, even though the correct page of data gets shown.
Here is the action method:
public ActionResult Results(TechSearch search, string NextPage, string PrevPage)
{
if (NextPage != null) search.Page++;
if (PrevPage != null) search.Page--;
int resultCount = search.GetResultCount(Desk);
List<int> pages = new List<int>();
int pageCount = (int)Math.Ceiling((decimal)resultCount / search.PageSize);
for (int i = 1; i <= pageCount; i++) pages.Add(i);
ViewData["pages"] = new SelectList(pages, search.Page);
ViewData["pageCount"] = pageCount;
return View(search);
}
And here is the relevant part of the view:
<input type="submit" value="<" name="PrevPage" />
Page
<%=Html.DropDownList("Page",(SelectList)ViewData["pages"]) %>
of
<%=ViewData["pageCount"]%>
<input type="submit" value=">" name="NextPage" />
Please help.
EDIT
Page 333 of Pro ASP.NET MVC Framework by Steven Sanderson:
How Input Controls Get Their Values
Each of [the HTML Helpers for Rendering Input Controls] tries to
populate itself by looking for a value
in the following places, in this order
of priority:
ViewData.ModelState["controlName"].Value.RawValue
value parameter passed to HTML helper method, or if you’ve called an
overload that doesn’t include a value
parameter, then
ViewData.Eval("controlName")
ModelState is a temporary storage area
that ASP.NET MVC uses to retain
incoming attempted values plus binding
and validation errors. Notice that
it’s at the top of the priority list,
so its values override anything you
might set explicitly. This convention
means that you can pass an explicit
value parameter to act as the helper’s
default or initial value; but when
rerendering the view after a
validation failure, the helper will
retain any user-entered value in
preference to that default.
I have verified that ViewData.ModelState["Page"].Value.RawValue does contain the unexpected page number that is pestering me. What's the best way to work around this?
The problem was that ModelState was holding the dropdownlist's passed in value. For the HtmlHelper input controls, ModelState is given priority over explicitly set values when displaying a value in the input control.
Here is what I have done in my action method to fix the problem:
ModelState.Remove("Page");
return View(search);
I am interested in hearing other ways of fixing my problem.
Change the selected value with javascript on the click of the next or previous button, right before your postback.
Are you sure that you are guaranteed that at least one of NextPage or PrevPage will be null? (And you don't get anything weird like String.Empty forcing an increment and decrement?)
Could .LastOrDefault() be replaced with just .Last() and you are sure that you are not assigning default(int) to search.Page?
Does the below have any effect?
ViewData["pages"] = new SelectList(pages,
pages.FirstOrDefault(p => p == search.Page ));
Replacing
ViewData["pages"] = new SelectList(pages, search.Page);
Related
I have a model with multiple fields in a .net core web app.
The model is bound to a view, which uses asp-for tag helpers to render hidden inputs, text inputs, select lists and a text area.
The issue..
On posting to the server, all inputs are bound correctly, except the text area. This is always coming through as null.
Even more strangely, if I assign a value to the text area property before rendering the view, the value displays in the text area as expected (not the strange bit), and when posted to the server, the value is posted back. If I try and change the value however, the original one is still picked up in the model binding, despite Chrome showing in the network tools that the new value was what was posted.
Has anyone else experienced anything like this? Am I being an idiot (again)?
A sub set of my code below:
Render View:
public IActionResult Review()
{
var requestVm = new Request();
requestVm.AdditionalComments = "This was set in the model before rendering the view and should be overwritten.";
return View(requestVm);
}
Html:
#model Models.Request
<form asp-controller="Request" asp-action="Review" method="post" role="form" enctype="multipart/form-data" class="ui form review">
<textarea asp-for="AdditionalComments" class="AdditionalComments"></textarea>
</form>
Post Action:
[HttpPost]
public IActionResult Review(Request requestVm)
{
// Some logic in here to save to db etc..
return RedirectToAction("Thank_You");
}
As you can see, nothing special going on there.
Any help appreciated.
Mark
Please use Bind(Include=string) attribute in the post method and mention all the property names including the one that gets missing. This demonstrates the use of the attribute:
[HttpPost]
public IActionResult Review([Bind(Include = "AdditionalComments,other properties")] Request requestVm)
{
// Some logic in here to save to db etc..
return RedirectToAction("Thank_You");
}
Let me know if this doesn't help.
As I rather suspected, there was in fact nothing wrong with the code..
Instead, there was a legacy hidden input for the same property being added to the page via a partial from a much earlier stage in the project before various design changes. It explains why setting a value on load was being carried through, and updating in the text are was never working, as the model was simply binding to the first instance of the property found.
Had to be something simple.. Hopefully, this will help anyone else who finds themselves in a similar situation.
I have an aspx page of images that, when selected, a popup appears prompting for various information tid bits that I then store the information as data attributes on hidden labels through use of jQuery (i.e. data-id="####", data-difficulty="###", etc.). I acknowledge that this isn't the best way to do it necessarily but I've tried other things (see below) and nothing has worked yet.
I've been attempting, and to no avail, to retrieve the dynamically updated data attributes so the various items can be stored to my local ms sql database. The updating of the attributes works perfectly in that I can view the items being updated properly in Chrome's developer tools. Despite this when I try to pull the same attributes I can see as being updated I'm unable to retrieve the updated values in the code behind and keep getting back the initial values (generally an empty string "").
Here's the implementation on the aspx page:
<asp:Label runat="server" ID="lblSelection" data-id="" data-count="" data-price="" data-difficulty="" CssClass="selected-items" />
and here's the relevant method being called when the "Submit" button is clicked further down on the same page:
protected void SubmitClicked(object sender, EventArgs e)
{
var currentID = lblSelection.Attributes["data-id"];
var currentCount = lblSelection.Attributes["data-count"];
var currentPrice = lblSelection.Attributes["data-price"];
var currentDifficulty = lblSelection.Attributes["data-difficulty"];
if (currentID == null || currentID == "")
{
// stop and throw an informative message to the user
}else{
// keep processing ...
}
}
The trouble is that when I run the project in debug mode and inspect those elements (again, making sure that I can visually see that the attributes are actually updated in the developer tools) they're all the initial value of "". My only guess is that there's some kind of post back issue but I wouldn't think that would happen until my method had been called and fully processed. As a note, I'm populating the images onto the page and updating their attributes already through a sql call based on the id of the item:
<img runat="server" src="" data-id="12345" />
The initial loading all works perfectly so I can clearly set the properties from the code behind fine. For whatever reason though I am unable to pick up the updated attribute values in the code behind after the jQuery updates the label's attributes (following some clicking and whatnot). Any tips or thoughts on this would be greatly appreciated.
What you are trying to do cannot work because:
The value of custom attributes is not posted back to the server (as discussed here).
Even if you set the text of the Label in client code, it would also not be available in the Text property in code-behind. A Label is rendered as a span element in the page, and the content of that type of element is not posted back to the server.
A nice list of properties included in a postback is given in this topic: Which values browser collects as a postback data?
As suggested by mshsayem, you should use HiddenFields. If, for some reason, you want to do it differently, you could use hidden TextBoxes, set their value in client code, and retrieve it with the Text property in code-behind. In other words: HiddenFields are not the only controls for which a value set in client-code can be retrieved in code-behind (but they are the obvious choice if the control is not to be displayed).
I encountered some weird behaviour today and I was hoping someone could shed some light on it for me because I'm perplexed.
I have a couple of methods I use to interact with the ui for the sole purpose of displaying error/success/warning messages to the user.
Here is one of them
public static void Confirm(string text)
{
var page = (Page)HttpContext.Current.Handler;
var uiConfirm = new HtmlGenericControl("div")
{
ID = "uiNotify",
InnerHtml = text
};
uiConfirm.Attributes.Add("class", "ui-confirm");
page.Master.FindControl("form1").Controls.AddAt(2, uiConfirm);
}
This works perfectly fine except for one nuance I encountered this morning and I was hoping someone could shed some light on it for me.
I am working on your run of the mill profile editing page. In this page, I am binding a couple of dropdownlists (country, province/state) on page load. I have a submit at the bottom and a click event that fires to update the information, then call the method above to notify the user that their information was successfully updated. This works the first time you click the submit button; the page posts back, the information gets updated in the database, the dynamically added div gets popped in, confirm message is displayed and all is good. However, if you then click the submit button again, it fails stating SelectedItem on the dropdowns I'm binding in the page load is null (Object reference not set to an instance of an object). The dropdown is actually wiped for some reason on the second postback, but not the first.
In sheer desperation after trying everything else, I decided to take out the call to the confirm method... and strangely enough the error disappears and I can update the information on the page as many times as I like.
If I add a generic control statically to the page I'm working on, and change my method slightly so that instead of adding a generic control to the form dynamically it just finds the generic control on the page, that does no produce the same error.
The problem also goes away if I remove the two dropdowns from the page or just stop interacting with them.
Why on earth would adding a dynamic control to the form wipe my dropdowns on postback?
I think you should consider using the PlaceHolder class in your MasterPage, the AddAt(2, uiConfirm) is going to bite you and probably is:
Markup:
.......
<asp:PlaceHolder id="PlaceHolder1"
runat="server"/>
......
Code-behind:
public static void Confirm(string text)
{
var page = (Page)HttpContext.Current.Handler;
var uiConfirm = new HtmlGenericControl("div")
{
ID = "uiNotify",
InnerHtml = text
};
uiConfirm.Attributes.Add("class", "ui-confirm");
//may need to change depending on where you put your placeholder
Control placeHolder = page.Master.FindControl("PlaceHolder1");
placeHolder.Controls.Clear();
placeHolder.Controls.Add(uiConfirm);
}
I've been looking for a reason why this doesn't work, but I can't find one. In my asp.net application I create a a bunch of hidden inputs in c# and then try to modify them in javascript before I call them back to the server.
My c# code:
hidden3 = new HtmlInputHidden();
hidden3.ID = "total";
hidden3.Value = index.ToString();
this.Controls.Add(hidden3);
my javascript code:
mod = document.getElementById("total");
mod.value = newVal;
I can call the value back fine but it doesn't change. I have also added alerts for the original value and then the value after changing values and they both show up fine. However the code is never changed so when I pull the values
To get the value back I am using this;
HtmlInputHidden hiddenControl = (HtmlInputHidden)FindControl("total");
Have you verified that the resulting input tag as the ID of "total"? By default, in Webforms, the actual client-side ID is prefixed with the parent's Id (and a delimiting character); this helps to ensure that IDs are unique. One way to get the real client-side Id is to pull the value from the ClientID property of the control, but you should only look at that value once it has been put in a Controls collection.
These controls are dynamically created and they have to be created in each postback. However, these should be built before Page_Load preferably in Page_Init event handler. If these are created in Page_Load, the view state has already been processed and the control can't be set from the posted value.
So I am experiencing an issue with an .aspx page and some server side code, where I am getting unexpected results.
The goal of this page is simple, there are 5 radio buttons and a button with a server side onclick function. The idea is the user picks 1 of the 5 radio buttons, and then clicks the button. Upon clicking the button I verify (not using form validation, because I wanted a different feel) that a button is checked, and then store the selected option in a database.
Due to the fact that the number of radio buttons may change in the future I decided to try and abstract the number of radio buttons to make it easier on my self to change in the future.
So at the top of my server side code I created a list of possible options.
I then have a registerVote function that takes in a RadioButton object, and a number to grab a setting from the config file. I throw those 2 values into a wrapper class, and then add them to the list of possible options.
Finally when the submit button is pressed, I iterate through all possible options to see which one is checked, and grab its associated value.
public partial class VotePanel : System.Web.UI.Page
{
List<VoteOption> voteOptions = new List<VoteOption>();
public string registerVote(RadioButton newRadioButton, int voteOption)
{
voteOptions.Add(new VoteOption(newRadioButton, voteOption));
return ConfigurationManager.AppSettings["vote_option_" + voteOption];
}
protected void Submit_Click(object sender, EventArgs e)
{
//Check vote
string vote_value = "";
bool someButtonChecked = false;
foreach (VoteOption vo in voteOptions)
{
if (!someButtonChecked && vo.button.Checked)
{
vote_value = vo.movie;
someButtonChecked = true;
}
}
//....
}
}
class VoteOption
{
public RadioButton button;
public int vote_value;
public VoteOption(RadioButton r, int v)
{
button = r;
vote_value= v;
}
}
The code I use in page to add a radio button looks like this
<asp:RadioButton ID="RadioButton1" runat="server" GroupName="Vote" style="position: relative; top: 3px;" /><%=registerMovie(RadioButton1,1)%>
Now for the problem I am experiencing. Whenever the submit button is clicked, the list has a count of zero, and looks like it has been reinitialized. I validated that values are being added, by returning the list count in the registerVote method, and objects are indeed being added, but for some reason are not available to the Submit function.
Now variables on a page like this shouldn't reinitialize right? I also tested a string, and it did not reset and was available to the Submit button. What I did was define a class variable string time = DateTime.Now.Ticks.toString(); and displayed that after the submit button was clicked, and the time was always the same reguardless of how many times I clicked it.
So why would my List reinitialize, but not a string? Any ideas?
Keep in mind that your page class will be constructed and destructed for every request - no state will be maintained between each page load, it is up to you to properly recreate state as needed. In this case it appears that your list voteOptions is not being recreated before Submit_Click is called.
You'll have to register all your voting options regardless of whether the page is in a postback or not inside the Page_Load or OnInit handlers of the page. This will reconstruct voteOptions, which will then be accessed when Submit_Click is called.
Take a look at the ASP.NET Page Life Cycle.
The problem seems to be that you are constructing the List<VoteOption> voteOptions at page render then expecting it to still be there on postback. The Page object does not exist past the point that the page is delivered to the browser, so your list of vote options gets disposed of as well when the browser has received the page.
You'll either need to reconstruct the voteOption list before or during Submit_Click on postback, or give yourself enough information in the value of the radio button that you don't need it.
I don't see in your code any place where the list that you are building is placed in memory. I believe you are rebuilding it on each page reload. P.s. might be my reading but you created a function called registerVote and you are calling a method called registerMovie so that might be your problem.
You could place the list in the session and get it back from session.
Personnally I would change the code to
1) Check if the list is in memory and get it. If not in memory call a method to generate it once and then place it in memory.
2) Use a RadioButtonList on your page that you can then bind to your list as a data source.
asp.net is stateless, so every postback (such as clicking Submit) recreates the server-side class. If you want your list to persist between calls, you should save it in ViewState or a Hidden field. Not sure about the string though; what you're describing doesn't fit the asp.net lifecycle.