Wednesday, March 28, 2012

Multiple UpdatePanels cause Validation controls to Fail

I'm new to web development in general, and have run into a problem with the UI I'm trying to design. I'm sure that I could lay out the UI in a different fashion to achieve the same ends, but am trying to maintain a simple one-page UI using AJAX for the simple task of entering one word description lookups into a table called TestLookups with only two varchar fields called Category and Description, neither of which allow NULLs. I am trying to use built-in ASP/AJAX controls and functionality as much as possible in order to limit my need to write client-side javascript which I have no experience in.

My page is simple: A DropDownList (and a DataSource for filling it) for selecting the Category of lookups to be entered. A DetailsView whose DefaultMode is Insert for adding new records to the current Category. And a GridView for listing current lookups from the selected Category, along with providing functionality for Editing and Deleting. I have a second DataSource on the page which filters the TestLookups table to the current Category and which serves both the DetailsView and GridView for database access.

As part of this, I simply want to make sure that when users enter a new lookup or edit an existing one, that they not be allowed to leave the description blank; for this I am trying to use RequiredFieldValidators.

The form initially works fine, including appropriate behaviour for the validators. The only exception is that if I edit an item in the GridView, the validator for the field in the DetailsView fires as well, which is undesirable. A perfect place for partial rendering, I think! So I add two update panels, one around the GridView with a trigger to fire when the DropDownList changes, and a second one around the DetailsView to separate its validation from whatever occurs in the GridView.

Now we come to my problems...

First... At this point the DetailsView works fine for data entry, with the exception that the validator only works if it's the first thing that gets fired on the form. For example, I fire up the form and click Insert right off the bat and the validator fires; however, if I fire up the form and do anything else (i.e. change the DropDownList, Delete an item, Edit an item, etc.), then the validator won't fire! Not sure why this is happening...

Second... The Edit functionality won't work! The Edit button gets me into edit mode, I type in my changes, but when I click Update, nothing happens! If I change the Update button's CasesValidation property to False, then it magically starts working, but then of course I've lost my ability to validate the textbox with the RequiredFieldValidator!

