わたしの日記だよ

ー生きる厳しさと哀しさを鮮烈に謳うー

フォームデータ送信後、DB更新に成功したら印刷プレビューを開く

画面から、任意の項目を選択→確認画面へ移行→OKボタン押下でサーバへ送信した後

DB更新に成功したら、元のページで印刷プレビューを開いてほしいな、と言われました。

ちなみに、struts2のプロジェクトです。

 

当初、ajax使って〜とのことだったので、jQuery.ajax()でasync:falseを指定して

実装してみたのだけど、jQuery1.8以降非推奨らしいし、エラーも出てしまう。

(Choromeで確認)

Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check http://xhr.spec.whatwg.org/.

これはちょっと嫌な感じなので、今回はformを使うことを考えてみました。

 

1.フォーム送信〜DB更新するところまで

 まず、jsp側(抜粋)。

<div class="print-off">
  <s:form>
    <input id="printStatus" type="hidden" value='<s:property value="printStatus" />'/>
    <s:iterator status="st" value="idList">
      <input type="hidden" 
             name='idList[<s:property value="#st.index" />]' 
             value='<s:property value="idList[#st.index]" />' />
    </s:iterator>
    <s:submit method="updateStatus" value="一括印刷"/>
  </s:form>
</div>

<div class="print-on">
・・・
</div>

「print-on」クラスを持つ要素は、通常はCSSで非表示にしておいて

印刷プレビューを表示するときだけ表示。

「print-off」クラスを持つ要素は、その逆です。

(印刷のときに、ボタンとか余計なものを入れないための処置)

 

また、hiddenの<input>タグは、以下の用途に使います。

 ・printStatus・・・DB更新結果を保持する

 ・idList[<s:property value="#st.index" />]・・・印刷したいIDの配列要素

 

 

Action側(抜粋)。jsp側の<s:submit>タグで指定した「updateStatus」メソッドを作り

その中でDB更新するだけです。抜粋っていうか、日本語しか書いていない。。。

public class TestAction extends ActionSupport {

  // 印刷したいID
  public List<string> idList = new ArrayList<string>();
  // DB更新結果フラグ
  public String printStatus = "";

  ・・・

  public String updateStatus() throws Exception {

    // idListを使って、DBテーブルの「印刷したよ」フラグを更新する処理・・・☆

    // ☆に成功したらprintStatusに"SUCCESS"、失敗したら"FAIL"をセットする

    ・・・
  }

  ・・・
}

 

2.printStatusの更新をチェックする

フォーム送信後、レスポンスを受け取ったときに、window.loadのイベントが呼ばれるので

そこでprintStatusの値をチェックすることにします。

 

jsファイル側。

$(window).load(function(){

  // 印刷フラグのチェック
  var status = $("#printStatus");
  if (!status && !status.val()) {

    var val = status.val();

    if (val === "SUCCESS") {

      // 印刷フラグ更新成功ー>印刷プレビュー表示
      showPrintPreview();

    } else if (val === "FAIL") {

      // 印刷フラグ更新失敗
      alert("DBの更新に失敗しました。");

    }
  }

})

function showPrintPreview() {
  
  // 印刷しない要素を非表示にする
  var printOff = document.getElementsByClassName("print-off");
  for(var i = 0; i < printOff.length; i++) {
    printOff[i].style.display = "none";
  }

  // 印刷する要素を表示する
  var printOn = document.getElementsByClassName("print-on");
  for(var i = 0; i < printOn.length; i++) {
    printOn[i].style.display = "block";
  }

  // 印刷プレビュー表示
  if (isIE()) {

    // IEの場合
    var sWebBrowserCode = '<object width="0" height="0" classid="CLSID:8856F961-340A-11D0-A96B-00C04FD705A2"></object>';
    document.body.insertAdjacentHTML( 'beforeEnd', sWebBrowserCode );

    var objWebBrowser = document.body.lastChild;
    if(objWebBrowser == null) {
      return;
    }

    objWebBrowser.ExecWB( 7, 1 );
    document.body.removeChild( objWebBrowser );

  } else {

    // IE以外
    window.print();

  }

  // 印刷しない要素を再表示する
  for(var i = 0; i < printOff.length; i++) {
    printOff[i].style.display = "block";
  }

  // 印刷する要素を非表示に戻す
  for(var i = 0; i < printOn.length; i++) {
    printOn[i].style.display = "none";
  }

}

function isIE(){

	if(!window.ActiveXObject){
		if(!document.documentMode){
			return false;
		}
	}
	return true;
	
}

ちょっと長いですが、

印刷したい部分のみを表示する→印刷プレビューを開く→表示を元に戻す

としているだけです。

IEのみ、印刷プレビューを表示するのにActiveXObjectを使わないとうまくいかないので

そこだけポイントです。

 

こちらを参考にしました。

ブラウザーに印刷プレビューボタン作成

 

3.印刷プレビューが表示される前に、表示を戻す処理が走ってしまう

上記のjsは、試しに<s:submit>タグのonclickイベントから呼んでいたときには

期待通りに動作していましたが、いざwindow.loadから呼んでみると

印刷プレビューを表示する前に、表示を戻す処理が走ってしまうようになってしまいました。

 

onclickから呼ぶのと何が違うのか、まだわかってないのですが

JavaScriptは非同期処理なのですね。

[JavaScript] JavaScriptはシングルスレッド:非同期処理の仕組み | Ouka Studio

 

だったら、表示を戻す処理は、印刷プレビューが閉じるタイミングで実行すればいいかな。

印刷プレビューが閉じて、元画面の描画が完了すると

window.onafterprintというイベントが呼ばれるので、こちらを使います。

jsファイル側(表示を戻す処理のみ抜粋)。

var afterPrint = function() {
  // 印刷しない要素を表示する
  var printOff = document.getElementsByClassName("print-off");
  for(var i = 0; i < printOff.length; i++) {
    printOff[i].style.display = "block";
  }

  // 印刷する要素を非表示に戻す
  var printOn = document.getElementsByClassName("print-on");
  for(var i = 0; i < printOn.length; i++) {
    printOn[i].style.display = "none";
  }
}

// Chorome・safari
if (window.matchMedia) {
	var mediaList = window.matchMedia('print');
	mediaList.addListener(function(mql) {
		if (!mql.matches) {
			afterPrint();
		}
	})
}

// IE・Firefox
window.onafterprint = afterAllPrint;

onafterprintイベントをサポートしていないブラウザもあるので

window.matchMediaを判定してごにょごにょする処理も加えておきます。

Operaはどっちもサポートしていないようです。。。困った!

(今回は、対象ブラウザじゃないのでパス)

 

JavaScript、いつもなんとなくで書いていたので

早急に入門しなおさなくては・・・