Will "the Mighty" Strohl

HOW TO: Implement a Client-Side Form on .Net Apps

There are times when you need to incorporate a client-side form on your web form, user control, or DNN module.  As you probably already know, this will not work.  By default, ASP.Net doesn't allow child forms anymore.

An example of this may be if you use a 3rd party provider for services, such as a newsletter provider.  In those instances, you typically have two choices.  First, you could just send your visitors to their web site.  Second, you can implement a submission process right on your own site.

The trick to doing this, is to "trick" ASP.Net.  I built a hybrid class based on the example found at Code Project.  You use your normal method to collect the informatoin, and then use client-side code to complete the process.

PLEASE NOTE: This example requires a postback, but in this situation, your end-users will likely expect this anyway.

For example, let's say that you have a form that your vendor wants you to use, and it looks like the following form:

<form name="frmSignUp" id="frmSignUp" action="http://www.somedomain.com/signup.asp" method="get">
    <input type="hidden" name="hidAffiliateId" id="hidAffiliateId" value="12345" />
    First name: <input type="text" name="txtFirstName" id="txtFirstName" />
    <br />
    Last name: <input type="text" name="txtLastName" id="txtLastName" />
    <br />
    E-Mail: <input type="text" name="txtEmail" id="txtEmail" />
    <br />
    <input type="submit" name="cmdSubmit" id="cmdSubmit" value="Submit" />
</form>

The above form allows you to complete this project as expected on a static HTML page.  However, we need to perform a work-around to get this to work with ASP.Net.

The first thing we need to do is convert this to a server-side form.  Here is the converted example:

First name: <asp:TextBox id="txtFirstName" runat="server" /><br />
Last name: <asp:TextBox id="txtLastName" runat="server" /><br />
E-Mail: <asp:TextBox id="txtEmail" runat="server" /><br />
<asp:Button id="cmdSubmit" runat="server" Text="Submit" />
<asp:Literal id="Literal1" runat="server" />

That part was easy, but now need a way to perform this client to server to client "magic".  We need a .Net class that will mimic the properties expressed in the HTML form.  Here is an example of my class.

