Dynamically loading user control AJAX with Webmethod

Hi all,

I am trying to load a user control using JQuery through ajax.

My webmethod is rendering and returning the HTML successfully to the ajax method. However, when I try to use .append() to add the new html to the div i get the following error;

Sys.ArgumentException: Value must be null for Components that are not Controls or Behaviors.
Parameter name: element
My Webmethod is as follows;
private static string control_path = "~/Public/usercontrols/";

    [WebMethod(EnableSession = true)]
    public string GetUserControl(string control, string properties)
    {
        try
        {
            using (Page dynamicPage = new Page())
            {
                 HtmlHead dynamicHead = new HtmlHead();

                dynamicPage.Controls.Add(dynamicHead);
                HtmlForm dynamicForm = new HtmlForm();
                dynamicPage.Controls.Add(dynamicForm);

                ScriptManager dynamicScript = new ScriptManager();
                dynamicForm.Controls.Add(dynamicScript);

                UserControl userControl = (UserControl)dynamicPage.LoadControl(control_path + control);

                var jss = new JavaScriptSerializer();
                var data = jss.Deserialize<dynamic>(properties);
                Dictionary<String, String> dict = (data as object[]).ToDictionary(key => (key as object[])[0].ToString(), val => (val as object[])[1].ToString());

                foreach (KeyValuePair<String, String> property in dict)
                {
                    PropertyInfo prop = userControl.GetType().BaseType.GetProperty(property.Key);
                    prop.SetValue(userControl, property.Value, null);

                }

                //TextWriter stringWriter = new StringWriter();
                //HtmlTextWriter htmlWriter = new HtmlTextWriter(stringWriter);
                //userControl.RenderControl(htmlWriter);
                //string html = stringWriter.ToString();
                //return html;
//error - wants to be inside a form tag with runat=server 

                dynamicForm.Controls.Add(userControl);

                using (StringWriter writer = new StringWriter())
                {
                    dynamicPage.Controls.Add(dynamicForm);
                    HttpContext.Current.Server.Execute(dynamicPage, writer, false);

                    return writer.ToString();//.Replace("\"", "\\\"");
                }
            }
        }
        catch (Exception ex)
        {
            Global.log.Error(String.Format("Error: AdminServices.cs: GetUserControl: :{0}:{1}", ex.Message, ex.InnerException));
            return ex.Message;
        }
    }

Open in new window


any ideas on how to get around this?
flynnyAsked:
Who is Participating?
 
Julian HansenCommented:
Using Firebug in Firefox and dumping returned script from GET calls to text file.

Didn't get far - FF chocked on the script and I had to restart the browser - which is when I called it a day
0
 
Julian HansenCommented:
Where are you getting the error and where is your code for appending the returned HTML?
0
 
flynnyAuthor Commented:
Hi Julian,

Sorry, the js code is as follows;

function addImageUpload(parent, element, text) {
    var html = "<div id='" + element.attr('id') + "_upload' class='small-button'><span>" + text + "</span></div>";

    parent.append(html);

    $("#" + element.attr('id') + "_upload").click(function () {

        var jsonArray = [
             { "fieldname": "control", "fieldtext": "ImageUploadPopupUserControl.ascx" },
             { "fieldname": "properties", "fieldtext": JSON.stringify( [[ "Tab1Header", "TEST TEST TEST" ], [ "ImageLocation", "~/public/images/upload/header-background" ]] )}
        ];

        runAjax('/Public/AdminServices.asmx/GetUserControl', jsonArray)
        .done(function (control) {
            alert(control.d);
            
            try
            {
                $('#UserControlPopup').show();
                
                $('#UserControlContainer').empty();
                
                $('#UserControlContainer').append(control.d);
            
                $('#UpdateProgress').hide();
            }
            catch (e) {
                alert(e.message);
            }


        })
        .error(function(jqXHR, textStatus, errorThrown) {
            alert(jqXHR.status + ":" + errorThrown);
        });

        $('#updateProgress').show();
    });

}

Open in new window


