does showModalDialog interfere with ClientScript.RegisterStartupScript? - asp.net

I'm showing a modal dialog via "window.showModalDialog(..." which happens in a vbscript function (the page shown is aspx). I'd like to do some resizing of the window based on the number of rows in a datatable that's coming back. So naturally I go to register a startup script that resizes the window based on the number of rows. Well, that didn't work, so I tried to register a script that just showed a msgbox.
The code looks like (in the OnLoad event handler):
if (!this.ClientScript.IsStartupScriptRegistered(typeof(MyPageClassName), "hello"))
{
this.ClientScript.RegisterStartupScript(typeof(MyPageClassName), "hello",
#"<script language=vbscript>
sub fnWindowOnLoad()
MsgBox ""hello""
end sub
<script>", false);
}
if (!this.ClientScript.IsStartupScriptRegistered(typeof(MyPageClassName), "hello"))
{
throw new Exception("Failed to load script");
}
To me it looks like this should work and show a message box that says "hello" when the page loads (I've got the window's onload event set to fnWindowOnLoad). But what happens is nothing, no exception, no alert. I've tried every Type I could think of in the typeof call. Nothing seems to work. The only thing I can think of is that since the dialog is a modal ClientScript.RegisterStartupScript won't run properly. But that doesn't make any sense to me.
I put the MsgBox "hello" call into my script block directly and the alert showed, so it's possible. But I need to modify some arguments in the code behind so I have to use RegisterStartupScript as far as I can tell.

Have you tried opening your window via window.open() rather than window.showModalDialog()? I've seen some postings on the web about incompatibilities between showModalDialog() and RegisterStartupScript.
showModalDialog() is an IE only method, so it's not recommended anyway. I know it's convenient because it returns a value, but there are various ways to simulate this functionality.
Edit: The other problem with showModalDialog() is that IE often caches the results. This means that if one time you calling the dialog, you do not resize it, then another time you do, then 2nd time might get your the first cached dialog. A way to get around this is to add a unique querystring at the end. Like MyDialog.aspx?q=320934 (randomly generated or generated based on server tics).

The solution for this was to have a script that read a value out of a hidden field and then resized the dialog. The value was set on the Page_Load. Using RegisterStartupScript never seemed to work, neither did RegisterClientScript, so I'm pretty sure modal dialog and RegisterXxx don't get along. Need to use window.dialogHeight & window.dialogWidth in the vbscript.

Related

Print Friendly Page

So I would like to be able to have a print button for entries in our database so users can print an entry via a print friendly "form".
My thought was to create a separate page, add labels and have those labels pull the relevant information.
I know I can add the open widget information via this code:
app.datasources.ModelName.selectKey(widget.datasource.item._key);
app.showPage(app.pages.TestPrint);
But I'm running into a few problems:
I can't get the page to open in a new window. Is this possible?
window.open(app.pages.TestPrint);
Just gives me a blank page. Does the browser lose the widget source once the new window opens?
I can't get the print option (either onClick or onDataLoad) to print JUST the image (or widget). I run
window.print();
And it includes headers + scroll bars. Do I need to be running a client side script instead?
Any help would be appreciated. Thank you!
To get exactly what you'd want you'd have to do a lot of work.
Here is my suggested, simpler answer:
Don't open up a new tab. If you use showPage like you mention, and provide a "back" button on the page to go back to where you were, you'll get pretty much everything you need. If you don't want the back to show up when you print, then you can setVisibility(false) on the button before you print, then print, then setVisibility(true).
I'll give a quick summary of how you could do this with a new tab, but it's pretty involved so I can't go into details without trying it myself. The basic idea, is you want to open the page with a full URL, just like a user was navigating to it.
You can use #TestPrint to indicate which page you want to load. You also need the URL of your application, which as far as I can remember is only available in a server-side script using the Apps Script method: ScriptApp.getService().getUrl(). On top of this, you'll probably need to pass in the key so that your page knows what data to load.
So given this, you need to assemble a url by calling a server script, then appending the key property to it. In the end you want a url something like:
https://www.script.google.com/yourappaddress#TestPage?key=keyOfYourModel.
Then on TestPage you need to read the key, and load data for that key. (You can read the key using google.script.url).
Alternatively, I think there are some tricks you can play by opening a blank window and then writing directly to its DOM, but I've never tried that, and since Apps Script runs inside an iframe I'm not sure if it's possible. If I get a chance I'll play with it and update this answer, but for your own reference you could look here: create html page and print to new tab in javascript
I'm imagining something like that, except that your page an write it's html content. Something like:
var winPrint = window.open('', '_blank', 'left=0,top=0,width=800,height=600,toolbar=0,scrollbars=0,status=0');
winPrint.document.write(app.pages.TestPage.getElement().innerHTML);
winPrint.document.close();
winPrint.focus();
winPrint.print();
winPrint.close();
Hope one of those three options helps :)
So here is what I ended up doing. It isn't elegant, but it works.
I added a Print Button to a Page Fragment that pops up when a user edits a database entry.
Database Edit Button code:
app.datasources.ModelName.selectKey(widget.datasource.item._key);
app.showDialog(app.pageFragments.FragmentName);
That Print Button goes to a different (full) Page and closes the Fragment.
Print Button Code:
app.datasources.ModelName.selectKey(widget.datasource.item._key);
app.showPage(app.pages.ModelName_Print);
app.closeDialog();
I made sure to make the new Print Page was small enough so that Chrome fits it properly into a 8.5 x 11" page (728x975).
I then created a Panel that fills the page and populated the page with Labels
#datasource.item.FieldName
I then put the following into the onDataLoad for the Panel
window.print();
So now when the user presses the Print Button in the Fragment they are taken to this new page and after the data loads they automatically get a print dialog.
The only downside is that after printing the user has to use a back button I added to return to the database page.
1.
As far as I know, you cannot combine window.open with app.pages.*, because
window.open would require url parameter at least, while app.pages.* is essentially an internal routing mechanism provided by App Maker, and it returns page object back, suitable for for switching between pages, or opening dialogs.
2.
You would probably need to style your page first, so like it includes things you would like to have printed out. To do so please use #media print
ex: We have a button on the page and would like to hide it from print page
#media print {
.app-NewPage-Button1 {
display : none;
}
}
Hope it helps.
1. Here is how it is done, in a pop up window, without messing up the current page (client script):
function print(widget, title){
var content=widget.getElement().innerHTML;
var win = window.open('', 'printWindow', 'height=600,width=800');
win.document.write('<head><title>'+title+'/title></head>');
win.document.write('<body>'+content+'</body>');
win.document.close();
win.focus();
win.print();
win.close();
}
and the onclick handler for the button is:
print(widget.root.descendants.PageFragment1, 'test');
In this example, PageFragment1 is a page fragment on the current page, hidden by adding a style with namehidden with definition .hidden{display:none;} (this is different than visible which in App Maker seems to remove the item from the DOM). Works perfectly...
2. You cannot open pages from the app in another tab. In principle something like this would do it:
var w=window.parent.parent;
w.open(w.location.protocol+'//'+w.location.host+w.location.pathname+'#PrintPage', '_blank');
But since the app is running in frame nested two deep from the launching page, and with a different origin, you will not be able to access the url that you need (the above code results in a cross origin frame access error). So you would have to hard code the URL, which changes at deployment, so it gets ugly very fast. Not that you want to anyway, the load time of an app should discourage you from wanting to do that anyway.