''' <summary>
''' WebForm
''' </summary>
''' <remarks></remarks>
''' <history>
''' [wstrohl] - 20070803 - Created by Will Strohl - http://www.strohlsitedesign.com
''' </history>
Public Class WebForm
#Region " Properties "
    Private p_Fields As SortedList(Of String, String) = Nothing
    ''' <summary>
    ''' A collection of the fields that you need to have as fields in the dynamically generated form.
    ''' </summary>
    ''' <value>SortedList(Of String, String)</value>
    ''' <returns>SortedList(Of String, String) - a collection of the fields that will be inserted into the form</returns>
    ''' <remarks></remarks>
    Public ReadOnly Property Fields() As SortedList(Of String, String)
        Get
            Return Me.p_Fields
        End Get
    End Property
    Private p_Method As String = "post"
    ''' <summary>
    ''' Method - the method that the form will use to submit the field data.
    ''' </summary>
    ''' <value>String</value>
    ''' <returns>String - this property will only accept and return "get" or "post". "post" is the default.</returns>
    ''' <remarks>
    ''' If for some reason a value other than "get" or "post" is assigned to this property, this property will
    ''' automatically default to "post".
    ''' </remarks>
    Property Method() As String
        Get
            If String.IsNullOrEmpty(Me.p_Method) Then Me.p_Method = "post"
            Return Me.p_Method
        End Get
        Set(ByVal Value As String)
            Dim re As New Regex("^(post|get)$", RegexOptions.IgnoreCase)
            If re.IsMatch(Value) Then
                Me.p_Method = Value
            Else
                Me.p_Method = "post"
            End If
        End Set
    End Property
    Private p_Action As String = String.Empty
    ''' <summary>
    ''' Action - this is the URL or path that the form will submit the information to.
    ''' </summary>
    ''' <value>String</value>
    ''' <returns>String - the URL or path that the form will submit to</returns>
    ''' <remarks>
    ''' If this property is not populated, the "ToString" method of this class will throw an exception.
    ''' </remarks>
    Property Action() As String
        Get
            Return Me.p_Action
        End Get
        Set(ByVal Value As String)
            Me.p_Action = Value
        End Set
    End Property
    Private p_AutoPost As Boolean = True
    ''' <summary>
    ''' AutoPost - this property tells this class whether the form will submit itself onload or not
    ''' </summary>
    ''' <value></value>
    ''' <returns>Boolean - the true or false value whether this form will automatically attempt
    ''' to submit the dynamically generated form (default is true)</returns>
    ''' <remarks></remarks>
    Property AutoPost() As Boolean
        Get
            Return Me.p_AutoPost
        End Get
        Set(ByVal Value As Boolean)
            Me.p_AutoPost = Value
        End Set
    End Property
    Private p_Name As String = String.Empty
    ''' <summary>
    ''' Name - the name and id of the form that will be created
    ''' </summary>
    ''' <value>String</value>
    ''' <returns>String - the name and id of the form</returns>
    ''' <remarks>
    ''' If you do not add a name, one will be generated for you.
    ''' </remarks>
    Property Name() As String
        Get
            Return Me.p_Name
        End Get
        Set(ByVal Value As String)
            Me.p_Name = Value
        End Set
    End Property
#End Region
#Region " Constructors "
    ''' <summary>
    ''' New - this method instantiates the class
    ''' </summary>
    ''' <remarks></remarks>
    Sub New()
        Me.p_AutoPost = True
        Me.p_Fields = New SortedList(Of String, String)
        Me.p_Method = "post"
        Me.p_Name = String.Concat("Form_", DateTime.Now.ToString("MMddyyyyhhmmss"))
        Me.p_Action = String.Empty
    End Sub
    ''' <summary>
    ''' New - this method instantiates the class
    ''' </summary>
    ''' <param name="FormAction">String - the URL or path that the form will submit to</param>
    ''' <remarks>
    ''' POSSIBLE EXCEPTIONS:
    ''' ArgumentNullException - this will occur if of the argument is Null or Empty
    ''' </remarks>
    Sub New(ByVal FormAction As String)
        '
        ' BEGIN VALIDATION
        '
        If String.IsNullOrEmpty(FormAction) Then _
            Throw New ArgumentNullException("The FormAction argument cannot be NULL or empty!")
        '
        ' END VALIDATION
        '
        Me.p_AutoPost = True
        Me.p_Fields = New SortedList(Of String, String)
        Me.p_Method = "post"
        Me.p_Name = String.Concat("Form_", DateTime.Now.ToString("MMddyyyyhhmmss"))
        Me.p_Action = FormAction
    End Sub
    ''' <summary>
    ''' New - this method instantiates the class
    ''' </summary>
    ''' <param name="FormAction">String - the URL or path that the form will submit to</param>
    ''' <param name="FormMethod">String - the method that will be used to submit the form ("get" or "post")</param>
    ''' <remarks>
    ''' POSSIBLE EXCEPTIONS:
    ''' ArgumentNullException - this will occur if any of the arguments are Null or Empty
    ''' </remarks>
    Sub New(ByVal FormAction As String, ByVal FormMethod As String)
        '
        ' BEGIN VALIDATION
        '
        If String.IsNullOrEmpty(FormAction) Then _
            Throw New ArgumentNullException("The FormAction argument cannot be NULL or empty!")
        If String.IsNullOrEmpty(FormMethod) Then _
            Throw New ArgumentNullException("The FormMethod argument cannot be NULL or empty!")
        '
        ' END VALIDATION
        '
        Me.p_AutoPost = True
        Me.p_Fields = New SortedList(Of String, String)
        Me.p_Method = FormMethod
        Me.p_Name = String.Concat("Form_", DateTime.Now.ToString("MMddyyyyhhmmss"))
        Me.p_Action = FormAction
    End Sub
    ''' <summary>
    ''' New - this method instantiates the class
    ''' </summary>
    ''' <param name="FormAction">String - the URL or path that the form will submit to</param>
    ''' <param name="FormMethod">String - the method that will be used to submit the form ("get" or "post")</param>
    ''' <param name="FormName">String - then name and id that you wish for the form to have</param>
    ''' <remarks>
    ''' POSSIBLE EXCEPTIONS:
    ''' ArgumentNullException - this will occur if any of the arguments are Null or Empty
    ''' </remarks>
    Sub New(ByVal FormAction As String, ByVal FormMethod As String, ByVal FormName As String)
        '
        ' BEGIN VALIDATION
        '
        If String.IsNullOrEmpty(FormAction) Then _
            Throw New ArgumentNullException("The FormAction argument cannot be NULL or empty!")
        If String.IsNullOrEmpty(FormMethod) Then _
            Throw New ArgumentNullException("The FormMethod argument cannot be NULL or empty!")
        If String.IsNullOrEmpty(FormName) Then _
            Throw New ArgumentNullException("The FormName argument cannot be NULL or empty!")
        '
        ' END VALIDATION
        '
        Me.p_AutoPost = True
        Me.p_Fields = New SortedList(Of String, String)
        Me.p_Method = FormMethod
        Me.p_Name = FormName
        Me.p_Action = FormAction
    End Sub