and I have a generic AJAX call for my web methods;
function runAjax(webmethod, fields) {

    //stringify passed field array
    if (fields !== null) {
        var jsonObject = {};

        for (var i = 0; i < fields.length; i++) {
            jsonObject[fields[i].fieldname] = fields[i].fieldtext;
        }

        //alert(JSON.stringify(jsonObject));

        return $.ajax({
            type: "POST",
            url: webmethod,
            data: JSON.stringify(jsonObject),
            contentType: "application/json; charset=utf-8",
            dataType: "json"
        });
    }
    else {
        return $.ajax({
            type: "POST",
            url: webmethod,
            contentType: "application/json; charset=utf-8",
            dataType: "json"
        });
    }  
}

Open in new window

0
Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

 
Julian HansenCommented:
This works for me
<!doctype html>
<html>
<head>
<title>Test</title>
<script src="http://code.jquery.com/jquery.js"></script>
<script type="text/javascript">
function runAjax(webmethod, fields) 
{
  //stringify passed field array
  if (fields !== null) {
    var jsonObject = {};

    for (var i = 0; i < fields.length; i++) {
      jsonObject[fields[i].fieldname] = fields[i].fieldtext;
    }

    //alert(JSON.stringify(jsonObject));

    return $.ajax({
      type: "POST",
      url: webmethod,
      data: JSON.stringify(jsonObject),
      contentType: "application/json; charset=utf-8",
      dataType: "json"
    });
  }
  else {
    return $.ajax({
      type: "POST",
      url: webmethod,
      contentType: "application/json; charset=utf-8",
      dataType: "json"
    });
  }  
}

$(function() {
  $('div').click(function() {
    var jsonArray = [
      { "fieldname": "control", "fieldtext": "ImageUploadPopupUserControl.ascx" },
      { "fieldname": "properties", "fieldtext": JSON.stringify( [[ "Tab1Header", "TEST TEST TEST" ], [ "ImageLocation", "~/public/images/upload/header-background" ]] )}
    ];
    
    runAjax('e007.php',jsonArray).done(function(control) {
      $('#UserControlContainer').empty();
      $('#UserControlContainer').append(control.d);
    });
  });
});
</script>
<style type="text/css">
</style>
</head>
<body>
<div>Click</div>
<div id="UserControlContainer"></div>
</body>
</html>

Open in new window

I am not calling a web-method - I have used a simple PHP script to return the raw JSon string and the JSon object which is then appended to the UserControlContainer. As you have stated in your previous posts that the WebMethod is returning the HTML correctly the source should not be relevant.

Here is a link to a working sample

http://www.marcorpsa.com/ee/e007.html
0
 
flynnyAuthor Commented:
Hi Julian,

thanks for posting this.

I have the working link here;

fun4all.partyepos.com/public

if you move the draggable div, elect logo settings, then click the button here you will see the result;

I alert out the returned html and then the error.

Also, I am using a update panel for this, so is it possible to somehow hook up this dynamically generated button to a server side event? (maybe using a hiddenfield?). I could then append the ascx server side?
0
 
Julian HansenCommented:
If you look at what is actually returned it does not look right.

It starts with a <head> statement - no doctype, no body declaration and then all the content is hidden or visibilty to hidden

See attached html file of what is actually returned by your WebMethod - so the append is probably working - jsut the returned code does not have anything visible in it.

Check with Inspect Element / Firebug after loading to see if the target div has the appended code - if it does you need to look at what is being rendered
e008.html
0
 
flynnyAuthor Commented:
Hi Julian,

yes the append does append the code to the div, but it is also throwning an error? (i.e. triggering the alert in the catch on the append call).

Could it be something to do with the AJAXToolkit controls i have inside the ascx file?

