UpdatePanel with input type other than text in html5
Asked Answered
L

3

10

I am developing an open source project for rendering HTML5 using ASP.NET. Here you can take a look: http://asphtml5.codeplex.com/

now I have a problem with update panel in posting back the input values that have type other than 'text'. as you might know, html 5 has introduced several input types, for example 'number', 'tel', 'search', etc. Now if I render such controls, everything works fine in normal situations, but if I put them inside an UpdatePanel, no value will be posted back and the value will be reset.

here is a small piece of code that produces the same error:

    <asp:UpdatePanel runat="server" ID="UP">
        <ContentTemplate>
            <p>
                Enter A Number:
                <asp:TextBox runat="server" ID="Number2" type="number" />
            </p>
            <asp:Button Text="Submit" runat="server" ID="BtnSubmit" OnClick="BtnSubmit_Click" />
            <p>
                You entered :
                <asp:Label Text="" ID="LblValue" runat="server" />
            </p>
        </ContentTemplate>
    </asp:UpdatePanel>

if you test this code on a browser that supports html 5, lets say Chrome as an example, a Numeric Up-Down field will be shown. but if you click on the submit button, it will lose the value that you have entered.

here is the code for event handler:

        protected void BtnSubmit_Click(object sender, EventArgs e)
        {
            LblValue.Text = Number2.Text;
        }

what I have already tried is reading UpdatePanel, ScriptManager and ScriptManagerProxy classes codes, nothing found.

I think I might need to create my own UpdatePanel and/or ScriptManager classes for use.

Could anyone help me, and tell me where to check.

Lanellelanette answered 27/12, 2011 at 21:12 Comment(4)
I believe this aslo has to do with paritial postbacks. can you create a Client or Server side ScriptManager..?Journeyman
This will be fixed in the next release of ASP.NET: connect.microsoft.com/VisualStudio/feedback/details/651085/…Dele
Thanks @Tim, but is there a way to get the fixed code and partially release it? Or, do you have any idea what they did to fix it? I think I am responsible for this problem as long as I am publishing HTML5 controls.Lanellelanette
If you dig through the client-side code which supports ASP.NET AJAX, I believe you will find the code which loops through all inputs on the page and collects them for async postback. There is probably a legitimate way (or hack) to modify which input types are collected.Heigl
L
2

All right then, I figured out a way to fix that. First of all I found the problem with the code that Tim Medora provided. It all was the this. modifier's fault. So this JavaScript fixed the problem:

