I've got a WebForm with two drop down lists, where the contents of the second one depend on the first.
So if the user changes the category, the second dropdown needs to be filled with the list of subcategories.
This sounds like a typical job for AutoPostBack.
However, there's a bit of a problem with AutoPostBack: if the list isn't dropped open, and the user uses the keyboard to make the choice, the postback happens right after the first keystroke. This prevents the user from scrolling down the list with the down arrow, or typing the name of the category.
This happens in Chrome and IE and Opera, but not in Firefox. Firefox fires the onchange event only when leaving the control (tabbing to the next control), just like it would when the list was dropped open, and that's what I want the other browsers to do too.
Any solutions how I can achieve this?
I tried to remove the AutoPostBack attribute and use onblur, but apparently the page works differently with AutoPostBack than without, because the browsers start complaining about Javascript errors.
Now since we're all so fond of jsFiddle, here's one. It doesn't actually do anything, but it can demonstrate the problem. Click on the first dropdown, then click again to close the list. (This is what happens when you navigate through the form with the tab key: dropdown lists don't open up.) Now type a letter or the down arrow. Firefox changes the current selection and waits for you to do anything else, but Chrome and IE and Opera all attempt to submit the form immediately, with drastic results.
So how can I avoid that? And note that simply changing the fiddle may not be enough, it must be translatable back to an ASP.NET solution.
Ok here is how I'd do it by using ajax and avoiding the use of AutoPostback all together to populate my sub category.
Create an object that represents the select list json object to send back.
public class SelectItem
{
public string Id { get; set; }
public string Text { get; set; }
}
Then create a PageMethod:
[WebMethod]
public static List<SelectItem> GetSubCategories(string Id)
{
// Returning dummy data for demo purposes
var subCats = new List<SelectItem>();
if (Id == "1")
{
subCats.Add(new SelectItem { Id = "1", Text = "1 Subs"});
}
else if (Id == "2")
{
subCats.Add(new SelectItem { Id = "2", Text = "2 Subs"});
}
return subCats;
}
Add a script manager and EnablePageMethods i.e.
<asp:ScriptManager runat="server" EnablePageMethods="true">
</asp:ScriptManager>
Change your dropdown lists to use ClientIDMode="Static"
<asp:DropDownList Id="ddlCategory" runat="server" ClientIDMode="Static">
<asp:ListItem Value ="1" Text ="One"></asp:ListItem>
<asp:ListItem Value ="2" Text ="Two"></asp:ListItem>
</asp:DropDownList>
<asp:DropDownList Id="ddlSubCategory" runat="server" ClientIDMode="Static">
</asp:DropDownList>
Then use the following jQuery:
<script type="text/javascript">
$(function () {
var $cat = $('#ddlCategory');
$cat.click(function () {
var catId = $cat.val();
$.ajax({
type: "POST",
url: "Default.aspx/GetSubCategories",
data: "{ Id: " + catId + " }",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (msg) {
var subs = msg.d;
// empty selection
var $ddlSubCategory = $('#ddlSubCategory');
$ddlSubCategory.empty();
$.each(subs, function (index, sub) {
$ddlSubCategory.append($('<option/>', {
value: sub.Id,
text: sub.Text
}));
});
}
});
});
});
</script>
Related
I have below asp.net Listbox which has been decorated with sumoselect.
After selecting an item in dropdown if user deselects the item (none of the item is selected) then user will be prompted a javascript confirmation box and if user clicks on Cancel button of that, then dropdown will continue to hold the values.
Below is my code.
<td class="ievent" style="width:22%; padding-bottom:10px; padding-right:30px;padding-left:10px;">
<asp:ListBox ID="ListBoxSol" runat="server" SelectionMode="Multiple" CssClass="textbox" Height="22px" Font-Size="Small" Width="230px">
</asp:ListBox>
</td>
if (lastselectedItemIndex == -1) {
var dropselvalue = sessionStorage.getItem("items");
var ans = confirm("If all Internal Solution are de-selected than Solution Revenue value will be saved as 0. Do you want to de-select all?");
if (ans == true) {
$('#LabelSolRev').hide();
$('#TextBoxSolRev').hide();
}
else {
event.preventDefault();
//$("#ListBoxSol option").find('AI Solutions').attr("selected", true);
//$('#ListBoxSol option').(":checked");
$("#ListBoxSol option").each(function () {
if ($(this).html() == "AI Solutions") {
$(this).prop("selected", true);
//$(this).checked(true);
$(this).removeClass("opt");
$(this).addClass("selected opt");
$(this).trigger("click");
return false;
}
});
}
}
But in my case multiselect dropdown is unable to hold the values after deselect by using the code given. Can anyone please suggest how to handle this scenario?
If I have understood your question right, you want to select multiple options from asp:ListBox control and upon deselecting all of the options you want to keep their record so that you can re-select them in some case.
First, the asp:ListBox control is not selected like this: $("#ListBoxSol option").
As this control runs at the server, some additional information is prepended to it. This will result in its id becoming something like: #MainContent_ListBoxSol.
To select the asp:ListBox control you need to replace $("#ListBoxSol option") with $("#<%= ListBoxSol.ClientID %> option"). You can read more about selecting asp.net controls in jQuery here.
Secondly, to retain the selected values you can store them in an array, say 'values'.
var values = [];
$("#<%= ListBoxSol.ClientID %> option:selected").each(function (i, option) {
values[i] = option;
});
Later you can traverse the array and re-select the options.
$.each(values, function (index, option) {
$("#<%= ListBoxSol.ClientID %> option[value='" + option.value + "']").prop('selected', true);
});
Hope it helps.
I have a label which I show numbers in and numbers are the count of data from database. Whenever new data is saved in DB, the number in label should increase. It increases when i refresh the page, because I call the method on page_load in codebehind.
What in my mind is that:
I should call the method periodically without refreshing the page.
For this purpose, I know that I should use AJAX, but I couldnt find appropriate usage for me.
Can you direct me to a solution?
This is the label on aspx page:
<div id="panoramCouponBarLittleTalep">
<asp:Label ID="LabelTalepSayisiSag" ClientIDMode="Static" Text="" runat="server" />
</div>
This is the page_load :
protected void Page_Load(object sender, EventArgs e)
{
if (Session["user"] != null)
{
if (!IsPostBack)
{
SonBesGunTalepSayisi();
}
}
}
And the method I use:
private void SonBesGunTalepSayisi()
{
RequestProvider rP = new RequestProvider();
int talepSayisi = rP.LastFiveDaysRequestCount();
if (talepSayisi > 0)
{
LabelTalepSayisiSag.Text = talepSayisi.ToString();
}
else
{
LabelTalepSayisi.Text = "";
}
}
I would use an ASP.NET AJAX Page Method to be the server endpoint that your JavaScript AJAX will call, based upon a timer; like this:
Code-behind:
[WebMethod]
public static string GetRequestCount()
{
RequestProvider rP = new RequestProvider();
int talepSayisi = rP.LastFiveDaysRequestCount();
if (talepSayisi > 0)
{
return talepSayisi.ToString();
}
return "";
}
I would add a CssClass value to your Label control as that will make it easier to use in a jQuery selector, like this:
Markup:
<div id="panoramCouponBarLittleTalep">
<asp:Label ID="LabelTalepSayisiSag" ClientIDMode="Static"
Text="" runat="server" CssClass="TheLabel" />
</div>
$(document).ready(function() {
setInterval(ajaxCall, 5000); // 5000 MS == 5 seconds
});
function ajaxCall() {
$.ajax({
type: "POST",
url: "PageName.aspx/GetRequestCount",
data: "{}",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function(result) {
// Put result of call into label
$('.TheLabel').text(result);
}
});
}
Note: Obviously you can adjust the timer interval value to be larger or smaller, depending upon your needs and performance. Also, the CssClass value on the Label control avoids the name mangling issue that plagues ASP.NET server controls. Even though you are using the ClientIDMode="Static", I like to avoid using the ID of a server control in jQuery when possible, because a class name will not get mangled by ASP.NET.
You can use ASP Page Methods to do it:
http://www.geekzilla.co.uk/View7B75C93E-C8C9-4576-972B-2C3138DFC671.htm
This is a problem I haven't come across before.
I'm working on an MVC4 project. I'm using an asp button control because there isn't a Html Helper that can be used for a button (re: There's no #Html.Button !). My button code is:
<td><asp:Button ID="ButtonUndo" runat="server" Text="Undo"
OnClick="ButtonUndo_Click" AutoPostBack="true"/></td>
I went to the Designer tab and clicked on this button which produced the event handler:
protected void ButtonUndo_Click(object sender, EventArgs e)
{
RRSPSqlEntities db = new RRSPSqlEntities();
int id = (int)ViewData["ClientId"];
var updateAddress = (from a in db.Address
where a.PersonId == id
select a).SingleOrDefault();
updateAddress.Deleted = false;
db.SaveChanges();
}
I should add that this code was added to the same .aspx page wrapped in a script tag. Also within this section is the Page_Load method. The eventhandler is not within Page_Load.
The problem was found when I set a breakpoint and stepped through the code. Clicking my button shows that it doesn't hit my event handler at all. I don't know why this is, particularly as ASP created the event from clicking the button in Design mode.
Clicking my button shows that it doesn't hit my event handler at all.
This isn't all that surprising. ASP.NET MVC uses a completely different event model (i.e. it doesn't have one like web forms). However, what you're trying to do is very straight forward. In your controller build a new method, let's call it Undo:
public ActionResult Undo(int id)
{
RRSPSqlEntities db = new RRSPSqlEntities();
var updateAddress = (from a in db.Address
where a.PersonId == id
select a).SingleOrDefault();
updateAddress.Deleted = false;
db.SaveChanges();
return View("{insert the original action name here}");
}
and then in your markup, simply markup the input like this:
<form method="POST" action="/ControllerName/Undo">
#Html.HiddenFor(Model.Id)
<input type="submit" value="Undo" />
</form>
where the Model for the View you're on contains a property, I've called it Id, that is the id you want passed into Undo.
I usually prefer to make ajax calls. You can try:
<button type="button" class="button" onclick="ButtonUndo();" />
In the form:
<script>
function ButtonUndo() {
$.ajax({
type: 'POST',
url: '/controller/action',
data: 'PersonID=' + ID,
dataType: 'json',
cache: false,
success: function (result) {
//do stuff here
},
error: function () {
//do error stuff here
}
});
}
</script>
Controller:
[HttpPost]
public ActionResult Action(int PersonID)
{
//Do your stuff here
return new JsonResult { result = "something" };
}
(Sorry for any typos or syntax errors...I pulled from existing code that we use in a project.)
I have two select lists in my ASP.NET site that are filled by the server with some elements.
// .aspx
<asp:dropdownlist id="abc" runat="server"></asp:dropdownlist>
<asp:dropdownlist id="def" runat="server"></asp:dropdownlist>
// .aspx.cs
abc.Items.Add(new ListItem("element1", "value1"));
def.Items.Add(new ListItem("element1", "value1"));
Due to too complicated reasons to explain right now, I also need to modify the options of the select lists with JavaScript, adding some values.
// In the <head> of the .aspx page
var abcList = document.getElementById("abc");
var defList = document.getElementById("def");
var newAbcElement = new Option("element2", "value2", false, false);
var newDefElement = new Option("element2", "value2", false, false);
abcList.options[abcList.length] = newAbcElement;
defList.options[defList.length] = newDefElement;
Of course, this will mess up Even Validation as soon as I send the form back to the server (be it by submitting or as a PostBack from some other form elements with AutoPostBack="true").
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.
Now, I don't have the resources and budget to completely overhaul the whole page design, so: What is the fastest and easiest way to change the dropdownlist that does not mean I have to rewrite the whole thing?
So that the values added via JavaScript are recognized by my CodeBehind file when submitting the form?
Ok, here is one more option for you. You can add those items to your lists using AsyncPostBackTrigger.
Some hidden fields:
<asp:TextBox ID="newItemsForAbc" runat="server" style="display:none;"></asp:TextBox>
<asp:TextBox ID="newItemsForDef" runat="server" style="display:none;"></asp:TextBox>
<asp:Button ID="addNewItems" runat="server" OnClick="addNewItems_Click"
style="display:none;" />
The Update Panel:
<asp:UpdatePanel runat="server" ID="UpdatePanel" UpdateMode="Conditional">
<ContentTemplate>
<asp:dropdownlist id="abc" runat="server"></asp:dropdownlist>
<asp:dropdownlist id="def" runat="server"></asp:dropdownlist>
</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID="addNewItems" EventName="Click" />
</Triggers>
</asp:UpdatePanel>
JS Function for doing an async post back:
<script type="text/javascript"> function UpdateStuff(value1, value2)
{
var abcItems = document.getElementById("<%= newItemsForAbc.ClientID %>");
var defItems = document.getElementById("<%= newItemsForDef.ClientID %>");
abcItems.value=value1;
defItems.value=value2;
__doPostBack("<%= addNewItems.ClientID %>","");
}
</script>
Server-side function that handles button click:
protected void addNewItems_Click(object sender, EventArgs e)
{
string[] n1 = newItemsForAbc.Text.Split(';');
string[] n2 = newItemsForDef.Text.Split(';');
foreach(string i in n1)
{
abc.Items.Add(new ListItem(i, i));
}
foreach(string i in n2)
{
def.Items.Add(new ListItem(i,i));
}
}
To Update Your Lists:
var newAbcElements = "Element1;Element2;Element3";
var newDefElements = "Element4;Element5";
UpdateStuff(newAbcElements, newDefElements);
Final note:
This piece of code probably will not do the job for you as it is. You may need to change the way you store new items in a string, thus splitting/parsing would change too. You may even need different strings for displaying a list item and its actual value. But I believe you get the basic idea.
You can disable the ViewState entirely for the DropDownList.
<asp:dropdownlist id="abc" runat="server" EnableViewState="false"></asp:dropdownlist>
To your question update:
This changes the question quite a lot. The new question is answered here: Invalid postback or callback argument. Event validation is enabled using '<pages enableEventValidation="true"/>'
You have a few options.
First, you might rewrite your code so that the server side generates all possible items for the DropDownList and then in your JavaScript remove the unneeded items instead of adding new ones.
Second option is to create a custom class derived from System.Web.UI.WebControls.DropDownList. The class should contain the one method shown below. The important piece is that your custom class will not have the System.Web.UI.SupportsEventValidationAttribute added to it - so DropDownList base methods will skip the event validation automatically. Now replace the usage from <asp:dropdownlist> to your method.
In case you can't modify the .aspx code (or you have a ton of dropdownlists to replace) you might use tag mapping in your configuration.
namespace Project.MyWebControls
{
public class MyDropDownList : System.Web.UI.WebControls.DropDownList
{
protected override bool LoadPostData(string postDataKey, NameValueCollection postCollection)
{
if (base.LoadPostData(postDataKey, postCollection))
return true;
// this means that the value selected was not present in the .Items collection
string[] values = postCollection.GetValues(postDataKey);
if (values == null || values.Length == 0)
return false;
// add the value to the Items collection so that it can be processed later on.
this.Items.Add(new ListItem("Custom value created by JavaScript", values[0]));
this.SetPostDataSelection(this.Items.Count - 1);
}
}
}
Note that depending on your code you might want to remove these custom values from the Items collection before rendering.
Sample for the .aspx file:
<%# Register TagPrefix="my" Namespace="Project.MyWebControls" Assembly="Project" %>
<my:MyDropDownList runat="server" ...></my:MyDropDownList>
I have an ASP.NET web app that is using JQuery autocomplete to build a nice dynamic combobox. One of the boxes on the page fires a change event that reloads another box. Basically like a UserGroup / Members scenario. My change event fires and repopulates the underlying select box, I then do a remove on the input and button that build the combobox - which all work great up to this point. My last line is to call the combobox method on the newly repopulated select which doesn't seem to fire ? The standard select shows with the new data but no JQuery Goodness. Any help is appreciated. Thanks.
On Change Event:
function GetAnalysts() {
$.ajax({
type: "POST",
url: "GetAnalystByGroup.ashx",
data: 'group=' + $("#<%=supportGroup.ClientID%>" + " option:selected").text(),
success: function (response) {
var analysts = eval(response);
$("#<%=assignedAnalyst.ClientID%>").children().remove();
$("#<%=assignedAnalyst.ClientID%>").append($('<option></option>').val('').html(firstoption));
for (var i = 0; i < analysts.length; i++) {
var text = analysts[i]['label'];
var val = analysts[i]['upn'];
$("#<%=assignedAnalyst.ClientID%>").append($('<option></option>').val(val).html(text));
}
//remove the JQ Combo then rebuild it
$("#<%=assignedAnalyst.ClientID%>JQ").remove();
$("#<%=assignedAnalyst.ClientID%>JQBut").remove();
$("#<%=assignedAnalyst.ClientID%>").show();
$("#<%=assignedAnalyst.ClientID%>").combobox();
},
error: function () {
}
});
}
I ended up working around the problem by coding the remove/show lines into a javascript pageload event and moving the AJAX call inside an update panel.