jquery .load( ) and trigger function AFTER new content loads? just like JavaScript onload event

Using jquery, I am swapping some content in a web page by use of jquery's .load() function. I want to trigger an event immediately once the content has actually been loaded, but not before and not after. I'm up for any graceful solution! Why? In this instance, I'm doing the old "fade out, swap content, fade in" approach. My problem? I want to fade back in AS SOON AS the new content is loaded.
Caveats:
Using a callback function as in $('#object').load(url, callback) triggers as soon as .load() function successfully executes (before the content is actually loaded). Useless here.
Using a timed delay for fading back in is not a graceful solution. Very "clunky", especially for those with faster Internet connectivity.
JavaScript's onload event trigger does not work, as the element that .load() is altering has already loaded into the HTML DOM.
jquery's .ready() function also does not work, as the actual element is already loaded.
I do not want to create an onload or .ready() sub-container element, because that's a workaround for what I'm actually trying, though it might be as graceful or more.
How can I fire a function when (and only when) the new .load() content is finally loaded, just like JavaScript's onload event does? Thanks much.
EDIT As it turns out, the jquery .load() function is working flawlessly, and I'm approaching this wrong.
Once the .load() function completes successfully, it calls any "callback" function included by the programmer, just like any other jquery function that accepts a callback as one of its "arguments".
The .load() function is complete once it either errors or successfully begins the HTML replacement and loading of new content, but that is IT! The content will then take however long it takes to load, but your .load call is already complete before that. Therefore, expecting the callback to run after the .load content has loaded will only disappoint you. ;)
I hope others can learn from this just as I did, including those who thought what I thought was the case. Proof: as stated in the jquery ajax .load page, the callback is executed when the request completes, not when the load completes. Eureka. Whoops. /EDIT
Try using a different method rather than load(), I would suggesting using get(). Something like this may be more useful to you...
var jqxhr = jQuery.get(url,vars);
jqxhr.success(function(data){
# This will only be called once the remote content has been loaded in
# The data will then be stored in the data param and can be used within your site
});
jqxhr.error(function(data){
# Something went wrong, never mind lets just handle it gracefully below...
});
I hope this is a solution to your problem!
For more information see http://api.jquery.com/jQuery.get/
I have quickly created this function below that may be of help to you... its not refined!
jQuery.fn.loadNewData = function() {
var object = jQuery(this);
var jqxhr = jQuery.get(arguments[0]);
// check for success
jqxhr.success(function(data) {
// load the data in
object.html(data);
});
jqxhr.error(function(){
alert('Failed to load data');
});
}
Using this you can call how similarly to how you would call the load() function.
jQuery('#object').loadNewData(url);
I think you might be misinterpreting what you are seeing. Depending on the browser you are using you won't see the new elements in the browser if you pop up an alert in the callback because it won't rerender the DOM until you cede control back to the browser. That doesn't mean you can't grab the new elements from the DOM and start fading them in. Take the following jsfiddle: http://jsfiddle.net/ttb55/8/ in Chrome it will show the first div when the second alert is up, then fade in the second div. In IE it won't show the first div when the second alert is up, this is the state I think you are in after load during the callback, but it still works once you hit ok because everything was in the DOM as promised.
Upon reading the jQuery docs pages for jQuery.get() and jQuery.load(), the callback argument is quoted as the following:
"A callback function that is executed if the request succeeds."
Let me stress the terms "request" and "succeeds". The request may succeed, but that does not mean that the content is loaded. Same problem as .load() — the functions aren't built the way I was thinking.
If I want to trigger an event once the new content finally loads, I'll need to take a different approach.
I could use the JS onload event, and trigger it by completely replacing an HTML element (having the replaced code contain an onload property). EDIT: Note that using HTML iframe elements is pretty awful, primitive, and "clunky". I just need to find a better way to trigger a function as soon as loading the new content finishes.
Also, I could use jQuery to check the .ready() state of new content, but ("AFAIK" / as far as I know) that will only work if the checked content is a new HTML element, not a preexisting HTML element whose interior content is changed. The jQuery .ready() status of any preexisting element will (AFAIK) already be shown as "ready" despite if new content is loading. Perhaps I am wrong about this, and I would like to be corrected if so.
Unless otherwise notified, this answer will be marked as the correct one. The original question was mistaken that .load() was all I needed. Cheers!