Heres the ascx code

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="ImageUploadPopupUserControl.ascx.cs" Inherits="Public_UserControls_ImageUploadPopupUserControl" %>



    <asp:panel id="ImageUploadSelect" Visible="true" runat="server">
        <ajaxToolkit:TabContainer runat="server" Width="100%" Height="600px" CssClass="ImageUploadTabStyle">
            <ajaxToolkit:TabPanel HeaderText="Upload New Image" runat="server">
                <HeaderTemplate>
                    <asp:label runat="server" id="Tab1HeaderLabel">Select new image to upload</asp:label>
                </HeaderTemplate>
                <ContentTemplate>
                    <script type="text/javascript">
                        function onClientUploadComplete(sender, e) {
                                                  onImageValidated("TRUE", e);
                                              }

                                              function onImageValidated(arg, context) {

                                                  var test = document.getElementById("testuploaded");
                                                  test.style.display = 'block';

                                                  var fileList = document.getElementById("fileList");
                                                  var item = document.createElement('div');
                                                  item.style.padding = '4px';

                                                  if (arg == "TRUE") {
                                                      var url = context.get_postedUrl();
                                                      url = url.replace('&amp;', '&');
                                                      item.appendChild(createThumbnail(context, url));
                                                  } else {
                                                      item.appendChild(createFileInfo(context));
                                                  }

                                                  fileList.appendChild(item);
                                              }

                                              function createFileInfo(e) {
                                                  var holder = document.createElement('div');
                                                  holder.appendChild(document.createTextNode(e.get_fileName() + ' with size ' + e.get_fileSize() + ' bytes'));

                                                  return holder;
                                              }

                                              function createThumbnail(e, url) {
                                                  var holder = document.createElement('div');
                                                  var img = document.createElement("img");
                                                  img.style.width = '80px';
                                                  img.style.height = '80px';
                                                  img.setAttribute("src", url);

                                                  holder.appendChild(createFileInfo(e));
                                                  holder.appendChild(img);

                                                  return holder;
                                              }

                                              function onClientUploadStart(sender, e) {
                                                  document.getElementById('uploadCompleteInfo').innerHTML = 'Please wait while uploading ' + e.get_filesInQueue() + ' files...';
                                              }

                                              function onClientUploadCompleteAll(sender, e) {

                                                  var args = JSON.parse(e.get_serverArguments()),
                                                      unit = args.duration > 60 ? 'minutes' : 'seconds',
                                                      duration = (args.duration / (args.duration > 60 ? 60 : 1)).toFixed(2);

                                                  var info = 'At <b>' + args.time + '</b> server time <b>'
                                                      + e.get_filesUploaded() + '</b> of <b>' + e.get_filesInQueue()
                                                      + '</b> files were uploaded with status code <b>"' + e.get_reason()
                                                      + '"</b> in <b>' + duration + ' ' + unit + '</b>';

                                                  document.getElementById('uploadCompleteInfo').innerHTML = info;
                                              }

                                        </script>
        
                                        <ajaxToolkit:AjaxFileUpload ID="AjaxImageUploader" CssClass="AjaxImageUpload" runat="server"
                                            MaximumNumberOfFiles="1" AllowedFileTypes="jpg,jpeg,png,gif" ThrobberID="AjaxThrobber"
                                            OnClientUploadStart="onClientUploadStart"
                                            OnClientUploadComplete="onClientUploadComplete" 
                                            OnClientUploadCompleteAll="onClientUploadCompleteAll" 
                                            OnUploadStart="AjaxImageUploader_UploadStart" 
                                            OnUploadComplete="AjaxImageUploader_OnUploadComplete"
                                            OnUploadCompleteAll="AjaxImageUploader_UploadCompleteAll" ></ajaxToolkit:AjaxFileUpload> 
                                        <asp:Label runat="server" ID="AjaxThrobber" Style="display: none;">
                                            <img align="absmiddle" alt="" src="/Public/Images/uploading.gif"/>
                                        </asp:Label>
                                        <div id="uploadCompleteInfo"></div>
                                        <br />
                                        <div id="testuploaded" style="display: none;">
                                            <h4>
                                                list of uploaded files:</h4>
                                            <hr />
                                            <div id="fileList">
                                            </div>
                                        </div>

                                    </ContentTemplate>
            </ajaxToolkit:TabPanel>
            <ajaxToolkit:TabPanel HeaderText="Select Image from Library" runat="server">
                                    <HeaderTemplate>
                                        Select an image from your library
                                    </HeaderTemplate>
                                    <ContentTemplate>
                                        <div class="PopupImageLibraryContainer">

                                            <script type="text/javascript">
                                                function BackgroundSelect(url) {

                                                    $('#Header').css('background-image', 'url(' + url + ')');

                                                    var css = [];
                                                    css.push(['background-image', 'url(' + url + ')']);

                                                    SaveChanges('#Header', css);
                                                    //ShowSavePanel();
                                                    //useless as postback cancelling this

                                                    $find("ImageUploadPopup").hide();
                                                    return false;
                                                }
                                            </script>

                                            <asp:Repeater ID="ImageUploadRepeater" OnItemDataBound="Repeater_ItemDataBound" runat="server">
                                                    <HeaderTemplate>

                                                    </HeaderTemplate>
                                                    <ItemTemplate>
                                                        <asp:ImageButton ID="Image" CssClass="HeaderBackgroundPreview" runat="server" ImageUrl='<%# Container.DataItem %>' OnClientClick='<%# String.Format( "javascript:return BackgroundSelect( \"{0}\" );", Container.DataItem ) %>'/>
                                                    </ItemTemplate>
                                                    <FooterTemplate>
                                                        <asp:Label ID="RepeaterErrorMessage" runat="server" Text="Sorry, you have no uploaded images show." Visible="false">
                                                        </asp:Label>
                                                    </FooterTemplate>
                                            </asp:Repeater>
                                        </div>
                                    </ContentTemplate>
                                </ajaxToolkit:TabPanel>
                            </ajaxToolkit:TabContainer>
  
                        </asp:panel>

                        <script type="text/javascript">
                            $(document).ready(function () {
                                var cropzoom = $('#<%=CropZoom.ClientID%>').cropzoom({
                                    width: 800,
                                    height: 575,
                                    bgColor: '#CCC',
                                    enableRotation: true,
                                    enableZoom: true,
                                    zoomSteps: 10,
                                    rotationSteps: 10,
                                    expose: {
                                        slidersOrientation: 'horizontal',
                                        zoomElement: '#zoom',
                                        rotationElement: '#rot',
                                        elementMovement: '#movement'
                                    },
                                    selector: {
                                        centered: true,
                                        borderColor: 'black',
                                        borderColorHover: 'yellow'
                                    },
                                    image: {
                                        source: '/public/default/bg-clouds.png',
                                        width: 1024,
                                        height: 768,
                                        minZoom: 50,
                                        maxZoom: 200,
                                        startZoom: 40,
                                        useStartZoomAsMinZoom: true,
                                        snapToContainer: true
                                    }
                                });
                            });
                        </script>
 
                        <asp:panel id="CropZoom" visible="false" runat="server">
                            <h2>Select the background area of your image</h2>
                        </asp:panel>

