Peter Bell was looking for an example of how to get data from a pop-up window back into a calling (opener, parent) window. I am not saying this is by any means a "best" practice, but this is how I do it. There are, of course, two pages: the pop-up and the primary. The pop-up needs a way to pass data back to the primary page. The primary page needs a way to handle the data.
The easiest way to accomplish this chain of communication is to pass the name of a Javascript call back method to the pop-up window. The pop-up window has a reference to the parent window (window.opener) and can then invoke the call back method through that window reference. Once the pop-up window has the reference, we can store it in a form field (if the page has a form with refresh-potential) so that we never lose the reference.
The beauty behind this is that we are coding to an interface. The pop-up window doesn't need to know how the data is being handled; it only needs to know that the data can be taken by some method. The parent (opener) doesn't need to know anything about the pop-up, it only needs to know that its call back method might be invoked. The only real agreement that has to happen between the two pages is that the data will be passed in some standard form (ie. an array, an object, a comma delimited list).
Ok, so let's take a look at the primary page:
Launch code in new window » Download code as text file »
Not much is going on here. The page just launches the pop-up and sends, as part of the query string, the textual name of the call back method. Then, it just sits there and waits. Notice that the call back method, PopUpHandler(), doesn't know anything about the pop-up or where the data is coming from - it just knows that if it gets data to add it to the form field.
Now, let's take a look at the pop-up window:
Launch code in new window » Download code as text file »
Again, not a ton of stuff going on. The Javascript is more complicated because we are doing our best to alert the user if something went wrong (ie. the parent window was closed and the communication chain was no longer valid). Other than that, it's just a standard ColdFusion page with a form. The only difference is that instead of submitting the form, we are calling a method that bundles the data and attempts to send it to the call back method of the parent window.
Also, note that we are actually storing the name of the call back method in the form field. If this page was a fairly complex page with search capabilities and pagination, the page might refresh. In that case, we might lose the call back method name. By storing it in the FORM and CFParaming the URL and FORM scopes, we help to ensure that this method name will never be lost.
So again, maybe not a best practice, but this methodology has worked nicely for me when I need to use it.
Download Code Snippet ZIP File
Comments (32) | Post Comment | Ask Ben | Permalink | Other Searches | Print Page
Force ColdFusion Server To Recompile A ColdFusion Template
Frameworks vs. Object Oriented Programming
That is a sensible approach. You can also do this by convention across a system and avoid having to have the server generate your javascript. You can document that popups can always call the WINDOWNAME_response(responseObject) function to post data back to the opener. This lets you have different handlers for different popups.
Same way from the javascript perspective, but you just cutout the CFOutput step in your popup javascript.
Posted by Chip Temm on May 2, 2007 at 2:50 PM
@Chip,
Not a bad idea at all. Conventions always help write better code, the way I see it. The only slip up would be if there were two places on a screen that could handle the call back (with two different purposes). In that case, maybe there could be an override that could be used.
Posted by Ben Nadel on May 2, 2007 at 3:50 PM
This is great - just what I needed to know - and thanks Chip for the comment. Also a good point. Life is looking good. Even *I* can understand this Javascript!
Posted by Peter Bell on May 2, 2007 at 5:20 PM
Sweeeet. Glad that we could help.
Posted by Ben Nadel on May 2, 2007 at 5:21 PM
Ben,
a couple of points to consider:
1. You could use something like "Form.serialize(objForm)" to serialize all the form values. I'm talking about Prototype.js but I'm sure jQuery (your favorite) has a similar method.
2. Popup windows are kind of old school now. Try overlay windows. There is one I've used called Lightwindow (based on Prototype and Scriptaculous): http://stickmanlabs.com/lightwindow/ or the jQuery equivalent: http://jquery.com/demo/thickbox/. The advantage of these are that 1) The look way more cool with much more options and 2) Your invoke happens from the same page and stays on the same page so there is no need to pass vars back and forth.
Posted by Boyan on May 3, 2007 at 11:16 AM
@Boyan,
I agree. The pop-up is old school. I have just not gotten around to implementing something uber cool like the thick box or light box or whatever else there is. One day :)
Posted by Ben Nadel on May 3, 2007 at 11:20 AM
Overlay windows look cool, but I'm not sure how I'd implement that in practice as the window needs to be able to go back to the server for filtering or paging through the recordset, so I'd have to make the entire iFramed page using AJAX otherwise on clicking links it'd reload the whole page - not just the overlay window.
Posted by Peter Bell on May 3, 2007 at 11:24 AM
Yeah, that's part of the reason I have not tried it yet.... the PopUp is just so easy cause there are no concerns.
Posted by Ben Nadel on May 3, 2007 at 11:26 AM
It's your lucky day I guess. You don't have to do anything special with LightWindow for calling server side pages. I just did one that calls an .cfm page with Ajax. The setting is just another parameter.
Posted by Boyan on May 3, 2007 at 11:38 AM
Yep, but what happens when THAT page then wants to call another page into the same LightWindow without refreshing the whole page? That was the problem I had.
Posted by Peter Bell on May 3, 2007 at 12:00 PM
Since you create the lightwindow in the first page, you can reinitialize it in script any time you want. You can even write javascript to change the content of the lightwindow.
Posted by Boyan on May 3, 2007 at 2:50 PM
Right, but my use case is that I want to open a window (which uses almost identical code I would use elsewhere) to list, filter, order and search through a recordset so the code in the window I open needs to be able to go back to the server without knowing anything about the fact that it is in a sub window.
I know I could do this is the screen was AJAX based, but unless I make all calls back the server AJAX, I don't think anything other than a pop up window would work (unless I'm misunderstanding this).
Posted by Peter Bell on May 3, 2007 at 4:59 PM
Hmm...maybe I didn't make it clear enough. You can tell lightwindow to use .cfm page for the content of the window (this pulls the page from the server with Ajax). That way you code your page like it would any other page but it's loaded in the lightwindow like anything else. In your use case, the contents of the lightwindog page will to go back to the server without knowing anything about the fact that it is in a sub window.
If you give me something more specific, I can probably put together an example.
Posted by Boyan on May 4, 2007 at 8:49 AM
@Boyan,
I think what Peter means is that that pop-up is not displaying a single page of data. Imagine that the pop-up has a search page where the user can filter the data they are looking for. That would require a form submission. If this was in a pop-up, the form would submit, the page would refresh, the new data would be displayed.
If this was in a lightbox style interface, then how would that search form be submitted? It could not be done using a form submission as that would cause the entire page to submit, not just the lightbox area. Instead, he would have to have AJAX handle the form submission itself and then refresh the contents of the lightbox window.
I am not saying that it is not possible; I think Peter (and myself) is just concerned that having a "pop-up" live within the context of another page makes updating the pop-up more difficult.
Posted by Ben Nadel on May 4, 2007 at 8:53 AM
Ben, that's an interesting point. Let me try that today and see how it works. I'll let you know.
Posted by Boyan on May 4, 2007 at 9:00 AM
@Boyan,
Certainly you could have some sort of flag in the URL for the "pop-up" that toggles the FORM action:
<cfif URL.use_ajax>
action="javascript:DoSomethingWithAjax();" onsubmit="return(false);"
<cfelse>
action="#CGI.script_name#"
</cfif>
Of course, this would assume that only standard submits work and that this can be "generalized" to work with lots of different pages. Just a thought.
Posted by Ben Nadel on May 4, 2007 at 9:02 AM
Hi Boyan,
That'd be great (and thanks for taking so much time on this). I know the long term solution is to use a lightwindow which loads a page that then uses AJAX for all its form submissions and links, but that now means that to get this feature working I have to recode all of my generic paging, filtering and ordering code that currently just returns to the server for a page refresh to use AJAX which is something I should do anyway but is not a completely trivial task.
That said, if there is any way I can load the html from a cfm into a lightwindow where the cfm page doesn't need to know it is in a sub window and can still return to the server and do a page refresh that only refreshes the content of the light window, that'd be very cool, but I'm not sure that makes any technical sense . . .
Posted by Peter Bell on May 4, 2007 at 9:04 AM
@Ben, The problem with that is I need to then write a generalized back end that'll accept those requests at which point I probably wouldn't even both making it conditional - I'd just make all of my ordering, paging and filtering AJAX (which is something I'm considering anyway). But it isn't a trivial task - especially when you consider the security implications and the right approaches to handling those.
Posted by Peter Bell on May 4, 2007 at 9:07 AM
@Peter,
Certainly not a trivial task at all. I can only imagine (as I still go the standard pop-up route). It will be interesting to see where this goes.
Posted by Ben Nadel on May 4, 2007 at 10:50 AM
Ben,
Thanks very much for this script. It worked like a charm, even for a js novice like me, but for one issue. Once completing my form, if I navigate away from the page and then back again, I lose all the data.
For example, if I submit the form without completing some required field, I get the data validation message inviting me to go back to the form. When I do so the all the fields are empty. This doesn't happen unless I've passed data using the popup.
Any suggestions? Thanks in advance.
--r
Posted by Rick on Jul 24, 2007 at 2:34 PM
@Rick,
I am not sure I am fully understanding. Which page is refreshing? The pop-up or the primary window?
Posted by Ben Nadel on Jul 24, 2007 at 3:10 PM
The primary window.
Posted by Rick on Jul 24, 2007 at 4:07 PM
Ben,
Further update. It appears to be an IE problem. Everything works fine in Firefox.
r
Posted by Rick on Jul 24, 2007 at 5:38 PM
Ben,
I found a workaround. If I save the form as .htm, rather than .cfm the form data persists. Don't know why this is so, but I can live with the form being .htm. If you have any ideas on this, I'd still be interested, as it may come up again. In any case, your script has been a huge help. Thanks.
rl
Posted by Rick on Jul 25, 2007 at 1:29 PM
@Rick,
Sorry for the delayed response. At first, I was confused, because I was trying to make the connection between the pop-up window and the error you are having... but, then when you said it was the primary window that was refreshing, I realized that this does not have anything to do with the pop-up :)
The issue you are having is dependent on the browser, I think, and how it caches form data. I don't really know anyway to get around that other than you should probably repopulate the form manually (via ColdFusion) when the people come back to the form maybe??
Posted by Ben Nadel on Jul 25, 2007 at 1:58 PM
Ben,
Thanks for your response. As I indicated in my previous post, it's a moot point, since I found a workaround.
FWIW, it does seem to have something to do with the popup. If I fill in the form data (except for the fields populated by the popup) and navigate away and then back, the form data persists. But, once I open the popup, if I navigate away, my date is lost. I emailed you a link to an example of this.
Thanks.
Posted by Rick on Jul 25, 2007 at 2:11 PM
@Rick,
That is interesting that the behavior changes... the link you sent doesn't seem to have any pop-up stuff (or maybe I am just not seeing it). But it looks like you are using ColdFusion built-in form field validation, is this correct?
Posted by Ben Nadel on Jul 25, 2007 at 2:26 PM
Nifty! Definitely the best description of this technique I've found.
I discovered something curious though. (I running a Windows version of IE6.) I don't believe this is a problem in Firefox.
It seems like any variable can be passed from the child to primary window except for 2-dim Arrays (or rather Array of Arrays). Weird, eh? Strings... good. Arrays look good. Even simple JSON objects pass back to the primary window.
I can see someone interested in passing a JSON object back to the primary window, but BEWARE. Don't include any 2-dim Arrays in your JSON. It appears to populate the primary window's global JavaScript variable, but after closing the popup window the variables return to their original primary window state.
Please somebody prove me wrong.
I guess the other possibility is to pass back a string and perform an eval() to create a real JSON object. Ain't pretty, but it'd work.
Posted by ArdMan on Aug 3, 2007 at 5:58 PM
@ArdMan,
Very strange indeed. I have not come across that, but I have also never tried that either.
Posted by Ben Nadel on Aug 6, 2007 at 2:04 PM
Thank you so much for this! I have been pulling my hair out trying to find away to do this. I have tried many different methods... none worked. This was easy and works perfectly.
Posted by Kim Tompkins on May 28, 2008 at 7:10 PM
@Kim,
Glad to be of service.
Posted by Ben Nadel on May 28, 2008 at 7:11 PM
This worked like a champ! Thank you, thank you!
Posted by Emily on Jun 27, 2008 at 4:26 PM