JavaScript puzzle to solve : window.confirm = divConfirm(strMessage)

Scenario is : old site which has lots of JS code already written. If user want to change all the alert messages to new age jazzy Div based alert which are very common using JQuery, YUI, Prototype... etc.
There are mainly tree JS dialogs
1. alert
To changes this its simple we just have to write new function which will show the div popup and show the message, after that override the window.alert
function showDivAlert(strMessage){
//div popup logic and code
}
window.alert = showDivAlert;
2. prompt
This too look easy to write function to accept the string and show the text box for input value. Now as return action is based on the click of "OK" button life is easy here.
function shoDivPromp(strMessage){
//div pop up to show the text box and accept input from the user
}
window.prompt = shoDivPromp;
3. confirm
Now above two were easy to override and modify the default dialogs but there is complication with the confirm.
However default JS confirm dialog stops JS execution and when user click OK or Cancel execution is resumed by determining the return value (true/false). But if we user div popup the execution is not stopped which is problem. We can still implement the confirm but in that case we have to bind methods for OK and CANCEL case which will be attached to OK and CANCEL button. With this function signature will be like.
function newConfirm(msg, fun OkAction(), fun CancelAction)
Now this is problem that this cant help me change the confirm dialog across the site as we did with alert();
Question
I am not sure whether its possible or not to achieve but i think can be using some JS pattern. So let me know if its possible.
Now this is problem that this cant help me change the confirm dialog across the site as we did with alert();
That's correct. It's not possible to reproduce the synchronous nature of the alert/confirm/prompt functions in native JavaScript. There is the non-standard method showModalDialog which can do it using a separate pop-up document, but it's not supported by all browsers and it's generally considered highly undesirable.
So the plug-in-replacement strategy isn't going to work. You are going to have to change every place you called these methods in the rest of the script.
The usual pattern is to do it using inline anonymous functions, to preserve the local variables using a closure, eg. replace:
function buttonclick() {
var id= this.id;
if (confirm('Are you sure you want to frob '+id+'?'))
frob(id);
wipe(id);
}
with:
function buttonclick() {
var id= this.id;
myConfirm('Are you sure you want to frob '+id+'?', function(confirmed) {
if (confirmed)
frob(id);
wipe(id);
});
}
If you need this to be preserved you would need to look at a further nested closure or function.bind to do it. If you have your call to confirm in a loop things get considerably more difficult.
Obviously you also have to ensure that critical global state doesn't change whilst the confirm box is up. Usually this risk is minimised by greying out the rest of the page with an overlay to stop clicks getting through. However if you have timeouts they can still fire.
All 3 methods actually stop js execution, not just the confirm, because they're all modal dialogs. Personally, I would try to keep everything as asynchronous as possible as modal dialogs prevent interaction with the current document.
Your best bet is to use callback functions from the new confirm popup as you suggested yourself.
I'm having a hard time understanding exactly what you want to achieve. It sounds like you want to do something like the following:
Run some javascript code
Display a "confirm" box
Wait until the ok button or cancel button is clicked
Continue code when user clicks ok, return when user clicks cancel.
The reason you want to do this is that overriding the function with something that makes use of callbacks would require rewriting each section of code that uses the confirm function. If you want my advice, I would go ahead and rewrite the code so that it performs asynchronously. There's no way you can delay script execution without locking up the document which includes the OK and Cancel actions of your dialog.
if you changed the roles Alert / Prompt / Confirm. slows the execution pending user interaction to run the following code.
Overriding these functions, the code continues its execution.
To achieve this you have to modify each part of the code and work as if you were with asynchronous functions.
Then you can use any plugin for windows as sexy-alert-box, and overwrite Alert / Prompt / Confirm
The function signature would simply be:
function newConfirm(msg, okAction, cancelAction);
and would be used as:
function newConfirm(msg, okAction, cancelAction){
var ok = doWhateverPromptIsNecessary();
if (ok) {
okAction();
} else {
cancelAction();
}
}
That is, to pass function "pointers" in to a function as arguments, simply pass in the function name without the (). The function signature is the same.

