pondělí 7. června 2004

JavaScript a zpětné volání mezi okny

Občas dostávám úlohy, které zpočátku vypadají zapeklitě, ale postupem času mi přijdou jednoduché. Nejinak tomu bylo při řešení problému, kdy bylo třeba zajistit zpětné volání rodičovského okna z okna dceřiného. Součást rodičovského okna tvořil formulář, pro vyplňování číselníkových hodnot tohoto formuláře se otevíralo dceřiné okno s číselníkem, ze kterého si při výběru přenesly hodnoty do okna rodičovského.

Pro zvýšení uživatelského komfortu vzniknul požadavek, aby bylo možné při vložení hodnoty z číselníku nastavit např. formulářové nabídky či provést jinou akci. Celý problém se musel vyřešit tak obecně, aby to nijak neovlivnilo klientské skripty, které číselníky používají, ale tuto funkčnost nepotřebují.

Jak na to

Pokud je potřeba zprostředkovat komunikaci mezi dceřiným a rodičovským oknem tak nemůžeme začít nikde jinde, než u objektu opener, ve kterém má každé otevřené okno vytvořené voláním window.open(), odkaz na okno rodičovské.

Jako rozhraní mezi oběma okny bude sloužit funkce, nazveme ji například onenumsubmit, která bude volána na rodičovském okně při ukončení práce s dceřiným oknem.

Funkce onenumsubmit bude volána s každým použitím dceřiného okna, ale budeme muset zajistit ošetření stavu kdy tato funkce nebude existovat. Pro tento účel se nabízí využití výjimek a chráněného bloku pomocí try, catch nebo testu na existenci funkce s využitím automatické konverze datových typů v JavaScriptu

Na ukázku si na onenumsubmit navěsíme klientskou funkci, kterou si implementují klientští programátoři. Pro zpestření si ukážeme, jak universálně předat další argumenty pro naši klientskou funkci a jejich vyzvednutí za pomocí pole arguments.

Komentované řešení

Začneme přípravou klientské funkce, ve které si můžeme implementovat logiku podle vlastní potřeby.

  
   <script type="text/javascript">
	function myListener(){
	  //logika pracující s rodičovským oknem
	}
   </script>
 

Připravenou funkci myListener pověsíme na onenumsubmit. Funkci onenumsubmit musíme zároveň přidat objektu window, v dceřiném okně nám pak opener ukazuje na window s funkcí onenumsubmit.

 
   <script type="text/javascript">
	window.onenumsubmit = myListener;
   </script>
 

Případně

 
   <body onload="window.onenumsubmit = myListener;">
 

Pro jednoduchost připravíme klientským programátorům co největší komfort. Protože používáme v rámci našich řešení XSL, připravil jsem jednoduchou šablonu, kterou mohou klientští programátoři volat.

 
  <xsl:template name="addEnumListener">
	<xsl:param name="listenerName" />
         <script type="text/javascript">
		window.onenumsubmit = myListener;
   	 </script>
  </xsl:template>
  .
  .
  .volání
  .
  <xsl:call-template name="addEnumListener">
    <xsl:with-param name="listenerName">myListener</xsl:with-param> 
  </xsl:call-template>	
 

Klientskou část tedy kód pro rodičovské okno máme připravený, nyní si ukážeme jak volat onenumsubmit z dceřiného okna. Díky volání onenumsubmit se zavolá i naše funkce myListener. Volání můžeme spojit s uzavřením dceřiného okna(window.close()) nebo události onunload.

 
   <script type="text/javascript">
	opener.onenumsubmit()
   </script>
 

Bohužel takto jednoduše neošetříme přiklad kdy funkce onenumsubmit nebude existovat. Můžeme použít zmíněný try, catch nebo test funkce.

 
   <script type="text/javascript">
	try{
	  opener.onenumsubmit()
	}catch(e){
	  //ignoruj výjimku
	}
   </script>

   <script type="text/javascript">
	if(opener.onenumsubmit){
          //funkce existuje
	  opener.onenumsubmit();
	}	
   </script>   
 

Rád bych upozornil, že ignorovat výjimky prázdným catch blokem není dobrá praktika. Výjimka, kterou jsme se rozhodli ignorovat prázdným catch blokem, totiž nemusí být nutně vyvolána neexistencí funkce onenumsubmit, ale i chybou v těle klientské funkce pověšené na onenumsubmit, v našem ilustrativním příkladem v myListener.

Pokud by tento případ nastal, měli bychom horké chvilky při dohledávání chyby neboť ta by byla vždy "spolknuta" bez jakéhokoliv chybového hlášení.

Nakonec jsem si nechal malé zpestření v předávání argumentů do klientské funkce. V JavaScriptu neni možné přetěžovat funkce počtem nebo typem argumentů, na druhou stranu všechny argumenty jsou v kontextu volané funkce k dispozici v poli arguments.

Řekněme, že bychom si rádi předali do klientské funkce dvě hodnoty z dceřiného okna.

 
   <script type="text/javascript">
	if(opener.onenumsubmit){
	  opener.onenumsubmit("fooValue","hooValue");
	}	
   </script>
 
  
   <script type="text/javascript">
	function myListener(){
	  fooValue = arguments[0];
          hooValue = arguments[1];
	}
   </script>
 

Samozřejmě by jste si rovnou mohli funkci myListener navrhnout s argumenty v deklaraci(myListener(fooValue, hooValue)), ale pole arguments by se mohlo hodit v případech kdy neznáte počet argumentů. Pro vyzkoušení přidávám odkaz na všeshrnující příklad.

Související články