Wicket 1.5.2 で、CSS Javascritp の Link 出力のコントール方法について調べてみました。
調べた結果を記載します。

[TOC]


前提

動作確認している環境の情報について記載します。

  • Wicket の Version

        <!-- https://mvnrepository.com/artifact/org.apache.wicket/wicket-core -->
        <dependency>
            <groupId>org.apache.wicket</groupId>
            <artifactId>wicket-core</artifactId>
            <version>1.5.2</version>
        </dependency>
    

  • OS

    % sw_vers
    ProductName:    Mac OS X
    ProductVersion: 10.12.6
    BuildVersion:   16G1036
    

  • ** Java の Version**

        <plugin>
            <inherited>true</inherited>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <debug>true</debug>
            </configuration>
        </plugin>
    


参考


Wicket 7.6.0 での CSS Javascript の Reference 出力のコントロール方法について

過去に以下の記事を作成しましたので、Wicket 7.6.0 での方法を知りたい方はご確認ください。
Wicket scriptタグ を body 閉じタグの直前に出力する | Monotalk

Wicket 7.6.0 ですと FilteredHeaderItem HeaderResponseDecorator使ってコントロールします。


Wicket 1.5.2 での CSS Javascript の Reference 出力のコントロール方法について

Wicket 7.6.0 と同等機能を Wicket 1.5.2 で利用するには以下の実装が必要になります。1

  1. HTML と、Page クラスに Footer 部とする Container を追加する

  2. HeaderResponseDecoratorHeader の設定

  3. HeaderResponseContainerFilteringHeaderResponse の拡張クラスの作成

以下、各実装について説明します。
作成したもの一式は gist に UP しました。
Wicket 1.5.2 で、CSS Javascript の Reference 出力をコントロールする

Footer 出力箇所とする、Page クラス、HTML にそれぞれ以下記述を追加します。
footerBucket ID 名称なので任意の名前で問題ありません。
HeaderResponseFilteredResponseContainer第二引数の footerBucket Footer 部に対応する Filter 名を適用します。

  • HTML

    <wicket:container wicket:id="footerBucket"/>
    

  • Pageクラス

    add(new HeaderResponseFilteredResponseContainer("footerBucket", "footerBucket"));
    

2. HeaderResponseDecoratorHeader の設定

WebApplication クラスで、setHeaderResponseDecorator で、HeaderResponseDecorator実行します。
3.説明する FilteringHeaderResponse設定します。
filter クラスには、AbstractHeaderResponseFilter を使用し、Header 用、Footer 用で、匿名インナークラスを2つ生成しています。

  • WebApplication クラス

            setHeaderResponseDecorator(new IHeaderResponseDecorator() {
                HeaderResponseContainerFilteringHeaderResponse.IHeaderResponseFilter[] filters = {
                    new AbstractHeaderResponseFilter("headerBucket") {}, 
                    new AbstractHeaderResponseFilter("footerBucket") {}}
                ;
    
                @Override
                public IHeaderResponse decorate(IHeaderResponse response) {
                    return new FilteringHeaderResponse(response, "headerBucket", filters);
                }
            });
    

  • CSSAcceptingHeaderResponseFilter 、JavaScriptAcceptingHeaderResponseFilter を利用しない理由
    以下の理由から、CSSAcceptingHeaderResponseFilter 、JavaScriptAcceptingHeaderResponseFilter は利用しませんでした。

    • 文字列ベースでのReference参照だと、大きなレベルでのコントロールしかできない。
      acceptReferenceacceptOtherJavaScriptacceptOtherCssいうメソッドがありますが、文字列ベースのresponse.renderJavaScriptReference() 実行の際は、acceptOtherJavaScriptresponse.renderCSSReference()実行の際は、acceptOtherCSS実装され、追加する/しない の判断が行われます。
      acceptOtherJavaScriptacceptOtherCss は、true/false のみを返すため、css ヘッダ部へ、<wbr>javascript は<wbr>フッタ部へする大雑把なコントロールしかできず、リソース単位でのコントロールができないためです。

3. HeaderResponseContainerFilteringHeaderResponse の拡張クラスの作成

リソース単位でのヘッダ出力、フッタ出力をできるようにするため、HeaderResponseContainerFilteringHeaderResponse の拡張クラス を作成しました。

  • renderJavaScriptReferenceIntoFooter、renderCSSReferenceIntoFooter メソッドの追加
    HeaderResponseContainerFilteringHeaderResponse JAVA DOC に以下の記載があります。 出力を完全にコントロールしたい場合は、runWithFilter メソッドを使えばよいとのことなので、runWithFilter メソッドを使用して、Footer部を指定してタグを追加するメソッドを追加しました。

If subclasses of this class have special cases where they force something into a particular bucket, regardless of the filters, they can create a Runnable that renders to the real response, and pass it to this method with the name of the filter (bucket) that they want it to appear in. Example:

                   public void renderJavascriptIntoHead(final String js, final String id) {
                           runWithFilter(new Runnable() {
                                   public void run()
                                   {
                                           getRealResponse().renderJavascript(js, id);
                                   }
                           }, "headerBucket");
                   }

  • aync属性の出力メソッドの追加
    Wicket 1.5.2 だと、defer 属性のみ出力できるのですが、Wicket 8.0 の実装を見ると、Javascript の async 属性も追加できるようになっています。
    async 属性で非同期実行の順序をコントロールしたいケースがありそうですので、以下クラスの実装を参考に、async 属性をコントロールできるメソッドを追加しました。

wicket/JavaScriptUtils.java at master · apache/wicket

使い方

Page クラスの renderHead で、IHeaderResponse FilteringHeaderResponse キャストして使用します。2

    @Override
    public void renderHead(IHeaderResponse response) {
        FilteringHeaderResponse filteringResponse = (FilteringHeaderResponse) response;
        filteringResponse.renderString("<script type=\"text/javascript\"><!-- document.write(\"Hello\"); // --></script>");
        filteringResponse.renderString("<link type=\"image/x-icon\" rel=\"shortcut icon\" href=\"resources/favicon.ico\" />");
        filteringResponse.renderCSSReference("https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css");
        filteringResponse.renderJavaScriptReference("https://code.jquery.com/jquery-1.12.4.min.js");
        filteringResponse.renderJavaScriptReference("https://code.jquery.com/jquery-1.12.4.min.js", true, false);
        filteringResponse.renderJavaScriptReferenceIntoFooter("https://code.jquery.com/jquery-2.2.4.min.js", true, true);
        super.renderHead(response);
    }

気にしなかったこと

TOP - async/defer属性とDOM構築 async / defer 属性についての説明があります。

async、defer 属性は論理型の属性(あれば true、なければ false)で、スクリプトがどのように実行されるべきかを表します。また src 属性がない場合は指定することができません。

論理属性なので、defer="defer" async="async"出る必要はないですが、HTML5 でない場合もあるかと思いましたので、この動作については変更は行いませんでした。

フレームワークは Version が上がると、痒いところに手が届くようになります。
エンタープライズだと、フレームワークの Version アップは難しかったりしますが、最新 Version の実装参考にしながら、オレオレ拡張は実施できる場合もありますね。
以上です。


  1. 手抜き気味なので、完全再現にはもっとゴリゴリ実装する必要があるかと思いますが、個人的に必要なユースケースはこれで満たすことができそうです。 

  2. キャストがダサいですね。親クラスで あらかじめでキャストしておくほうがいいかもしれません。 

コメント