var _textTypes = /^(text|password|hidden|search|tel|url|email|number|range|color|datetime|date|month|week|time|datetime-local)$/i;
function Sys$WebForms$PageRequestManager$_onFormSubmit(evt) {
...
if (_textTypes.test(type) ||
       (((type === 'checkbox') || (type === 'radio')) && element.checked)) {
       formBody.append(encodeURIComponent(name));
       formBody.append('=');
       formBody.append(encodeURIComponent(element.value));
       formBody.append('&');
...
}

Now I had to inject my function into ScriptResource.axd. For now I found a way that seems to work:

I created a class ScriptResouceHandler that extends System.Web.Handlers.ScriptResourceHandler in namespace DotM.Html5.Handlers.

in its ProcessRequest I called base.ProcessRequest(context) to do its job. But I wanted to add my function to the one rendering original function. I found out that it was when the encrypted ZSystem.Web.Extensions,4.0.0.0,,31bf3856ad364e35|MicrosoftAjaxWebForms.debug.js| was passed.

Another problem was that in System.Web.Handlers.ScriptResourceHandler, 'Page.DecryptString' method which is internal is called to decrypt the query string param. so there was no way for me to invoke that method via reflection.

here is the code:

        protected sealed override void ProcessRequest(HttpContext context)
        {
            base.ProcessRequest(context);
            if (CypherContainsAjax(context.Request.QueryString["d"]))
                context.Response.Write(OnFormSubmit);
        }

        private bool CypherContainsAjax(string cypher)
        {
            var text = DecryptString(cypher);
            if (text == null)
                return true; //Then Add it everywhere. What else could I do? :D
            return text.Contains("MicrosoftAjaxWebForms");
        }

        private string DecryptString(string cypher)
        {
            if (PageDecryptString == null)
                return null;
            return (string)PageDecryptString.Invoke(null, new object[] { cypher });
        }
        private static MethodInfo PageDecryptString;
        static ScriptResourceHandler()
        {
            PageDecryptString = typeof(Page).GetMethod("DecryptString", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
        }

You may call this some kind of ugly hacking ...

Lanellelanette answered 31/12, 2011 at 20:40 Comment(0)
H
2

Interestingly enough, the ASP.NET 4.0 AJAX framework does seem to be aware of HTML 5 input types (see code), yet as @TimSchmelter pointed out, this is confirmed by Microsoft as a bug.

This may give you a starting point for debugging the behavior and/or overriding the default behavior and finding a solution.

It could also be an error on the server-side processing code for these input types, although I'm not sure why they should/would care about an async postback versus a normal postback.

this._textTypes = /^(text|password|hidden|search|tel|url|email|number|range|color|datetime|date|month|week|time|datetime-local)$/i;

function Sys$WebForms$PageRequestManager$_onFormSubmit(evt) {
        var i, l, continueSubmit = true,
            isCrossPost = this._isCrossPost;
        this._isCrossPost = false;
        if (this._onsubmit) {
            continueSubmit = this._onsubmit();
        }
        if (continueSubmit) {
            for (i = 0, l = this._onSubmitStatements.length; i < l; i++) {
                if (!this._onSubmitStatements[i]()) {
                    continueSubmit = false;
                    break;
                }
            }
        }
        if (!continueSubmit) {
            if (evt) {
                evt.preventDefault();
            }
            return;
        }
        var form = this._form;
        if (isCrossPost) {
            return;
        }
        if (this._activeDefaultButton && !this._activeDefaultButtonClicked) {
            this._onFormElementActive(this._activeDefaultButton, 0, 0);
        }
        if (!this._postBackSettings || !this._postBackSettings.async) {
            return;
        }
        var formBody = new Sys.StringBuilder(),
            count = form.elements.length,
            panelID = this._createPanelID(null, this._postBackSettings);
        formBody.append(panelID);
        for (i = 0; i < count; i++) {
            var element = form.elements[i];
            var name = element.name;
            if (typeof(name) === "undefined" || (name === null) || (name.length === 0) || (name === this._scriptManagerID)) {
                continue;
            }
            var tagName = element.tagName.toUpperCase();
            if (tagName === 'INPUT') {
                var type = element.type;
                if (this._textTypes.test(type)
                    || ((type === 'checkbox' || type === 'radio') && element.checked)) {
                    formBody.append(encodeURIComponent(name));
                    formBody.append('=');
                    formBody.append(encodeURIComponent(element.value));
                    formBody.append('&');
                }
            }
            else if (tagName === 'SELECT') {
                var optionCount = element.options.length;
                for (var j = 0; j < optionCount; j++) {
                    var option = element.options[j];
                    if (option.selected) {
                        formBody.append(encodeURIComponent(name));
                        formBody.append('=');
                        formBody.append(encodeURIComponent(option.value));
                        formBody.append('&');
                    }
                }
            }
            else if (tagName === 'TEXTAREA') {
                formBody.append(encodeURIComponent(name));
                formBody.append('=');
                formBody.append(encodeURIComponent(element.value));
                formBody.append('&');
            }
        }
        formBody.append("__ASYNCPOST=true&");
        if (this._additionalInput) {
            formBody.append(this._additionalInput);
            this._additionalInput = null;
        }

// truncated for length
Heigl answered 29/12, 2011 at 22:52 Comment(2)
And again interesting enough, the client side code that is rendered in my test application, does not use the regex you provided. It checks for only 'text', 'password' and 'hidden' types. and when I appended the my number input with Chrome's Console, it worked fine. Now I am looking for a way to hack that function and I hope it will work.Lanellelanette
@MaziarTaheri - ASP.NET could be sending different scripts to different browsers, but I used Chrome to pull down the client-side scripts which DID include the different text types. I think the best answer is to override the _textTypes property in all cases. If you need help finding the actual code, let me know. I believe it's just the "public" property of a JS object.Heigl
L
2

All right then, I figured out a way to fix that. First of all I found the problem with the code that Tim Medora provided. It all was the this. modifier's fault. So this JavaScript fixed the problem:

var _textTypes = /^(text|password|hidden|search|tel|url|email|number|range|color|datetime|date|month|week|time|datetime-local)$/i;
function Sys$WebForms$PageRequestManager$_onFormSubmit(evt) {
...
if (_textTypes.test(type) ||
       (((type === 'checkbox') || (type === 'radio')) && element.checked)) {
       formBody.append(encodeURIComponent(name));
       formBody.append('=');
       formBody.append(encodeURIComponent(element.value));
       formBody.append('&');
...
}

Now I had to inject my function into ScriptResource.axd. For now I found a way that seems to work:

I created a class ScriptResouceHandler that extends System.Web.Handlers.ScriptResourceHandler in namespace DotM.Html5.Handlers.

in its ProcessRequest I called base.ProcessRequest(context) to do its job. But I wanted to add my function to the one rendering original function. I found out that it was when the encrypted ZSystem.Web.Extensions,4.0.0.0,,31bf3856ad364e35|MicrosoftAjaxWebForms.debug.js| was passed.

Another problem was that in System.Web.Handlers.ScriptResourceHandler, 'Page.DecryptString' method which is internal is called to decrypt the query string param. so there was no way for me to invoke that method via reflection.

here is the code:

        protected sealed override void ProcessRequest(HttpContext context)
        {
            base.ProcessRequest(context);
            if (CypherContainsAjax(context.Request.QueryString["d"]))
                context.Response.Write(OnFormSubmit);
        }

        private bool CypherContainsAjax(string cypher)
        {
            var text = DecryptString(cypher);
            if (text == null)
                return true; //Then Add it everywhere. What else could I do? :D
            return text.Contains("MicrosoftAjaxWebForms");
        }

        private string DecryptString(string cypher)
        {
            if (PageDecryptString == null)
                return null;
            return (string)PageDecryptString.Invoke(null, new object[] { cypher });
        }
        private static MethodInfo PageDecryptString;
        static ScriptResourceHandler()
        {
            PageDecryptString = typeof(Page).GetMethod("DecryptString", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic);
        }

You may call this some kind of ugly hacking ...

Lanellelanette answered 31/12, 2011 at 20:40 Comment(0)
S
1

This was already fixed in .Net 4 Reliability Update 1 (there is also a version 2, but it does not contain the first one): http://support.microsoft.com/kb/2533523

But if you are using AjaxControlToolkit, it uses its own internal MicrosoftAjaxWebForms.js which is an older fork, and there is still no official fix for it - you can use mine solution from here: http://ajaxcontroltoolkit.codeplex.com/workitem/27041

So - you can either include the fixed ToolkitScriptManager with your project (a bloat, I know), or you can try to include the new version of MicrosoftAjaxWebForms.js by experimenting with vanilla ScriptManager properties AjaxFrameworkMode="Explicit", Scripts or CompositeScript.

Use the AjaxFrameworkMode property to enable all Microsoft Ajax script files, to disable all Microsoft Ajax script files, or to explicitly include individual script files.

The Scripts collection does not contain the core Microsoft Ajax Library scripts. The scripts in the core library are rendered automatically; they do not have to be registered with the ScriptManager control. However, if you want to override a core script or any control script and substitute a different version of the script, you can add your version to the Scripts collection.

<asp:ScriptManager runat="server">
  <Scripts>
    <asp:ScriptReference Path="~/MicrosoftAjaxWebForms.js" />
  </Scripts>
</asp:ScriptManager>

You can get the new version of MicrosoftAjaxWebForms.js from System.Web.Extensions assembly Resources (with .Net Reflector) on machines with installed Reliability Update 1.

Sneeze answered 23/3, 2012 at 15:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.