Link to home
Start Free TrialLog in
Avatar of flynny
flynnyFlag for United Kingdom of Great Britain and Northern Ireland

asked on

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?
Avatar of Julian Hansen
Julian Hansen
Flag of South Africa image

Where are you getting the error and where is your code for appending the returned HTML?
Avatar of flynny

ASKER

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

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
Avatar of flynny

ASKER

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?
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
Avatar of flynny

ASKER

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

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?
Avatar of flynny

ASKER

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.
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.
Avatar of flynny

ASKER

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).
ASKER CERTIFIED SOLUTION
Avatar of Julian Hansen
Julian Hansen
Flag of South Africa image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of flynny

ASKER

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.
You are welcome - thanks for the points - have a great day.