Asp.net and JavaScript pop up windows

I am writing an intranet application and am considering the use of a pop up window. I am not worried about accessibility since it's an intranet app.
The scenario is such as I need to be able to have the same code be used in a server page as well as in the middle of a process; which is why I decided when using it in the middle of the process, it's best to have it as a pop up window to running out of the real estate on the screen.
Any thoughts on this? I am hesitant to use a pop up window in such a manner as I usually only use it for error messages.
I don't completely understand what you're trying to do, but I think a popup window might be somewhat of an issue if the user's browser automatically blocks popup windows. Plus, if you were trying to run a process in the popup window, the user could close it and no longer have a way to check on the process.
Would it be possible to use Ajax to call back to a web service that gives the page information about the process? You could give the user a way to make the Ajax call to check on the status of the process or just have it continually polling in the background.
Edit:
You said you weren't too familiar with Ajax. For the most part, there are libraries to handle all the of hard details. I'll recommend jQuery because that's what I've been using for a while now.
If you go the Ajax route you'll be able to contain everything on one page and make the updates you need to make when the Ajax call is successful. Depending on how you write the code, it should be pretty reusable if you do it right. It really depends on how specific the your needs on each page.
Take a look at the jQuery documentation though. It may have what you need already built into it. Otherwise, someone else might be able to suggest some reasons why their favorite JavaScript library works better for what you're trying to do.
I think you might want to do something like this:
Inside of the parent page:
<input id="btnShowModal" runat="server" type="button" value='Show Modal' onclick="ShowModal()" />
function ShowModal()
{
var retVal = window.showModalDialog("MyPopup.aspx?param1=value","","center=yes;dialogWidth=200px;dialogHeight=200px;status:0;help:0")
if(retVal != "" && retVal != undefined)
{
//This code will be executed when the modal popup is closed, retVal will contain the value assigned to window.returnValue
}
}
Inside of the modal popup:
<input id="btnSave" runat="server" type="button" value='Save' onclick="Save()" />
function Save()
{
window.returnValue = "Whatever you want returned to the parent here"
window.close()
}
The usual argument against popup windows is that they are unreliable. The user may have disabled script initiated popups, I know I have.
In a controlled environment, such as an inranet, you may be able to be guaranteed that this is not the case, but even so, why risk it, there is an alternative.
Instead of popping up a new window just insert a new, absolutely positioned <div> into the document and insert your content into that using ajax or even an <iframe>. There are lots of examples/libraries on the web.
Thickbox for jQuery for example. There are of course scripts that don't require libraries.
I generally use a div with a z-index and absolute positioning; the .show() can be written and called on demand, it would have a button to .close(), and AJAX can make it seem modal so it must be clicked to close if you so desire. Then again, I hate messageboxes.
I was trying to avoid AJAX, simply because I have never used and don't have the time frame to learn it now. However, I am not totally opposed to it.
In short what I need to do is for the pop up window interact back with the page. Imagine that on the page I am building the links of the chain. Each link has unique properties. When user clicks on "ADD LINK" button, I was thinking have a pop up window with the little form and a Save button. The only issue with this is that a pop up needs to interact with the page; we need to know when something has been saved or not saved.
A div on the same page is one way. A pop up is yet another way. Another catch is that this code (adding new link) needs to be reusable, because I am also going to have a page that just creates new links.

