flynny
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;
any ideas on how to get around this?
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;
My Webmethod is as follows;
Sys.ArgumentException: Value must be null for Components that are not Controls or Behaviors.
Parameter name: element
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;
}
}
any ideas on how to get around this?
Where are you getting the error and where is your code for appending the returned HTML?
ASKER
Hi Julian,
Sorry, the js code is as follows;
and I have a generic AJAX call for my web methods;
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();
});
}
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"
});
}
}
This works for me
Here is a link to a working sample
http://www.marcorpsa.com/ee/e007.html
<!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>
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
ASKER
Hi Julian,
thanks for posting this.
I have the working link here;
fun4all.partyepos.com/publ ic
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?
thanks for posting this.
I have the working link here;
fun4all.partyepos.com/publ
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
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
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
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('&', '&');
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>
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?
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?
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 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.
Can't assist beyond that.
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).
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
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
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.
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.