概要

Ajaxを活用して、画面をポストポストバックせずに、郵便番号から住所反映しようと思うと、主に以下の2点が問題となります。
  • XMLノードはどうやって取得する?
  • ActiveButtonによって、例えば『住所反映』なんてボタンを作れば、PRADOのActiveButton と ActiveLabel などで 実装できますが、それよりもやっぱり テキストボックスを更新したら、反映したいです。これに対応するコントロールが 見つけられない。(TActiveTextBoxでは更新時にアクションが起きなかった・・・。そもそもこれは何のActiveControl なんだ?)

郵便番号から住所を取得するXMLノード

郵便番号から住所を取得するAPIは実は既に公開されています。(http://zip.cgis.biz/)ところが、Ajaxを使って このページからノードを取得することをブラウザが拒否します。(おそらく同じドメイン以外からのアクセスを禁止 しているものと思われます。)そこで、同じホストにおいて、郵便番号から住所を表示するXMLノードが必要となります。 もしかしたら、郵便番号のデータベースを保有するケースも結構あるかも知れないけど、せっかく郵便局がAPI公開しているので、 重たいテーブルを保持・管理する様なことはせず、APIのデータをサーバーサイドで呼び出して、同じホスト内に表示してしまえば 良いと考えました。つまり、クライアントサイドでは不可能なことをサーバーサイドにおいて実現してしまおうというものです。

PRADOにおけるXMLノード表示

Application.xmlの設定

PRADOでは、各々のページに対して適用されるレイアウトがあります。ヘッダーやフッターといったものですが、 XMLの場合にはこれがあるとXMLノードとしてはエラーとなります。そのため、通常のページとはレイアウトを分ける 必要がります。
まずは、Application.xml で大元の設定を変更します。
 <services>
   <service id="page" class="TPageService" DefaultPage="Home">
	  <pages MasterClass="Application.layouts.Base" Theme="Red_Gold" />
	</service>
	<service id="parser" class="TPageService" />
 </services>
通常は、<service></service>タグで、共通レイアウトが page にだけ設定されているところを、もう一つ(今回は parser )加えます。 この parser に対する基本レイアウトは設定しません。ちなみにこれらの設定はURL上で
 index.php?page=・・・・・
 index.php?parser=・・・・
といった様にリンクすることになります。

ファイルレイアウト

一連のファイルレイアウトは以下の感じです。(関係のないディレクトリもありますが・・・) XMLノードを提供するディレクトリは 一まとめにしておきます。
#ref(): ERROR: File not found: "Screenshot-11.png" at page "PRADO/JavaScript使用方法/郵便番号から住所反映"

config.xml

以下の様にします。
<?xml version="1.0" encoding="utf-8"?>
 <configuration>
  <pages MasterClass="Application.pages.loadXml.layout" />
 </configuration>
ここでこのフォルダーに適用されるレイアウトファイルを設定します。通常、上部ディレクトリのconfig.xmlなどが適用されてしまうのですが、 先のApplication.xmlでの設定があるため、他のレイアウトファイルは適用されません。

layout.tpl layout.php

適用されるレイアウトファイルの内容です。layout.tpl は 冒頭から最後まで『改行』がないことが非常に重要です。
<com:TContentPlaceHolder ID="content"/>
次にlayout.phpです。
<?php
 class layout extends TTemplateControl
 {
 }
?>
なーんもありません。

postalCode.page postalCode.php

xmlノードのパーサーです。まずは、postalCode.page です。便宜上改行していますが、このファイルも『改行』してはならないファイルです。
<com:TContent ID="content"><%% header('Content-Type: application/xml; charset=utf-8'); %><%=
$this->Page->query %></com:TContent>
次に postalCode.php です。
<?php
 class postalCode extends TPage {
	public function onload(){
		$pc=$this->getPage()->Request['pc'];
		$postal_xml=simplexml_load_file('http://zip.cgis.biz/xml/zip.php?zn='.$pc);
		$this->setquery($postal_xml->asXML());
	}
	public function setquery($value){
		$this->query=$value;
	}
}
?>

get変数で、変数pc (郵便番号6桁)を受け取り、それを 郵便番号のAPIページ (http://zip.cgis.biz/xml/zip.php?)にそのまま 渡します。帰ってきたXMLノードをそのまま返します。simplexml は php5.0以降に実装されています。

XMLノードを表示

これで
(ホスト)/index.php?parser=loadXml.postalCode&pc=6048571
といった感じでXMLノード表示ができます。

PRADOにAjax実装

ハンドラへの関数を後付け

PRADOは、コントロールを配置して、そのイベントをクラスファイルに書き込む訳で、Activeコントロールを使うと、Ajaxを利用した ポストバックが起きない制御ができる訳です。でも前述のとおり、テキストボックスのonchangeハンドラに対応したAjaxコントロール が見つけられず・・・。そうなれば、JavaScriptのハンドラを後から付け足す方法で実装していきます。付け足す方法というのは
<com:TClientScript PradoScripts="prado,effects,ajax" />
・
・
・
・
・
<com:TTextBox id='text_01' />
<script language='JavaScript'>
  function myAlert(){
    alert('ok');
  }
  $('ctl0_content_ctl0_text_01').onchange=myAlert;
</script>
一番上のコントロールは、prototype.js を読み込むコントロールです。prototype.js はJavaScriptの有名なライブラリだそうです。prototype.jp辺りをご参照ください。これによって たとえば、
$('ctl0_content_ctl0_text_01')
という関数が使えるようになるのです。$ は 
getElementById()
の代わりになるもので、その他にメソッドなどが追加されています。話がそれましたが、JavaScriptのハンドラへの後付けは、
$('ctl0_content_ctl0_text_01').onchange=myAlert;
の一行で行っています。引数を渡すのには、もうちょっと複雑なことをしなくてはなりませんが、これでもかなりの処理が 後付けで付け加えられることが分かると思います。

郵便番号から住所取得Ajax実装

ようやく前準備が完了しました。Ajax実装コマンドなので、以下の様に、どこかの.page や .tpl へ付け加えられます。
<li>郵便番号<br />
  <com:TTextBox id='postal_code' />
</li>
<li>ご住所<br />
  <com:TLabel id='address_state_city' /><br>
  <com:TTextBox id='address_01' Columns="40" />
       <script language='JavaScript'><!--
           //全角郵便番号を半角に直す関数
	    function replace_han(str){
               str=str.replace(/([0-9a-zA-Z])/g
                    ,function (match_str){return String.fromCharCode(match_str.charCodeAt(0) - 65248)});
		str=str.replace('ー','-');
		return str;
	     }

           //郵便番号から住所反映のスクリプト
           function adapt_address(){
		$('ctl0_content_ctl0_address_01').value="住所検索・・・・";
		//住所反映テキストボックスを使用不可にする。
		$('ctl0_content_ctl0_address_01').disabled=true;
		var pc=$('ctl0_content_ctl0_postal_code').value;
		//郵便番号を半角変換
		pc=replace_han(pc);
		//郵便番号が 999-9999 形式の場合には、7桁数値に変換
		var patern_pc=/(\d{3})+-+(\d{4})/;
		var r=pc.match(patern_pc);
		if(r){
			pc=pc.split("-")[0]+pc.split("-")[1];
		}
		//ポスタルコードを置き換える
		$('ctl0_content_ctl0_postal_code').value=pc;
		//prototypeのAjax関数を利用して郵便番号ノードページを読み込み
		var url = '';
		var pars = 'parser=loadXml.postalCode&pc=' + pc;
		var myAjax = new Ajax.Request(
			url,
			{
				method: 'get',
				parameters: pars,
				onComplete:showResponse
			});
	    }
           //郵便番号ページ読み込んだ際の処理
	    function showResponse(originalRequest){
		//XMLノード中 result エレメントを取得
		var result=originalRequest.responseXML.getElementsByTagName('result');
		//住所データが返されているかフラグを確認する
		var result_code=0;
		var i=0;
		while(result[i]){
			if(result[i].getAttributeNode('result_code')){
				var result_code=result[5].getAttributeNode('result_code');
			}
			i++;
		}
		//alert(result_code.value);

		//住所データが返されている場合には住所反映
		if(result_code.value==1){
			//value エレメントを取得
			var v=originalRequest.responseXML.getElementsByTagName('value');
			var state="";
			var city="";
			var address="";
			var company="";
			var i=0;
			while(v[i]){
				if(v[i].getAttributeNode('state')){
					state=v[i].getAttributeNode('state').value;
				}
				if(v[i].getAttributeNode('city')){
					city=v[i].getAttributeNode('city').value;
				}
				if(v[i].getAttributeNode('address')){
					address=v[i].getAttributeNode('address').value;
				}
				if(v[i].getAttributeNode('company')){
					if(v[i].getAttributeNode('company').value != 'none'){
						company=v[i].getAttributeNode('company').value;
					}
				}
				i++;
			}
			$('ctl0_content_ctl0_address_state_city').innerHTML=state+city;
			$('ctl0_content_ctl0_address_01').disabled=false;
			$('ctl0_content_ctl0_address_01').value = address+company;
		}else{
			//住所データが返されていない場合には”該当なしを表示”
			$('ctl0_content_ctl0_address_state_city').innerHTML="<font color='red'>該当する住所がありません</font>";
			$('ctl0_content_ctl0_address_01').disabled=false;
			$('ctl0_content_ctl0_address_01').value="";
		}
		//住所欄にフォーカスする
		$('ctl0_content_ctl0_postal_code').focus();	//なぜか一度他のテキストボックスをフォーカスする必要あり
		$('ctl0_content_ctl0_address_01').focus();
	}
	$('ctl0_content_ctl0_postal_code').onchange=adapt_address;
--></script>
</li>

全角で入力したり、○○○ー○○○○ というような形式を読み替えるなど、あまり関係のない制御を付け加えているのでわかりにくかもしれませんが、基本構造はhhttp://www.s2factory.co.jp/tech/prototype/prototype.js.htmlとまったく同じです。