Solved

Dynamically loading user control AJAX with Webmethod

Posted on 2014-10-06
13
847 Views
Last Modified: 2014-10-14
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?
0
Comment
Question by:flynny
  • 7
  • 6
13 Comments
 
LVL 51

Expert Comment

by:Julian Hansen
ID: 40365723
Where are you getting the error and where is your code for appending the returned HTML?
0
 

Author Comment

by:flynny
ID: 40370494
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
 
LVL 51

Expert Comment

by:Julian Hansen
ID: 40370595
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
 

Author Comment

by:flynny
ID: 40370733
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
 
LVL 51

Expert Comment

by:Julian Hansen
ID: 40370930
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
 

Author Comment

by:flynny
ID: 40370943
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
Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

 
LVL 51

Expert Comment

by:Julian Hansen
ID: 40371324
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
 

Author Comment

by:flynny
ID: 40372402
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
 
LVL 51

Expert Comment

by:Julian Hansen
ID: 40372617
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
 

Author Comment

by:flynny
ID: 40372688
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
 
LVL 51

Accepted Solution

by:
Julian Hansen earned 500 total points
ID: 40372728
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
 

Author Comment

by:flynny
ID: 40379523
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
 
LVL 51

Expert Comment

by:Julian Hansen
ID: 40379602
You are welcome - thanks for the points - have a great day.
0

Featured Post

Do You Know the 4 Main Threat Actor Types?

Do you know the main threat actor types? Most attackers fall into one of four categories, each with their own favored tactics, techniques, and procedures.

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
DLL in ASP.NET 20 45
Data Saving 5 40
c# regex: extract & replace text between braces 6 30
ASP.NET Web API or ASP.NET Core MVC? 3 31
IntroductionWhile developing web applications, a single page might contain many regions and each region might contain many number of controls with the capability to perform  postback. Many times you might need to perform some action on an ASP.NET po…
OverviewThis article demonstrates a simple search form using AJAX. The purpose of the article is to demonstrate how to use the same code to render a page and javascript (JQuery) and AJAX to make subsequent calls to refine the results. The princip…
Get a first impression of how PRTG looks and learn how it works.   This video is a short introduction to PRTG, as an initial overview or as a quick start for new PRTG users.
This video demonstrates how to create an example email signature rule for a department in a company using CodeTwo Exchange Rules. The signature will be inserted beneath users' latest emails in conversations and will be displayed in users' Sent Items…

760 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

18 Experts available now in Live!

Get 1:1 Help Now