Open in new window

0
 
Julian HansenCommented:
Well if the error is triggering on the client then whatever happens on the server is already complete.

Your WebMethod is creating an html response - which it returns - this is just passive data from the perspective of the WebMethod.

It is only when the returned html is activated in the client by the append that the code becomes active.

There is javascript being returned as part of that response packet so that is where we need to look.

What I would do is save the returned html to a file and try and load that separately - see if you get the error and if so what line it is on - that should help narrow down what is causing the problem

The next question is - what is the returned code supposed to do - what are you trying to achieve with the returned html / javascript?
0
 
flynnyAuthor Commented:
Hi Julian,

The returned html is the rendered html from the ascx mentioned. This contains a ajaxtoolkt tabcontainer and ajaxtoolkit fileupload control. Basically I want to append the returned html to a modal popup and show it.

After looking through firebug at the modal div it looks like the append is completed successfully (as the code is appended to the div). However, it seems that the error may occur when it tries to hook up the JS (not sure if it would do this with an append call). As the controls are there, but the display has been set to none? as though it hasnt been instantiated in some way?

I have ammended the web method and returned a control with some simple html back successfully.
0
 
Julian HansenCommented:
The error is occurring in Component$Create function in the AjaxControlToolkit but could not debug it due to issues listed above. Specifically in the create function - whatever is being passed to the create function is not correct.

Can't assist beyond that.
0
 
flynnyAuthor Commented:
Thanks for all the help Julian,

May I ask how you debugged this? I output the returned html to a html file and run locally but I just get the same result (blank display:none on the controls).
0
 
flynnyAuthor Commented:
Hi Julian,

For anyone else the issue was because I was adding a general scriptmanager to the page and not the toolkitscriptmanager in my webmethod.

Thanks for all the help Julian, i appreciate it.
0
 
Julian HansenCommented:
You are welcome - thanks for the points - have a great day.
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.