#End Region
#Region " Public Methods "
    ''' <summary>
    ''' AddField - this method allows you to add a field to the form.
    ''' </summary>
    ''' <param name="FieldName">String - the name of the field to add</param>
    ''' <param name="FieldValue">String - the value of the field to add</param>
    ''' <remarks>
    ''' These fields will be rendered as "Hidden" input fields.
    '''
    ''' You can send an empty string to the FieldValue argument, but the FieldName is required.
    ''' </remarks>
    Sub AddField(ByVal FieldName As String, ByVal FieldValue As String)
        '
        ' BEGIN VALIDATION
        '
        If String.IsNullOrEmpty(FieldName) Then _
            Throw New ArgumentNullException("The FieldName argument cannot be NULL or empty!")
        '
        ' END VALIDATION
        '
        Me.Fields.Add(FieldName, FieldValue)
    End Sub
    ''' <summary>
    ''' ToString - this override of the ToString method outputs the Form and its required
    ''' JavaScript.
    ''' </summary>
    ''' <returns>String - the form, its fields, and its JavaScript</returns>
    ''' <remarks>
    ''' If AutoPost is set to True, then the JavaScript will automatically submit the
    ''' form once the page loads.  Otherwise, you should add a client-side script to call
    ''' the JavaScript "submitWebForm" function.  There are no parameters.
    ''' </remarks>
    Overrides Function ToString() As String
        '
        ' BEGIN VALIDATION
        '
        If String.IsNullOrEmpty(Me.Action) Then _
            Throw New ArgumentNullException("The ToString() method cannot be processed without the 'Action' property assigned.")
        If Not Me.Fields.Count > 0 Then _
            Throw New ArgumentNullException("The ToString() method cannot be processed without any Fields added.")
        If String.IsNullOrEmpty(Me.Name) Then _
            Throw New ArgumentNullException("The ToString() method cannot be processed without the Form 'Name' property assigned.")
        '
        ' END VALIDATION
        '
        Dim sb As New StringBuilder
        With sb
            ' create the form tag
            .Append("<form id=""")
            .Append(Me.Name)
            .Append(""" name=""")
            .Append(Me.Name)
            .Append(""" action=""")
            .Append(Me.Action)
            .Append(""" method=""")
            .Append(Me.Method)
            .Append(""">")
            .Append(Environment.NewLine)
            ' loop through and add the form fields
            For Each kvp As KeyValuePair(Of String, String) In Me.Fields
                .Append("<input type=""hidden"" id=""")
                .Append(kvp.Key)
                .Append(""" name=""")
                .Append(kvp.Key)
                .Append(""" value=""")
                .Append(kvp.Value)
                .Append(""" />")
                .Append(Environment.NewLine)
            Next
            ' close the form tag
            .Append("</form>")
            .Append(Environment.NewLine)
            ' add javascript to automatically "submit" the form
            .Append("<script type=""text/javascript"" language=""javascript"">/*<![CDATA[*/")
            If Not Me.AutoPost Then .Append("function submitWebForm(){")
            .Append("document.getElementById('")
            .Append(Me.Name)
            .Append("').submit();")
            If Not Me.AutoPost Then .Append("}")
            .Append("/*]]>*/</script>")
        End With
        Return sb.ToString
    End Function
#End Region
    End Class

The above class allows you to generate this form server-side and then automatically execute it client-side.  But how do you use this class?  Here is an example of the server-side event that we will use to accomplish this.

Protected Sub cmdSubmit_Click(ByVal sender As Object, ByVal e As System.Web.UI.ButtonClickEventArgs) Handles cmdSubmit.Click
    Dim objWebForm As New WebForm("http://www.somedomain.com/signup.asp")
    With objWebForm
        .AddField("hidAffiliateId", "12345")
        .AddField("txtFirstName", txtFirstName.Text)
        .AddField("txtLastName", txtLastName.Text)
        .AddField("txtEmailName", txtEmail.Text)
    End With
    ' add this dynamically created form to the page
    Literal1.Text &= wf.ToString
End Sub

Once you have the above code snippets in place, run your web application, and you will have a fully functional client-side form.



blog comments powered by Disqus