From 7f4315bde39d8094f59970114ae9a270e9e199b2 Mon Sep 17 00:00:00 2001 From: eeichinger Date: Fri, 17 Apr 2009 13:32:28 +0000 Subject: [PATCH] fixed SPRNET-1118, updated refdocs --- doc/reference/src/index.xml | 18 +- doc/reference/src/validation.xml | 138 +++++------ .../UI/Controls/AbstractValidationControl.cs | 214 ++++++++++++------ src/Spring/Spring.Web/Web/UI/Page.cs | 7 +- src/Spring/Spring.Web/Web/UI/UserControl.cs | 7 +- .../AbstractValidationControlTests.cs | 27 ++- 6 files changed, 259 insertions(+), 152 deletions(-) diff --git a/doc/reference/src/index.xml b/doc/reference/src/index.xml index 315e22d7..c6370f6c 100644 --- a/doc/reference/src/index.xml +++ b/doc/reference/src/index.xml @@ -1,4 +1,4 @@ - + @@ -53,16 +53,16 @@ ]> - The Spring.NET Framework Reference Documentation - Version 1.2.0 - Last Updated Novermber 10, 2008 (Latest documentation) + Version 1.2.1 + Last Updated April xx, 2009 (Latest documentation) Mark @@ -80,6 +80,10 @@ Bruno Baia + + Erich + Eichinger + Federico Spinazzi diff --git a/doc/reference/src/validation.xml b/doc/reference/src/validation.xml index 9945c2a1..5dfeac31 100644 --- a/doc/reference/src/validation.xml +++ b/doc/reference/src/validation.xml @@ -692,7 +692,17 @@ bool userInfoIsValid = userInfoValidator.Validate(userInfo, errors); </object> <v:group id="tripValidator"> - // our validation rules + <v:required id="departureAirportValidator" test="StartingFrom.AirportCode"> + <!-- write error message to 2 providers --> + <v:message id="error.departureAirport.required" providers="departureAirportErrors, errorSummary"/> + </v:required> + + <v:group id="destinationAirportValidator"> + <v:required test="ReturningFrom.AirportCode"> + <!-- write error message to 2 providers --> + <v:message id="error.destinationAirport.required" providers="destinationAirportErrors, errorSummary"/> + </v:required> + </v:group> </v:group> </objects> @@ -719,83 +729,30 @@ bool userInfoIsValid = userInfoValidator.Validate(userInfo, errors); <spring:validationSummary/> controls to the ASP.NET form: - <%@ Page Language="c#" MasterPageFile="~/Web/StandardTemplate.master" Inherits="TripForm" CodeFile="TripForm.aspx.cs" %> -<%@ Register TagPrefix="spring" Namespace="Spring.Web.UI.Controls" Assembly="Spring.Web" %> - -<asp:Content ID="head" ContentPlaceHolderID="head" runat="server"> - - <script language="javascript" type="text/javascript"> - <!-- - function showReturnCalendar(isVisible) - { - document.getElementById('<%= returningOnDate.ClientID %>').style.visibility = isVisible? '': 'hidden'; - document.getElementById('returningOnCalendar').style.visibility = isVisible? '': 'hidden'; - } - --> - </script> - -</asp:Content> - -<asp:Content ID="body" ContentPlaceHolderID="body" runat="server"> - <div style="text-align: center"> - <h4><asp:Label ID="caption" runat="server"></asp:Label></h4> - <spring:ValidationSummary ID="validationSummary" runat="server" /> + <!-- code snippet taken from the SpringAir sample application --> +<%@ Page Language="c#" MasterPageFile="~/Web/StandardTemplate.master" Inherits="TripForm" CodeFile="TripForm.aspx.cs" %> + + <!-- render all error messages sent to 'errorSummary' provider --> + <spring:ValidationSummary ID="summary" Provider="errorSummary" runat="server" /> <table> - <tr class="formLabel"> - <td>&nbsp;</td> - <td colspan="3"> - <spring:RadioButtonGroup ID="tripMode" runat="server"> - <asp:RadioButton ID="OneWay" onclick="showReturnCalendar(false);" runat="server" /> - <asp:RadioButton ID="RoundTrip" onclick="showReturnCalendar(true);" runat="server" /> - </spring:RadioButtonGroup> - </td> - </tr> <tr> - <td class="formLabel" align="right"> + <td> <asp:Label ID="leavingFrom" runat="server" /></td> - <td nowrap="nowrap"> + <td> <asp:DropDownList ID="leavingFromAirportCode" AutoCallBack="true" runat="server" /> - <spring:ValidationError id="departureAirportErrors" runat="server" /> + <!-- render error messages sent to 'departureAirportErrors' provider --> + <spring:ValidationError ID="leavingFromError" Provider="departureAirportErrors" runat="server" /> </td> - <td class="formLabel" align="right"> + <td> <asp:Label ID="goingTo" runat="server" /></td> - <td nowrap="nowrap"> + <td> <asp:DropDownList ID="goingToAirportCode" AutoCallBack="true" runat="server" /> - <spring:ValidationError id="destinationAirportErrors" runat="server" /> + <!-- render error messages sent to 'destinationAirportErrors' provider --> + <spring:ValidationError ID="goingToError" Provider="destinationAirportErrors" runat="server" /> </td> </tr> - <tr> - <td class="formLabel" align="right"> - <asp:Label ID="leavingOn" runat="server" /></td> - <td nowrap="nowrap"> - <spring:Calendar ID="leavingFromDate" runat="server" Width="75px" AllowEditing="true" Skin="system" /> - <spring:ValidationError id="departureDateErrors" runat="server" /> - </td> - <td class="formLabel" align="right"> - <asp:Label ID="returningOn" runat="server" /></td> - <td nowrap="nowrap"> - <div id="returningOnCalendar"> - <spring:Calendar ID="returningOnDate" runat="server" Width="75px" AllowEditing="true" Skin="system" /> - <spring:ValidationError id="returnDateErrors" runat="server" /> - </div> - </td> - </tr> - <tr> - <td class="buttonBar" colspan="4"> - <br/> - <asp:Button ID="findFlights" runat="server"/></td> - </tr> </table> - </div> - - <script language="javascript" type="text/javascript"> - if (document.getElementById('<%= tripMode.ClientID %>').value == 'OneWay') - showReturnCalendar(false); - else - showReturnCalendar(true); - </script> - -</asp:Content> +
Rendering Validation Errors @@ -927,5 +884,50 @@ bool userInfoIsValid = userInfoValidator.Validate(userInfo, errors); It's as simple as that!
+ +
+ How Validate() and Validation Controls play together + + Validation Controls (ValidationSummary, ValidationError) need to + somehow get the list of errors collected during validation. Both, + Spring.Web.UI.Page and Spring.Web.UI.UserControl come with a + ValidationErrors property and implement IValidationContainer. + ValidationControls will automatically pick the IValidationContainer + control they are placed on: + + // ASPX / ASCX Template Code +<%@ Control Language="c#"%> + + <!-- render all error messages sent to 'errorSummary' provider --> + <spring:ValidationSummary ID="summary" Provider="errorSummary" runat="server" /> + + <asp:DropDownList ID="leavingFromAirportCode" AutoCallBack="true" runat="server" /> + <!-- render error messages sent to 'departureAirportErrors' provider --> + <spring:ValidationError ID="leavingFromError" Provider="departureAirportErrors" runat="server" /> + + +<script language="C#" runat="server"> +public void SearchForFlights(object sender, EventArgs e) +{ + if (Validate(Controller.Trip, tripValidator)) + { + Process.SetView(Controller.SearchForFlights()); + } +} +</script> + +If you need to render errors from a UserControl not in the + hierarchy of your Validation control, you can specify the name of the + target validation container control: + + // ASPX / ASCX Template Code +<%@ Page Language="c#" %> +<%@ Register TagPrefix="user" TagName="EmployeeInfoEditor" Src="EmployeeInfoEditor.ascx" %> + + <spring:ValidationSummary ID="summary" Provider="summary" ValidationContainerName="editor" runat="server" /> + <user:EmployeeInfoEditor ID="editor" runat="server" /> + + +
diff --git a/src/Spring/Spring.Web/Web/UI/Controls/AbstractValidationControl.cs b/src/Spring/Spring.Web/Web/UI/Controls/AbstractValidationControl.cs index 49581020..f92ea067 100644 --- a/src/Spring/Spring.Web/Web/UI/Controls/AbstractValidationControl.cs +++ b/src/Spring/Spring.Web/Web/UI/Controls/AbstractValidationControl.cs @@ -18,20 +18,14 @@ #endregion -#region Imports - using System; using System.Collections; -using System.Diagnostics; using System.Web.UI; -using System.Web.UI.WebControls; using Spring.Context; using Spring.Util; using Spring.Validation; using Spring.Web.UI.Validation; -#endregion - namespace Spring.Web.UI.Controls { /// @@ -40,9 +34,11 @@ namespace Spring.Web.UI.Controls /// Erich Eichinger public abstract class AbstractValidationControl : Control { - private string provider; - private string validationContainerName; + private string _provider; + private string _validationContainerName; private IValidationErrorsRenderer renderer; + private IValidationErrors _validationErrors; + private IMessageSource _messageSource; #if !NET_2_0 private bool initialized; @@ -70,21 +66,51 @@ namespace Spring.Web.UI.Controls { return this.Site.DesignMode; } - return (this.Context == null) && initialized; + return initialized && (this.Context == null); } } #endif /// - /// If set, will resolve to the named control specified + /// Set a particular message source to be used for + /// resolving error messages to display texts. + /// + /// + /// If not set, the control will probe the control hierarchy + /// for containing controls implementing + /// and use the container's . + /// + public IMessageSource MessageSource + { + get { return _messageSource; } + set { _messageSource = value; } + } + + /// + /// Allows to set a particular instance of the validation errors + /// collection to render. + /// + /// + /// If not set, the control will probe the control hierarchy for + /// containing controls implementing + /// and use the container's + /// + public IValidationErrors ValidationErrors + { + get { return _validationErrors; } + set { _validationErrors = value; } + } + + /// + /// If set, will resolve to the named control specified /// by this property. The behavior of name resolution is identical to /// , except that if the name /// starts with "::", the resolution will start at the page level instead of relative to this /// control /// - public virtual string ValidationContainerName + public string ValidationContainerName { - get { return validationContainerName; } - set { validationContainerName = value; } + get { return _validationContainerName; } + set { _validationContainerName = value; } } /// @@ -95,20 +121,20 @@ namespace Spring.Web.UI.Controls { get { - if (this.provider == null) + if (this._provider == null) { - this.provider = this.ID; - if (this.provider == null) + this._provider = this.ID; + if (this._provider == null) { - this.provider = string.Empty; + this._provider = string.Empty; } } - return this.provider; + return this._provider; } set { AssertUtils.ArgumentNotNull(value, "Provider"); - this.provider = value; + this._provider = value; } } @@ -137,17 +163,6 @@ namespace Spring.Web.UI.Controls } } - /// - /// Gets the MessageSource to be used for resolve error messages - /// - /// - /// By default, returns 's MessageSource. - /// - protected virtual IMessageSource MessageSource - { - get { return ValidationContainer == null ? null : ValidationContainer.MessageSource; } - } - /// /// Create the default /// for this ValidationControl if none is configured. @@ -155,49 +170,109 @@ namespace Spring.Web.UI.Controls protected abstract IValidationErrorsRenderer CreateValidationErrorsRenderer(); /// - /// Gets the , who's - /// shall be rendered by this control. + /// Gets the MessageSource to be used for resolve error messages /// - protected virtual IValidationContainer ValidationContainer + /// + /// By default, returns 's MessageSource. + /// + /// the to resolve message texts. May be null + protected virtual IMessageSource ResolveMessageSource() { - get + IMessageSource messageSource = this.MessageSource; + if (messageSource == null) { - // is an explicit container specified? - if (ValidationContainerName != null) - { - Control start = this.NamingContainer; - string containerName = this.ValidationContainerName; - // shall we do a global search? - if (containerName.StartsWith("::")) - { - containerName = containerName.Substring(2); - start = this.Page; - } - IValidationContainer container = start.FindControl(containerName) as IValidationContainer; - if (container == null) - { - throw new ArgumentException(string.Format("Validation Container Control specified by {0} does not exist or does not implement IValidationContainer", this.ValidationContainerName)); - } - return container; - } - - for (Control parent = this.Parent; parent != null; parent = parent.Parent) - { - IValidationContainer container = parent as IValidationContainer; - if (container != null - && container.ValidationErrors != null) - { - return container; - } - } - return null; + IValidationContainer validationContainer = FindValidationContainer(); + messageSource = (validationContainer == null) + ? null + : validationContainer.MessageSource; } + return messageSource; } /// - /// Resolves the 's list of validation errors to a list + /// Gets the list of validation errors to render + /// + /// the to render. May be null + protected virtual IValidationErrors ResolveValidationErrors() + { + IValidationErrors validationErrors = this.ValidationErrors; + + if (validationErrors == null) + { + IValidationContainer container = this.FindValidationContainer(); + if (container != null) + { + validationErrors = container.ValidationErrors; + } + } + return validationErrors; + } + + /// + /// Gets the , who's + /// shall be rendered by this control. + /// + /// + /// First, it tries to resolve the specified , if any. If no explicit name + /// is set, will probe the control hierarchy for controls implementing . + /// + protected virtual IValidationContainer FindValidationContainer() + { + // is an explicit container specified? + if (ValidationContainerName != null && ValidationContainerName.Length > 0) + { + Control start = this.NamingContainer; + string containerName = this.ValidationContainerName; + // shall we do a global search? + if (containerName.StartsWith("::")) + { + containerName = containerName.Substring(2); + start = this.Page; + } + IValidationContainer container = start as IValidationContainer; + if (containerName.Length > 0) + { + container = start.FindControl(containerName) as IValidationContainer; + } + if (container == null) + { + throw new ArgumentException( + string.Format( + "Validation Container Control specified by {0} does not exist or does not implement IValidationContainer", + this.ValidationContainerName)); + } + return container; + } + + for (Control parent = this.Parent; parent != null; parent = parent.Parent) + { + IValidationContainer container = parent as IValidationContainer; + if (container != null + && container.ValidationErrors != null) + { + return container; + } + } + return null; + } + + /// + /// Resolves the list of validation errors either explicitely specified using + /// or obtained from the containing + /// resolved by to a list /// of elements containing the error messages to be rendered. /// + /// + /// + /// The list of validation errors may either be explicitely specified using + /// or will automatically be obtained from the containing resolved by + /// . + /// + /// + /// Error Messages are resolved using either an explicitely specified or the + /// obtained from the validation container. + /// + /// /// a list containing elements. May return null protected virtual IList ResolveErrorMessages() { @@ -210,19 +285,14 @@ namespace Spring.Web.UI.Controls return errorMessages; } - IValidationContainer container = this.ValidationContainer; - if (container == null) - { - return null; - } - - IValidationErrors validationErrors = container.ValidationErrors; + IValidationErrors validationErrors = ResolveValidationErrors(); if (validationErrors == null) { return null; } + IMessageSource messageSource = this.ResolveMessageSource(); - errorMessages = validationErrors.GetResolvedErrors(this.Provider, this.MessageSource); + errorMessages = validationErrors.GetResolvedErrors(this.Provider, messageSource); return errorMessages; } diff --git a/src/Spring/Spring.Web/Web/UI/Page.cs b/src/Spring/Spring.Web/Web/UI/Page.cs index c39449f8..a8fba0f6 100644 --- a/src/Spring/Spring.Web/Web/UI/Page.cs +++ b/src/Spring/Spring.Web/Web/UI/Page.cs @@ -1263,12 +1263,17 @@ namespace Spring.Web.UI } /// - /// Gets the validation errors container. + /// Gets or sets the validation errors container. /// /// The validation errors container. public virtual IValidationErrors ValidationErrors { get { return validationErrors; } + set + { + AssertUtils.ArgumentNotNull(value, "ValidationErrors"); + validationErrors = value; + } } /// diff --git a/src/Spring/Spring.Web/Web/UI/UserControl.cs b/src/Spring/Spring.Web/Web/UI/UserControl.cs index de50ba92..8108fc94 100644 --- a/src/Spring/Spring.Web/Web/UI/UserControl.cs +++ b/src/Spring/Spring.Web/Web/UI/UserControl.cs @@ -683,12 +683,17 @@ namespace Spring.Web.UI } /// - /// Gets the validation errors container. + /// Gets or sets the validation errors container. /// /// The validation errors container. public virtual IValidationErrors ValidationErrors { get { return validationErrors; } + set + { + AssertUtils.ArgumentNotNull(value, "ValidationErrors"); + validationErrors = value; + } } /// diff --git a/test/Spring/Spring.Web.Tests/Web/UI/Controls/AbstractValidationControlTests.cs b/test/Spring/Spring.Web.Tests/Web/UI/Controls/AbstractValidationControlTests.cs index ca2de191..2e252b08 100644 --- a/test/Spring/Spring.Web.Tests/Web/UI/Controls/AbstractValidationControlTests.cs +++ b/test/Spring/Spring.Web.Tests/Web/UI/Controls/AbstractValidationControlTests.cs @@ -63,7 +63,7 @@ namespace Spring.Web.UI.Controls } [Test] - public void ResolvesAndRendersValidationErrors() + public void ResolvesAndRendersValidationErrorsUsingValidationContainer() { TestValidationControl vc = new TestValidationControl(); vc.ID = "TestControl"; @@ -79,6 +79,27 @@ namespace Spring.Web.UI.Controls Assert.AreEqual("Resolved Message Text", vc.LastErrorsRendered[0]); } + [Test] + public void ResolvesAndRendersValidationErrorsUsingExplicitlySpecifiedErrorsAndMessageSource() + { + TestValidationControl vc = new TestValidationControl(); + vc.ID = "TestControl"; + + Page page = new Page(); + page.Controls.Add(vc); + + ValidationErrors errors = new ValidationErrors(); + errors.AddError(vc.Provider, new ErrorMessage("msgId")); + vc.ValidationErrors = errors; + + StaticMessageSource msgSrc = new StaticMessageSource(); + msgSrc.AddMessage("msgId", CultureInfo.CurrentUICulture, "Resolved Message Text"); + vc.MessageSource = msgSrc; + + vc.TestRender(null); + Assert.AreEqual("Resolved Message Text", vc.LastErrorsRendered[0]); + } + [Test] public void ThrowsIfCreateValidationErrorsRendererReturnsNull() { @@ -305,12 +326,12 @@ namespace Spring.Web.UI.Controls public IMessageSource TheMessageSource { - get { return base.MessageSource; } + get { return base.ResolveMessageSource(); } } public IValidationContainer TheValidationContainer { - get { return base.ValidationContainer; } + get { return base.FindValidationContainer(); } } public bool TheDesignMode