Setting Focus with ASP.NET AJAX Control Toolkit

I'm using the AutoComplete control from the ASP.NET AJAX Control Toolkit and I'm experiencing an issue where the AutoComplete does not populate when I set the focus to the assigned textbox.
I've tried setting the focus in the Page_Load, Page_PreRender, and Page_Init events and the focus is set properly but the AutoComplete does not work. If I don't set the focus, everything works fine but I'd like to set it so the users don't have that extra click.
Is there a special place I need to set the focus or something else I need to do to make this work? Thanks.
We had exactly the same problem. What we had to do is write a script at the bottom of the page that quickly blurs then refocuses to the textbox. You can have a look at the (terribly hacky) solution here: http://www.drive.com.au
The textbox id is MainSearchBox_SearchTextBox. Have a look at about line 586 & you can see where I'm wiring up all the events (I'm actually using prototype for this bit.
Basically on the focus event of the textbox I set a global var called textBoxHasFocus to true and on the blur event I set it to false. The on the load event of the page I call this script:
if (textBoxHasFocus) {
$get("MainSearchBox_SearchTextBox").blur();
$get("MainSearchBox_SearchTextBox").focus();
}
This resets the textbox. It's really dodgy, but it's the only solution I could find
this is waste , its simple
this is what you need to do
controlId.focus(); in C#
controlID.focus() in VB
place this in page load or button_click section
eg. panel1.focus(); if panel1 has model popup extender attached to it, then we put this code in page load section
How are you setting focus? I haven't tried the specific scenario you've suggested, but here's how I set focus to my controls:
Public Sub SetFocus(ByVal ctrl As Control)
Dim sb As New System.Text.StringBuilder
Dim p As Control
p = ctrl.Parent
While (Not (p.GetType() Is GetType(System.Web.UI.HtmlControls.HtmlForm)))
p = p.Parent
End While
With sb
.Append("<script language='JavaScript'>")
.Append("function SetFocus()")
.Append("{")
.Append("document.")
.Append(p.ClientID)
.Append("['")
.Append(ctrl.UniqueID)
.Append("'].focus();")
.Append("}")
.Append("window.onload = SetFocus;")
.Append("")
.Append("</script")
.Append(">")
End With
ctrl.Page.RegisterClientScriptBlock("SetFocus", sb.ToString())
End Sub
So, I'm not sure what method you're using, but if it's different than mine, give that a shot and see if you still have a problem or not.
What I normally do is register a clientside script to run the below setFocusTimeout method from my codebehind method. When this runs, it waits some small amount of time and then calls the method that actually sets focus (setFocus). It's terribly hackish, but it seems you have to go a route like this to stop AJAX from stealing your focus.
function setFocusTimeout(controlID) {
focusControlID = controlID;
setTimeout("setFocus(focusControlID)", 100);
}
function setFocus() {
document.getElementById(focusControlID).focus();
}
I found the answers from Glenn Slaven and from Kris/Alex to get me closer to a solution to my particular problem with setting focus on an ASP.NET TextBox control that had an AutoCompleteExtender attached. The document.getElementById(focusControlID).focus() kept throwing a javascript error that implied document.getElementById was returning a null object. The focusControlID variable was returning the correct runtime ClientID value for the TextBox control. But for whatever reason, the document.getElementById function didn't like it.
My solution was to throw jQuery into the mix, as I was already using it to paint the background of any control that had focus, plus forcing the Enter key to tab through the form instead of firing a postback.
My setFocus function ended up looking like this:
function setFocus(focusControlID) {
$('#' + focusControlID).blur();
$('#' + focusControlID).focus();
}
This got rid of the javascript runtime error, put focus on the desired TextBox control, and placed the cursor within the control as well. Without first blurring then focusing, the control would be highlighted as if it had focus, but the cursor would not be sitting in the control yet. The user would still have to click inside the control to begin editing, which would be an UX annoyance.
I also had to increase the timeout from 100 to 300. Your mileage my vary...
I agree with everyone that this is a hack. But from the end-user's perspective, they don't see this code. The hack for them is if they have to manually click inside the control instead of just being automatically placed inside the control already and typing the first few letters to trigger the auto lookup functionality. So, hats off to all who provided their hacks.
I hope this is helpful to someone else.

Resources