If I ignore my desire for required field validation messages, then I'm OK: the DetailsView simply won't do anything when Insert is clicked (fine behaviour) and the GridView will return an error about an invalid NULL value (not a great behaviour, but it's an internal website and my users would be able to figure it out).

I'm hoping someone will tell me what I can do to properly hook these pieces together, or let me know if I'm simply trying to do too much on a single web page (doesn't seem like it though). If there are any workarounds for this out there, I haven't seen anyone talking about them.

I'll include my simple web page below (the final non-working version that I think should work). All anyone needs to do to get this working is create the simple table I described and set themselves up with a Connection.

Thanks for any help anyone can provide!

- Ronn

Source Code:

<%@dotnet.itags.org. Page Language="VB" AutoEventWireup="false" CodeFile="zzzTest1.aspx.vb" Inherits="zzzTest1" %><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml" ><head id="Head1" runat="server"> <title>Untitled Page</title></head><body> <form id="form1" runat="server"> <div> <asp:SqlDataSource ID="dsTestLookupCategories" runat="server" ConnectionString="<%$ ConnectionStrings:LabTestingConnectionString%>" SelectCommand="SELECT DISTINCT Category FROM TestLookups ORDER BY Category"></asp:SqlDataSource> <asp:SqlDataSource ID="dsTestLookups" runat="server" ConflictDetection="CompareAllValues" ConnectionString="<%$ ConnectionStrings:LabTestingConnectionString%>" DeleteCommand="DELETE FROM TestLookups WHERE Category = @dotnet.itags.org.Category AND Description = @dotnet.itags.org.Original_Description" InsertCommand="INSERT INTO TestLookups (Category, Description) VALUES (@dotnet.itags.org.Category, @dotnet.itags.org.Description)" OldValuesParameterFormatString="Original_{0}" SelectCommand="SELECT Description FROM TestLookups WHERE (Category = @dotnet.itags.org.Category) ORDER BY Description" UpdateCommand="UPDATE TestLookups SET Description = @dotnet.itags.org.Description WHERE Category = @dotnet.itags.org.Category AND Description = @dotnet.itags.org.Original_Description"> <SelectParameters> <asp:ControlParameter ControlID="ddlCategory" Name="Category" PropertyName="SelectedValue" Type="String" /> </SelectParameters> <InsertParameters> <asp:ControlParameter ControlID="ddlCategory" Name="Category" PropertyName="SelectedValue" Type="String" /> <asp:Parameter Name="Description" Type="String" /> </InsertParameters> <UpdateParameters> <asp:ControlParameter ControlID="ddlCategory" Name="Category" PropertyName="SelectedValue" Type="String" /> <asp:Parameter Name="Description" Type="String" /> <asp:Parameter Name="Original_Description" Type="String" /> </UpdateParameters> <DeleteParameters> <asp:ControlParameter ControlID="ddlCategory" Name="Category" PropertyName="SelectedValue" Type="String" /> <asp:Parameter Name="Original_Description" Type="String" /> </DeleteParameters> </asp:SqlDataSource> </div> <asp:ScriptManager ID="ScriptManager" runat="server" EnablePartialRendering="True"> </asp:ScriptManager> <asp:DropDownList ID="ddlCategory" runat="server" AutoPostBack="True" DataSourceID="dsTestLookupCategories" DataTextField="Category" DataValueField="Category" Width="175px"> </asp:DropDownList><br /> <br /> <asp:UpdatePanel ID="UpdatePanel2" runat="server"> <ContentTemplate>   <asp:DetailsView ID="dvTestLookups" runat="server" AutoGenerateRows="False" DataSourceID="dsTestLookups" DefaultMode="Insert"> <Fields> <asp:TemplateField HeaderText="Add New Lookup" ShowHeader="False" SortExpression="Description"> <InsertItemTemplate> <asp:TextBox ID="txtDescriptionInsert" runat="server" Text='<%# Bind("Description")%>' Width="165px"></asp:TextBox><br /> <asp:RequiredFieldValidator ID="rfvDescription" runat="server" ControlToValidate="txtDescriptionInsert" CssClass="validationtext" Display="Dynamic" ErrorMessage="Lookup text is required"></asp:RequiredFieldValidator> </InsertItemTemplate> <ItemStyle BackColor="#9CBACE" /> </asp:TemplateField> <asp:TemplateField ShowHeader="False"> <InsertItemTemplate> <asp:Button ID="btnInsert" runat="server" CausesValidation="True" CommandName="Insert" Text="Insert" Width="60px" /> <asp:Button ID="btnCancel" runat="server" CausesValidation="False" CommandName="Cancel" Text="Cancel" Width="60px" /> </InsertItemTemplate> <ItemStyle BackColor="#9CBACE" HorizontalAlign="Left" /> </asp:TemplateField> </Fields> </asp:DetailsView> </ContentTemplate> </asp:UpdatePanel>   <br /> <asp:UpdatePanel ID="UpdatePanel1" runat="server"> <ContentTemplate>  <asp:GridView ID="gvTestLookups" runat="server" AllowPaging="True" AutoGenerateColumns="False" DataSourceID="dsTestLookups" PageSize="15" Width="550px"> <Columns> <asp:TemplateField HeaderText="Current Lookups" SortExpression="Description"> <EditItemTemplate> <asp:TextBox ID="txtDescription" runat="server" Text='<%# Bind("Description")%>' Width="425px"></asp:TextBox><br /> <asp:RequiredFieldValidator ID="rfvDescription" runat="server" ControlToValidate="txtDescription" CssClass="validationtext" Display="Dynamic" ErrorMessage="Lookup text is required"></asp:RequiredFieldValidator> </EditItemTemplate> <ItemTemplate> <asp:Label ID="lblDescription" runat="server" Text='<%# Bind("Description")%>'></asp:Label> </ItemTemplate> </asp:TemplateField> <asp:TemplateField ShowHeader="False"> <EditItemTemplate> <asp:LinkButton ID="btnUpdate" runat="server" CausesValidation="True" CommandName="Update" Text="Update"></asp:LinkButton> <asp:LinkButton ID="btnCancel" runat="server" CausesValidation="False" CommandName="Cancel" Text="Cancel"></asp:LinkButton> </EditItemTemplate> <ItemTemplate> <asp:LinkButton ID="btnEdit" runat="server" CausesValidation="False" CommandName="Edit" Text="Edit"></asp:LinkButton> <asp:LinkButton ID="btnDelete" runat="server" CausesValidation="False" CommandName="Delete" Text="Delete"></asp:LinkButton> <ajaxToolkit:ConfirmButtonExtender ID="cbeDelete" runat="server" ConfirmText="You are about to delete the current lookup. Are you sure?" TargetControlID="btnDelete"> </ajaxToolkit:ConfirmButtonExtender> </ItemTemplate> <ItemStyle HorizontalAlign="Right" Width="100px" /> </asp:TemplateField> </Columns> </asp:GridView> </ContentTemplate> <Triggers> <asp:AsyncPostBackTrigger ControlID="ddlCategory" EventName="SelectedIndexChanged" /> </Triggers> </asp:UpdatePanel>  <br />   </form></body></html>

The proper way to isolate validation areas in a page is to use the ValidationGroup property on controls. Set one validationGroup for the controls and validators in the detailsView, and another for the controls and validators in the gridView. You shouldn't need to use UpdatePanels.
This worked great... Thanks!
Validators are not compatable with final version of AJAX
http://weblogs.asp.net/scottgu/archive/2007/01/25/links-to-asp-net-ajax-1-0-resources-and-answers-to-some-common-questions.aspx

No comments:

Post a Comment