So here we go. I've got two drop down lists on a "customer" record. The first one is called "Store". The second one is called "StoreWarehouse". When the user selects a "Store" value, that filters the "StoreWarehouse" drop down list based on their selection. I have the filtering done via jQuery and AJAX. I am using a ListView and this is in the InsertItemTemplate.
Here's the code for that:
function StoreWarehouseLoad(store_ddl) {
var store_id = store_ddl.options[store_ddl.selectedIndex].value;
var js = JSON.stringify({ store: store_id });
$.ajax({
url: baseUrl + '/AutoComplete/StoreList.asmx/GetStores',
data: js,
dataType: "json",
type: "POST",
contentType: "application/json; charset=utf-8",
success: function (data) {
var ddl = $("#StoreWarehouse_ddl");
ddl.find('option').remove();
$(data.d).each(function (index) {
ddl.append('<option value="' + this.WarehouseID + '">' + this.WarehouseID + '</option>');
});
switch (culture) {
case "en-CA":
ddl.val(store_id.substring(0, 2) + "I");
break;
case "en-US":
ddl.val('001');
break;
default:
alert("The default warehouse cannot be set because there's something wrong with the current culture...");
break;
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
sendErrorEmail(window.location.href, 'Customers.aspx.StoreWarehouseLoad', XMLHttpRequest);
}
});
}
This JavaScript calls a web service that loads the "StoreWarehouse" drop down list based on the selection chosen in the "Store" drop down list. This is all working wonderfully.
My issue is when I save a record. Say I'll be entering in a new record for a customer, enter in all the values (as well as Store and StoreWarehouse drop down lists). I will click "Save" - which is then supposed to save the data to the database. My issue is the SelectedValue from the StoreWarehouse drop down list is not coming through to the business layer. I even captured the e.Values for the "OnInserting" event for the ListView and e.Values["DefaultWarehouseId"] is null. Here's my aspx page for the two drop down lists:
<div class="labelAndTextboxContainer">
<div class="labelContainer">
<asp:Label CssClass="rightFloat" ID="StoreId_lbl" runat="server" Text="Branch:"></asp:Label><br />
</div>
<div class="textboxContainer">
<asp:DropDownList ID="Store_ddl" runat="server" DataSourceID="StoreDataSource" AppendDataBoundItems="true"
onchange="StoreWarehouseLoad(this);" DataValueField="StoreID" DataTextField="StoreID"
CssClass="leftFloat" Font-Size="Smaller" SelectedValue='<%# Bind("StoreID") %>'>
<asp:ListItem />
</asp:DropDownList>
<asp:RequiredFieldValidator ID="StoreId_ddl_rfv" runat="server" ControlToValidate="Store_ddl"
Text="*" ForeColor="Red" />
</div>
</div>
<br class="clear" />
<div class="labelAndTextboxContainer">
<div class="labelContainer">
<asp:Label CssClass="rightFloat" ID="StoreWarehouse_lbl" runat="server" Text="Def. Store Warehouse:"></asp:Label><br />
</div>
<div class="textboxContainer">
<asp:DropDownList ID="StoreWarehouse_ddl" runat="server" CssClass="leftFloat" Font-Size="Smaller"
OnDataBound="StoreWarehouse_ddl_OnDataBound" onchange="changeWarehouse();" SelectedValue='<%# Bind("DefaultWarehouseId") %>'
ClientIDMode="Static">
</asp:DropDownList>
</div>
</div>
As you can see, I've got the "SelectedValue" on the StoreWarehouse drop down list set to <%# Bind("DefaultwarehouseId")%>. DefaultWarehouseId for some reason is not picking up the value in the drop down list for StoreWarehouse.
The funny thing is I can put the following into my JavaScript that is shown above and it alerts me of the selected value of that drop down list:
alert("store warehouse selected is " + $("#StoreWarehouse_ddl").val());
Why isn't the server side code picking up this value?
Thanks a bunch!
Mike
Like #shiznit123 said this information is stored in the ControlState. One of the reasons I prefer the MVC framework over WebForms.
However, in your situation I believe you can get around this issue using hidden fields. asp:hidden. You just have to manage the value directly. So when the user selects an item in the Drop Down List update the corresponding hidden field.
I would suspect the problem is because you are populating your DropDownList control client side. When you postback the serverside ignores your javascript generated items because it rebuilds the controls based on the hidden ControlState.
If you want ajax for this sort of thing in WebForms I'd look into the ajax controls provided and just use an update panel.
Related
I've been trying to get an ASP.NET WebForms page up and running that has dual listboxes. This is a standard paradigm where there are two side-by-side listboxes, with available items on the left, selected items on the right, and buttons that move the items from one side to another.
I want all the work to be done on the clientside, via JQuery (or whatever). And I want to be able to retrieve the selected items upon postback.
I've run into all sorts of hurdles. Here's a few:
1) When the postback occurs, I receive this error:
Invalid postback or callback argument. Event validation is enabled using in configuration or <%# Page EnableEventValidation="true" %> in a page. For security purposes, this feature verifies that arguments to postback or callback events originate from the server control that originally rendered them. If the data is valid and expected, use the ClientScriptManager.RegisterForEventValidation method in order to register the postback or callback data for validation.
Turing "EnableEventValidation" off is risky. But "ClientScriptManager.RegisterForEventValidation" is confusing.
2) Assuming I get to the point of actually getting the postback to occur, my listbox doesn't contain the selected items.
A common recommended solution for this is to copy the selected values into a hidden field before the postback. Seriously? There's got to be a better way.
3) And of course, all the details for how the client-side scripting should work is confusing also, although not nearly as bad as the first two items.
So what's the solution? I've posted the answer below.
According to https://blog.stackoverflow.com/2011/07/its-ok-to-ask-and-answer-your-own-questions/, it's not only OK to ask and answer your own question, it's encouraged. So that's what I'm doing.
Here's my working solution, and after this I will go into details. Please note that I'm by no means an expert in web development or Javascript -- far from it. So take all of this with a grain of salt, and always remember that I'm not responsible if your code somehow manages to execute a HCF code.
<script>
function addAllItems() {
$("#<%= lbAvail.ClientID %> option").appendTo("#<%= lbSelected.ClientID %>");
enableControls();
}
function addSelectedItems() {
$("#<%= lbAvail.ClientID %> option:selected").appendTo("#<%= lbSelected.ClientID %>");
enableControls();
}
function removeAllItems() {
$("#<%= lbSelected.ClientID %> option").appendTo("#<%= lbAvail.ClientID %>");
enableControls();
}
function removeSelectedItems() {
$("#<%= lbSelected.ClientID %> option:selected").appendTo("#<%= lbAvail.ClientID %>");
enableControls();
}
function enableButtons(listBoxControlId, buttonAllControlId, buttonSelectedContolId) {
var count = $("#" + listBoxControlId + " option").length;
document.getElementById(buttonAllControlId).disabled = count <= 0;
count = $("#" + listBoxControlId + " option:selected").length;
document.getElementById(buttonSelectedContolId).disabled = count <= 0;
}
function enableControls() {
enableButtons("<%= lbAvail.ClientID %>", "<%= btnAddAll.ClientID %>", "<%= btnAddSelected.ClientID %>");
enableButtons("<%= lbSelected.ClientID %>", "<%= btnRemoveAll.ClientID %>", "<%= btnRemoveSelected.ClientID %>");
}
function selectAll() {
$("#<%= lbSelected.ClientID %> option").prop("selected", true);
}
</script>
<div style="margin-top: 20px;">
<table>
<tr>
<td>
<asp:ListBox ID="lbAvail" runat="server" style="min-width: 150px" Height="150" SelectionMode="Multiple"
onchange="enableControls();" ondblclick="addSelectedItems();"/>
</td>
<td style="padding-left: 10px; padding-right: 10px;">
<table>
<tr>
<td>
<asp:Button Width="35" ID="btnAddAll" Text="»" OnClientClick="addAllItems(); return false;" UseSubmitBehavior="False" runat="server"/>
</td>
</tr>
<tr>
<td style="padding-top: 5px">
<asp:Button Width="35" ID="btnAddSelected" Text=">" OnClientClick="addSelectedItems(); return false;" UseSubmitBehavior="False" runat="server"/>
</td>
</tr>
<tr>
<td style="padding-top: 5px">
<asp:Button Width="35" ID="btnRemoveSelected" Text="<" OnClientClick="removeSelectedItems(); return false;" UseSubmitBehavior="False" runat="server"/>
</td>
</tr>
<tr>
<td style="padding-top: 5px">
<asp:Button Width="35" ID="btnRemoveAll" Text="«" OnClientClick="removeAllItems(); return false;" UseSubmitBehavior="False" runat="server"/>
</td>
</tr>
</table>
</td>
<td>
<asp:ListBox ID="lbSelected" runat="server" style="min-width: 150px" Height="150" SelectionMode="Multiple"
onchange="enableControls();" ondblclick="removeSelectedItems();"/>
</td>
</tr>
</table>
<div style="margin-top: 10px;">
<asp:Button ID="btnSubmit" CssClass="btn btn-primary" Text="Submit" runat="server" OnClientClick="selectAll();"/>
</div>
</div>
...
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
lbAvail.Items.Add(new ListItem("Monday", "Mon"));
lbAvail.Items.Add(new ListItem("Tuesday", "Tues"));
lbAvail.Items.Add(new ListItem("Wednesday", "Wed"));
lbAvail.Items.Add(new ListItem("Thursday", "Thur"));
lbAvail.Items.Add(new ListItem("Friday", "Fri"));
lbAvail.Items.Add(new ListItem("Saturday", "Sat"));
lbSelected.Items.Add(new ListItem("Sunday", "Sun"));
ScriptManager.RegisterStartupScript(this, GetType(), "CallEnableControls", "enableControls()", true);
}
else
{
string[] selectedValues = Request.Form.GetValues(lbSelected.UniqueID);
// ...
}
}
protected override void Render(HtmlTextWriter writer)
{
// Register all Available items as valid items in the Selected listbox.
foreach (ListItem item in lbAvail.Items)
{
ClientScript.RegisterForEventValidation(lbSelected.UniqueID, item.Value);
}
// When a user removes an item from the Selected listbox, we put it into the
// Available listbox, so we have to register it here, or we'll get an error.
foreach (ListItem item in lbSelected.Items)
{
ClientScript.RegisterForEventValidation(lbAvail.UniqueID, item.Value);
}
base.Render(writer);
}
Here's some explanations:
Remember the "Invalid postback or callback argument" error that I got after moving items from one listbox to the other, then posting the page? This error is actually a clue to the fact that ASP.NET does indeed include the modified listbox contents in the postback data.
In order to access the modified listbox items, all we need is this line:
string[] selectedValues = Request.Form.GetValues(lbSelected.UniqueID);
This gives us an array of the values in the Selected listbox. We don’t need to use a hidden field or textbox or whatever. Since the data is being sent back to us, we might as well use it. Note that these are the values, not the display strings.
One important note: Only the selected items in a listbox are sent back. So up in the markup code, you'll see that when the Submit button is clicked, I call some javascript code that selects all the items in the Selected listbox (I don’t care what's in the Available listbox).
So how do we prevent the "Invalid postback or callback argument" error, so we can actually use this data? We have to tell ASP.NET what values are valid for each listbox. We do this using "ClientScript.RegisterForEventValidation", in the "Render()" method.
ASP.NET already knows that the values we originally placed in each listbox are valid values to return. So all we have to do is register the other values that might be returned. That's what I'm doing in the "Register" method above.
As for the Javascript code, it should be fairly self-explanatory. Just remember it's actually calling some JQuery functions, so you obviously have to include Jquery.
I hope this helps. It took me a while to figure it all out, and like I said, I couldn't find one place with all the answers. Of course, as soon as I post this, 15 people will point out where I could have found this information easily, but that's life.
I have two textboxes, ID and Name. Based on the combination of these two, my address dropdown should get populated.
I have written an sp to bind items to my dropdown. But the text_changed event of Name textbox causes AutoPostback. I want my dropdownlist to bind automatically when I leave the textbox without causing any refreshing of the page. On page load all 3 should be blank.
Please let me know how to accomplish this using javascript and jquery.
Please excuse for any typing mistakes.
You could either user jQuery Ajax or UpdatePanel
Using JQuery Ajax...
$.ajax({
data: data,
url: "ur url",
type: "POST",
contentType: "application/json; charset=utf-8",
success: function (result) {
//ur code to bind data to dropdown
}
error: function OnError(result) {
//ur code
}
});
Using Update Panel you can call server side method to bind data to dropdown
<asp:UpdatePanel ID="UpdatePanel1" UpdateMode="Conditional" runat="server">
<ContentTemplate>
<asp:TextBox ID="txtBox1" OnTextChanged="txtBox1_TextChanged" AutoPostBack="true" runat="server" />
</ContentTemplate>
</asp:UpdatePanel>
I have looked at other questions and answers and still cant seem to solve my issue.
I am using jQuery UI tabs with 3 tabs: Basic Info, Payment, Confirmation
What I want to do is pass the values on the basic information tab textbox's, and payment tab into a label that is on the confirmation tab
ASPX:
<ul>
<li>Basic Information</li>
<li>Payment Information</li>
<li>Confirmation</li>
</ul>
<div id="tabs-1">
<asp:Label runat="server" ID="lblFirstName" Text="First Name" />
<asp:TextBox runat="server" ID="tbFirstName" AutoPostBack="true"/>
<asp:Button runat="server" ID="btnContinue" Text="Continue" CssClass="nexttab" />
</div>
<div id="tabs-2">
</div>
<div id="tabs-3">
<asp:Label runat="server" ID="lblConfirmFirstTitle" Text="First Name" />
<asp:Label runat="server" ID="lblConfirmFirst" />
</div>
</div>
Script in head:
$("#btnContinue").click(function () {
$("#lblConfirmFirst").html($("#tbFirstName.").val());
});
The next tab function works fine, and the values retain in the textbox when the next tab is selected, however it wont pass to the label on the 3rd tab.
I think the problem is asp.net rename your server controls if the page has a master page.
add the attribute: ClientIDMode="Static" to your asp controls.
As you are using the ASP.NET controls, the ID will get changed while rendered. Use the jQuery Ends With selector for selecting the elements.
Use jQuery text method for ASP.NET Labels (i.e. html span), and jQuery val method for ASP.NET Toolboxes (i.e. html input).
Also, as the button is ASP.NET button, it gets post back every time you clickit, so use return false; at the end of button click method.
You have a AutoPostBack="true" for textbox, which might also create problem by resetting the label text while post back.
Below code will work if AutoPostBack="true" is removed from textbox.
$(document).ready(function () {
$(function () {
$("#tabs").tabs();
});
$("input[id$=btnContinue]").click(function () {
$("span[id$=lblConfirmFirst]").text($("input[id$=tbFirstName]").val());
return false;
});
});
I want the click event of a LinkButton in my GridView to not call the grid view's OnRowCommand event.
The reason is because I want to encapsulate this in an UpdatePanel and update ErrorUpdateTextBox without performing a postback.
<asp:GridView ID="DataSourceMappingGridView" runat="server" DataKeyNames="Index" ClientIDMode="Static" OnRowCommand="DataSourceMappingGridView_RowCommand" OnRowDataBound="DataSourceMappingGridView_RowDataBound">
<Columns>
<asp:TemplateField>
<ItemTemplate>
<asp:UpdatePanel ID="ErrorUpdatePanel" runat="server">
<ContentTemplate>
<asp:LinkButton ID="ErrorTextLinkButton" runat="server" Text='View Errors' OnClick="ErrorTextLinkButton_Click" />
<asp:TextBox ID="ErrorUpdateTextBox" runat="server" Text="">
</ContentTemplate>
</asp:UpdatePanel>
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
If you really want to avoid a post back (UpdatePanels still do a partial post back), then I would recommend capturing the link button's click event client-side and then doing an AJAX call to the server to save/get whatever data is relevant to the link button being clicked, like this:
Remove the OnClick attribute (leave the runat="server" attribute so you can add the ID value to the link button during binding) and add a class attribute to the LinkButton markup to allow jQuery to easily wire up a click event handler, like this:
<asp:LinkButton ID="ErrorTextLinkButton" runat="server" Text='View Errors'
class="error" />
$(document).ready(function() {
$('.error').click(function() {
$.ajax({
type: "POST",
url: "PageName.aspx/GetError",
data: "{'id':'7'}",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(data) {
// Do something error data returned here
}
});
});
});
Finally, create an ASP.NET AJAX Page Method, like this:
[WebMethod]
public static string GetError(int id)
{
// Go and get error data from database or service, etc.
return "Your error message here";
}
I have a GridView with a button called btnShowTradeScreenshot. The GridView creates many buttons and I want to apply the jQuery button to it. Here's my relevant GridView code:
<asp:GridView
ID="grdTrades"
runat="server"
>
<Columns>
<asp:TemplateField HeaderText="Screenshot" >
<ItemTemplate>
<input name="btnShowTradeScreenshot" runat="server" visible='<%# Eval("screenshotId") != DBNull.Value %>' type="button" value='View...' onclick='<%# "ShowImageInNewPage(\"App_Handlers/ImageHandler.ashx?screenshotId=" + Eval("screenshotId") + "\")" %>' />
</ItemTemplate>
</asp:TemplateField>
</Columns>
</asp:GridView>
I am trying to apply the jQuery using this code:
<script type="text/javascript">
$(document).ready(function () { $("#<%= btnShowTradeScreenshot.ClientID %>").button(); });
</script>
Nothing happens and I get the standard button, not the jQuery button. When I look at the page source, I notice that the buttons have mangled names like:
ctl00$contentMain$grdTrades$ctl05$ctl03
ctl00$contentMain$grdTrades$ctl10$ctl03
etc
Any ideas on how to apply the jQuery to all my buttons?
Thanks.
You could use a class name on the buttons instead of relying on the ClientID; that way you save on JavaScript length and don't need to bind to every specific control.
In JS:
// equivalent to $(document).ready(function() {
$(function() {
$('.customButton').button();
});
In ASP.NET:
<input type="button" runat="server" class="customButton" .../>
Also, if your grid is an AJAX-bound grid, the <input/> elements will be recreated and you'll need to rerun the code on the grid to get the styles applied:
$('#<%= grdTrades.ClientID %> .customButton').button();
Try using a name selector instead:
$('input[name$=btnShowTradeScreenshot]').button();
or even better apply a CSS class to your input and then:
$('.button').button();
There are many buttons created and ASP will generate different IDs for them so as to ensure each one has a unique ID. Since $('#...') is aimed at selecting a single uniquely identified element you cannot use it to select multiple buttons.
Try $(':button') to select all button objects. Alternatively you could set each button's class to something like ShowTradeScreenshot and then select them all with $('.ShowTradeScreenshot').