I am using an updatepanel and I'd like to use the Sys.WebForms.PageRequestManager endRequest event to display a success message. But I can only get this event to fire when I generate an error in my procedure. My understanding is that it should fire all the time. What am I missing?
So, you don't give much context here.
but, lets use a update panel, drop in a simple button.
On button click, we will display a please wait.....
For code behind on button click, we just do a "sleep" for 4 seconds to fake a long running process.
When done, of course the post-back will automatic hide the please wait message, and then we have some javascript code run by using "end request".
so, first up, our markup in the UP, and a simple please wait....
(we don't need a UP for that simple please wait processing message, but lets just use a UP for this).
So, this:
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<h3>Start the Big Process</h3>
<asp:Button ID="cmdStart" runat="server"
Text="Start Process" CssClass="btn"
ClientIDMode="Static"
OnClick="cmdStart_Click" />
<asp:UpdateProgress ID="UpdateProgress1" runat="server">
<ProgressTemplate>
<img src="Content/wait2.gif" width="64" />
Update in progress...
</ProgressTemplate>
</asp:UpdateProgress>
</ContentTemplate>
</asp:UpdatePanel>
So, how the "Progress Template works", is that area and markup is displayed DURING the post back. So, very handy.
So, say the code behind is this:
protected void cmdStart_Click(object sender, EventArgs e)
{
// fake 4 seconds long running
System.Threading.Thread.Sleep(4000);
}
So, when we run, we get/see this effect:
Now, of course we want a message, or at the very least to run some client side code AFTER the post-back (processing) is done.
Well, we can adop, use the very "long time" approach of simple using a register client script at the end of the code. And as such, no update panel event is required.
So, say after done, we want a client side alert(), or even better, say a toast message (any kind of client side js code is the goal here).
So, we could say do this:
protected void cmdStart_Click(object sender, EventArgs e)
{
// fake 4 seconds long running
System.Threading.Thread.Sleep(4000);
MyToast(this.Page, cmdStart.ClientID, "Processing Complete",
#"All steps for project<br/>are now complete", "6000");
}
void MyToast(Page mypage, string ctrlBeside,
string Heading, string strMessage,
string strDelay = "3000")
{
string strScript =
#"toastcall('#ctrlBeside','#Heading','#Text','#strDelay');";
strScript = strScript.Replace("#ctrlBeside", ctrlBeside);
strScript = strScript.Replace("#Heading", Heading);
strScript = strScript.Replace("#Text", strMessage);
strScript = strScript.Replace("#strDelay", strDelay);
ScriptManager.RegisterStartupScript(mypage, mypage.GetType(), "mytoast", strScript, true);
}
Now, I just have a toast message "helper" in above, and all it does is setup the js code, and inject (register). Do note that in place of Page.ClientScript.RegisterStartupScript, we used ScriptManger to register (we need to do this for update panels - other than that - same functionally).
And of course, I do assume in place of a simple alert(), that I have jQuery.toast installed.
So, I have this client side js code in that page:
function toastcall(sControl, sHeading, sMsg, sTime) {
var btn = $('#' + sControl)
var btnpos = btn.position()
$.toast({
heading: sHeading,
text: sMsg,
position: {top: (btnpos.top + btn.height()), left: btnpos.left + btn.width()},
icon: 'info',
hideAfter: sTime,
allowToastClose: false,
loader: false,
showHideTransition: 'slide'
});
}
And now we get this effect:
So far we really don't need that event, since the "last line" of the code behind is free like "any" and "all" code behind to inject some js script to run.
And if you want a another button on the page OUTSITE of UP to trigger that up?
Then just use a client side button, and use a "click" of a button inside of the UP.
Say like this:
<asp:Button ID="Button2" runat="server" Text="client side button - trigger update panel"
OnClientClick="$('#cmdStart').click();return false"
/>
So, the above will run the button inside of the UP, and not a full page post back.
However, just place that 2nd button in another UP, and that click will then ALWAYS load + post BOTH UP's. (in fact all on the given page will ALWAYS be posted back as a whole group of UP's). If you don't want all UP's posted back to the server, then you have to add this to the UP.
UpdateMode="Conditional"
So, keep in mind that multiple UP's all post back by default unless you add above. (of course that ONLY applies to a UP post-back, since with a regular button and post-back outside of UP, then you have a regular plain jane post-back).
So, do keep in mind that UP is in fact a post-back! - or better stated what we call a "partial page post back". And not that when using a UP, even the page load event fires EACH time.
So, a UP does not 100% eliminate a post-back, despite it "appearing" to not have a post-back. A page life cycle and "round trip" does occur, but only the "things" inside of the UP are posted back.
Ok, last but not least?
The question is how to use end request event?
that event is for CLIENT SIDE code, not server side.
So, in place of that register script at the end of the server side code (in update panel), we could have the UP panel ALWAYS call some client side code, and we thus eliminate the requirement to "register" some client side js code to run.
As I stated, the "use case" for this is not all that great, since we have the ability to register any script in the server side code anyway.
however, lets remove the register script code, and use the "end request" event of the update panel.
So, we do it this way:
<script>
var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.add_endRequest(mytoastmsg);
function mytoastmsg() {
btnID = $('#cmdStart').attr("id")
toastcall(btnID, "Processing Complete",
"All steps for project<br/>are now complete", "6000")
}
function toastcall(sControl, sHeading, sMsg, sTime) {
var btn = $('#' + sControl)
var btnpos = btn.position()
$.toast({
heading: sHeading,
text: sMsg,
position: {top: (btnpos.top + btn.height()), left: btnpos.left + btn.width()},
icon: 'info',
hideAfter: sTime,
allowToastClose: false,
loader: false,
showHideTransition: 'slide'
});
}
</script>
So, note the add_endRequest - that sets up the "end request" code client side. So, now for any and all "end" of the UP, that one js code stub will run.
so, our button in UP becomes this:
protected void cmdStart_Click(object sender, EventArgs e)
{
// fake 4 seconds long running
System.Threading.Thread.Sleep(4000);
//MyToast(this.Page, cmdStart.ClientID, "Processing Complete",
// #"All steps for project<br/>are now complete", "6000");
}
So, for testing, we just commented out the final script register, since now, the "end request" setup will fire + trigger the client side js code ALWAYS at the end of the post back for the UP.
the effect is 100% the same as the register script example.
Related
I'm having an issue with the cycle of a page reload and I can't figure it out. I have an ASP button the runs at the server but it has an associated client side click. The client side Javascript is running correctly and returning true to the button click so it is also running. The Javascript makes a modification to the query string on the URL and this is also working. However, in the C# code behind, the query string is not there. Somewhere, I'm missing something.
The HTML link:
<asp:Button ID="btnRunMOReport" class="button-dbg" runat="server"
Text="Run MO Report" OnClick="btnMO_Report_Click"
OnClientClick="return validateCheckBoxesMO()" />
The JavaScript portion:
function validateCheckBoxesMO() {
token='xyz';
let url1 = window.location.href;
if (url1.indexOf("?") > 0) {
url1 = url1.substring(0, url.indexOf("?"));
}
url1 += "?hiddenToken=" + token;
window.location.replace(url1);
return true;
}
The hiddenToken is now represented on the page (?hiddenToken=xyz).
The code behind:
protected void btnMO_Report_Click(object sender, EventArgs e)
{
MailMessage mailtest = new MailMessage();
mailtest.IsBodyHtml = true;
SmtpClient SmtpServertest = new SmtpClient(ConfigurationManager.AppSettings["smtp_server"]);
mailtest.To.Add("Whoever#test123.com");
mailtest.From = new MailAddress("Whoever#test123.com");
mailtest.Subject = Request.QueryString["hiddenToken"];
mailtest.Body = "Whatever";
}
The mail comes just fine but the subject is blank. Somehow, during the page reload cycle, the query string has not yet been set.
If there is a better way to pass data from the JavaScript to the code behind, I'm all ears.
I want to launch another page from the code behind but I need some data that is returned from the JS. The token is actually something I fetch, process the JSON and now I want to make that token available to the code behind for additional information to add to the new URL I am constructing. Probably TMI for this but it is what I am trying to do.
Thanks for your assistance.
Your script isn't working because the browser makes a POST request to submit the form (and __VIEWSTATE) using the action="" attribute of the <form> that WebForms adds to your page.
When your client-script sets window.location it isn't changing how the <form> will behave. You could use your script to append the new querystring value to the <form>'s action="" attribute and this may work, however it will likely fail if the application has request-validation enabled (in which case ASP.NET will reject a tampered form submission).
As you're using WebForms (and you shouldn't be using WebForms in 2021...) you shouldn't try to fight it unless you understand how it all works (I'm not trying to be condescending: it took me years to figure it all out and I've been using WebForms since 2004).
Instead, provide the value through an <asp:HiddenField>:
Change your .aspx markup to this:
<asp:Button runat="server" ID="btnRunMOReport" class="button-dbg"
Text="Run MO Report" OnClick="btnMO_Report_Click"
OnClientClick="return validateCheckBoxesMO()" />
<asp:HiddenField runat="server" ID="superSecretHiddenField" />
Change your client script to this:
function validateCheckBoxesMO() {
const hiddenFieldId = '<%= this.superSecretHiddenField.ClientID %>';
const hiddenField = document.getElementById( hiddenFieldId );
token='xyz';
hiddenField.value = token;
return true; // <-- This is wrong, btw. Instead use `Event.prototype.stopPropagation()` - but that requires the on-click function to be wired-up correctly and I don't remember the specifics other than that WebForms *doesn't* do things correctly (not out-of-spite, but because WebForms predates the standardisation of client-script events).
}
And your code-behind to this:
protected void btnMO_Report_Click(object sender, EventArgs e)
{
MailMessage mailtest = new MailMessage();
mailtest.IsBodyHtml = true;
SmtpClient SmtpServertest = new SmtpClient(ConfigurationManager.AppSettings["smtp_server"]);
mailtest.To.Add("Whoever#test123.com");
mailtest.From = new MailAddress("Whoever#test123.com");
mailtest.Subject = this.superSecretHiddenField.Value;
mailtest.Body = "Whatever";
}
As noted, a button post back will in general over-write the url that you change. Unless you actually do a navigation client side that is caused by the js, then it will not persist.
So, on the most simple level, just drop in a text box, or hidden field, and put the value you need/want into that hidden textbox or field.
So, client side? Markup?
You can use this:
<asp:Button ID="Button1" runat="server" Text="Delete"
OnClientClick="SetHidden();"/>
<asp:HiddenField ID="HiddenField1" runat="server" ClientIDMode="Static"/>
<br />
<script>
function SetHidden() {
hField = document.getElementById('HiddenField1');
hField.value = 'zoo';
return true;
}
</script>
So in above, we set our value in js to zoo, and of course we do return true. If we return false then the asp.net button code server side will not run - so we can control this, or even say pop up a confirm dialog and return true/false based on that to control if the server side code behind will run.
Server side, code behind? You can now use this:
Protected Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Debug.Print(HiddenField1.Value)
End Sub
So the above is easy, clean. You can also use a text box, and set the style="display:none", but a hidden field is just as well and easy.
I’m trying to use a button click to open a page in a new tab/window. I’ve looked at solutions similar to this, but the answers given there are either to use a link or have it open in the same window. I need to use a button because it needs to generate the link based on criteria data entered in the form (string manipulation). This button is not submitting the form though; it’s just validating some of the data on an external site. I need this in a different window or tab so they can go back and forth between my form and the validation site. This is basically my current Button_Click event:
var Address = AddressTextbox.Text.Trim();
Address = Address.Replace(' ', '+');
var Url = "http://www.example.com/query?text=" + Address + "¶m1=foo¶m2=bar";
Response.Redirect(Url);
This works except that Response.Redirect(Url) only opens in the same window, not a new one.
Just spit out javascript code from the Click event handler on the Button.
var Address = AddressTextbox.Text.Trim();
Address = Address.Replace(' ', '+');
var Url = "http://www.example.com/query?text=" + Address + "¶m1=foo¶m2=bar";
Page.ClientScript.RegisterStartupScript(this.GetType(), "dsadas", "window.open('"+Url+"');", true);
Use the OnClientClick attribute:
<script type="text/javascript">
redirectToPage = function(url){
void(window.open(url, "child_window"));
return false;
}
</script>
<asp:Button ID="Button1" runat="server"
Text="Click Me!"
OnClientClick="return redirectToPage('/somefolder/somepage.aspx');" />
Using the OnClientClick attribute, it's important to remember to return false at the end, otherwise the button click will trigger a postback.
EDIT
If you want to open a new window from the click event handler, you can do something like this:
protected void Button1_Click(object sender, EventArgs e)
{
Page.ClientScript.RegisterStartupScript(Page.GetType(), "open_window",
string.Format("void(window.open('{0}', 'child_window'));", "/somefolder/somepage.aspx"), true);
}
Admittedly, this is kind of ugly, but if there's certain information that you won't have until the button click logic is executed, this might be your best option. If there's any way you can build the URL before the button is clicked, you can just assign the OnClientClick in code-behind.
I'm using Div's, now I have to handle 2 Events with one Div-Click..
On the Button I would take, once onclick="" and OnClientClick.. but if I use a Div I can't use OnClientClick :(
Can you guys tell me, what I shall do, to get this? (there is one JavaScript method and one codebehind method, which I want to call) - till now I can call only one.
after wizards help:
got this:
<asp:Button ID="fakeButton" runat="server" Text="dummy" onclick="dummyButton_Click" style="display:none" />
and JS:
alert("div clicked");
document.getElementById("<%=fakeButton.ClientID%>").click();
So but he dont enters the Method:
protected void dummyButton_Click(object sender, EventArgs e) { } ..
i putted a breakpoint, he never entered, only showed the alert.. –
First, add dummy hidden button with the proper server side click event:
<asp:Button id="fakeButton" runat="server" OnClick="MyMethod" Text="dummy" style="display: none;" />
Second, have such code for the DIV:
<div onclick="DivClick();">text here...</div>
And finally such client side code:
function DivClick() {
//your client side code here...
//e.g.
alert("div clicked");
//now invoke server side click as well:
document.getElementById("<%=fakeButton.ClientID%>").click();
}
This will first execute your client side code e.g. the alert, then "auto click" the button and by this causing post back and the server side code to execute.
If you don't want full reload, try putting it all inside UpdatePanel.
I have an application that I am currently writing that works by iterating through nodes, and then updating the page with the information of the current node. I have an UpdatePanel in the page which contains a label, textbox, and a button. The label lists the currently available children of the current node, the user enters in which child they want to go to into the textbox, and then hits the submit button. I set the new value of the node in the submit button's event handler.
Here's my problem: Every time I enter in which node I want to navigate to, the object resets its value to the value it was initially initialized to. I have even put this same code into a Windows Form to validate that it's working correctly to iterate through my tree, and it works as it should, so I know my problem is AJAX-related.
This is the first app that I have written using AJAX, so I am still in the process of learning how it works. Any help would be greatly appreciated. I have Googled and searched SO through and through.
Here is the HTML:
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager" runat="server"></asp:ScriptManager>
<asp:UpdatePanel ID="UpdatePanel" runat="server" UpdateMode="Conditional">
<ContentTemplate>
<asp:Label ID="question" runat="server" Text=""></asp:Label>
<br />
<asp:TextBox ID="answer" runat="server"></asp:TextBox>
<br />
<asp:Button ID="Submit" runat="server" Text="Submit" onclick="Submit_Click" />
</ContentTemplate>
</asp:UpdatePanel>
</form>
And the C#:
protected void Submit_Click(object sender, EventArgs e)
{
int ans = int.Parse(answer.Text);
if (!current.ChildIDs.Contains(ans))
{
return;
}
current = tree.Node(ans);
question.Text = current.Question;
}
current is the current node, which has a public ArrayList of all of its children's IDs. tree is the NodeTree I have; calling Node just returns the new node. Both current and Tree get initialized in the Page_Load event, and that only fires once (when the page is first loaded).
It's really pretty simply code; I'm just having difficulty understanding why the AJAX isn't working correctly.
I have even put this same code into a
Windows Form to validate that it's
working correctly to iterate through
my tree, and it works as it should, so
I know my problem is AJAX-related.
It sounds like you're expecting ASP.NET to remember what the object current is between requests, since that's how Windows forms applications work.
Web applications are stateless - after each request, ASP.NET discards all your variables. To access the variable during a subsequent request, you have to either:
1) Send enough data with the request to reconstruct the variable. You can do this using a querystring parameter or an HTML form value (the hidden fields another response mentioned).
2) Save the variables in a Session store (which can be in-memory or backed by a database).
3) Store the value in a coookie.
Of these three, it's easiest to show you how to use Session, given what you've shared in your question. However, beware: session has its risks - by default, ASP.NET session objects are stored in-memory, and it's a potential security hazard. But here's how you should be able to get your application to work.
// In your Page_Load code that initializes your 'current' variable
// When the user first requests the page, create a new Node
if (! this.IsPostBack)
{
Node current = new Node(); //
Session("currentNode") = current;
}
// When the user clicks a button on the page (posts), use the
// node in session instead
else
{
current = Session("currentNode");
}
When you update non-form elements in the browser (labels, literals, etc.), .NET is unable to see any of the changes you've made.
Try adding a hidden input for each label that records the new value. Then within the method you have wired up to the button's OnClick event, do something like this:
myLabel.Text = myHiddenInput.value;
I think you just need to tell the updatepanel to update itself. Try this:
protected void Submit_Click(object sender, EventArgs e)
{
int ans = int.Parse(answer.Text);
if (!current.ChildIDs.Contains(ans))
{
return;
}
current = tree.Node(ans);
question.Text = current.Question;
UpdatePanel.Update();
}
I have a LinkButton that has to postback to perform some logic.
Once it is finished, instead of loading the page back up in the browser, I want to leave it alone and pop open a new window.
So far, the best idea I've had is to put the LinkButton in an UpdatePanel, and have it render some JavaScript out when it reloads, yet I think that is totally hacky. Also, if I recall right, JavaScript within a update panel won't run anyways.
Any other ideas?
Use LinkButton.PostBackUrl to set a different page to POST to, and some client script to get a new window (and the old target restored so that future postbacks work normally). The 2nd page can use PreviousPage to get access to any needed state from the original page.
<script runat="server">
void lnk_Click(object sender, EventArgs e) {
// Do work
}
</script>
<script type="text/javascript">
var oldTarget, oldAction;
function newWindowClick(target) {
var form = document.forms[0];
oldTarget = form.target;
oldAction = form.action;
form.target = target;
window.setTimeout(
"document.forms[0].target=oldTarget;"
+ "document.forms[0].action=oldAction;",
200
);
}
</script>
<asp:LinkButton runat="server" PostBackUrl="Details.aspx" Text="Click Me"
OnClick="lnk_Click"
OnClientClick="newWindowClick('details');" />
Here is the code:
protected void Button1_Click(object sender, EventArgs e)
{
// Do some server side work
string script = "window.open('http://www.yahoo.com','Yahoo')";
if (!ClientScript.IsClientScriptBlockRegistered("NewWindow"))
{
ClientScript.RegisterClientScriptBlock(this.GetType(),"NewWindow",script, true);
}
}
One thing you could try is to have your LinkButton OnClick event do its processing, then register a Page.ClientScript.RegisterStartupScript with the popup code, which will put some Javascript into the tag to fire off after the page loads. This should launch your new window after the processing completes.
EDIT: Reading your comment, I believe you can still use this approach, have your results stored in a session variable, and then have the popup page pull the results from there.