ListView に配置した Formフィールド 受信

WicketListView でリストの中に TextField やフォーム送信するものを配置することが時々ある。

送信後、どうやって受け取るか以前はよく悩んでたもので、getRequestCycle().getRequest().getPostParameters() から取得する力技をよく
書いてたものだ。
そんなことすると、パラメータ名を辿りながらの取得でコーディングしていて悲しくなる。

Java8 から以下の方法で取得すればリストに書いた流れで取得できるではないですか。

例えば、ListView があって

final ListView<Foo> listview = new ListView<Foo>("listview", new PropertyModel<List<Foo>>(this, "list")){
   @Override
   protected void populateItem(ListItem<Foo> item){
       // フォームフィールドの配置
       item.add(new TextField<String>("name", new Model<>()));
       item.add(new HiddenField<String>("id", new Model<>()));
       // ・・・等々。。。
   }
};
queue(listview);

これを、AjaxButton の submit や、Form の onSubmit() で受信する時に、
ListView から取得するコンポーネントイテレータ処理をラムダで書けば、、
つまり、、ListView → iterator()java.util.IteratorforEachRemaining(Consumer)

順序どおりの受信を簡単に取得できるではないですか。

listview.iterator().forEachRemaining(c->{
    String name = ((TextField<String>)c.get("name")).getModelObject();
    String id = ((HiddenField<String>)c.get("id")).getModelObject();
    // 後は好きなように処理をする。。。
});

table で複数行の sortable

テーブルの Sortable 去年書いたけど、書き直す。ドラッグした後に、きちんとハンドルを外す。

sortable の stop オプションで、ui.item.data('multidrag') で取得した移動対象を移動後に
ui-selected を外してやります。

HTML

<table id="sortable">
  <thead>
     <tr><th>header-1</th><th>header-2</th></tr>
  </thead>
  <tbody>
     <tr><td>Item-1</td><td>aaa</td></tr>
     <tr><td>Item-2</td><td>bbb</td></tr>
     <tr><td>Item-3</td><td>ccc</td></tr>
     <tr><td>Item-4</td><td></td></tr>
     <tr><td>Item-5</td><td>eee</td></tr>
     <tr><td>Item-6</td><td></td></tr>
     <tr><td>Item-7</td><td>fff</td></tr>
     <tr><td>Item-8</td><td></td></tr>
     <tr><td>Item-9</td><td></td></tr>
     <tr><td>Item-10</td><td></td></tr>
    </tbody>
</table>

CSS

@CHARSET "UTF-8";

table{
   margin: 40px 50px;
   box-sizing: border-box;
   border-collapse; collapse;
   border-spacing: 0;
   white-space: nowrap;
}
th{ background-color: #c0c0c0; }
th,td{ border: 1px solid #444444; height: 40px; width: 100px; padding: 0 8px; }
.ui-selecting{ background-color: #feca40; }
.ui-selected{ background-color: #f39814; color: #ffffff; }
.ui-state-highlight{ background-color: #ffe45c; }

f:id:posturan:20160914003320j:plain


jQueury

$('#sortable tbody').selectable({
   cancel: '.sort-handle, .ui-selected',
}).sortable({
   placeholder: "ui-state-highlight",
   axis: 'y',
   opacity: 0.9,
   items: "> tr",
   handle: 'td, .sort-handle, .ui-selected',
   helper: function(e, item){
      if (!item.hasClass('ui-selected')){
         item.parent().children('.ui-selected').removeClass('ui-selected');
         item.addClass('ui-selected');
      }
      var selected = item.parent().children('.ui-selected').clone();
      ph = item.outerHeight() * selected.length;
      item.data('multidrag', selected).siblings('.ui-selected').remove();
      return $('<tr/>').append(selected);
   },
   cursor: "move",
   start: function(e, ui){
      ui.placeholder.css({'height':ph});
   },
   stop: function(e, ui){
      var selected = ui.item.data('multidrag');
      ui.item.after(selected);
      ui.item.remove();
      selected.removeClass('ui-selected');
      $(selected).children("td").removeClass('ui-selected');
   },
   update: function(e, ui){
      console.log("updated !!");
   }
});

Wicket scheduleRequestHandlerAfterCurrent 使用の書き方を見直す

Wicket の WebPage でファイルダウンロードさせる1つの方法である RequestCycle scheduleRequestHandlerAfterCurrent を使用する時、

そのまま以下のようなことを書いていた。。

getRequestCycle().scheduleRequestHandlerAfterCurrent(new ResourceStreamRequestHandler(new AbstractResourceStreamWriter(){
   @Override
   public void write(OutputStream out){

      // OutputStream に出力

      out.flush();
      out.close();
   }
   @Override
   public String getContentType(){
      // 返す ContentType を指定
      return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
   }
}, "filename"));

でも、これは AbstractResourceStreamWriter の close() が約束されてないことになり Eclipse では
潜在的なリソース・リーク: '' may not be closed
になる

AbstractResourceStreamWriter のインターフェースである IResourceStreamWriter は、IResourceStream の継承で
Closeable であるので、try(){ ~ } 構文にすべきだ。

try(IResourceStreamWriter writer = new AbstractResourceStreamWriter(){
   @Override
   public void write(OutputStream out) throws IOException{

      // OutputStream に出力する。

      out.flush();
      out.close();
   }
   @Override
   public String getContentType() {
      // 返す ContentType を指定
      return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
   }
}){
   getRequestCycle().scheduleRequestHandlerAfterCurrent(
      new ResourceStreamRequestHandler(writer, URLEncoder.encode("サンプル.xlsx", "UTF-8")
   ));
}catch(Exception e){
   // エラー捕捉
}

Java で横長カレンダーを書くための処理

先日、jQuery で横長の帯状のカレンダーを書くための処理を書いて
横長カレンダーを書く - Oboe吹きプログラマの黙示録
、では Java だったらどう書く?
と思ったので書いてみました。

例えば、

LocalDate start = LocalDate.parse("2016-12-22");
LocalDate end   = LocalDate.parse("2017-01-05");

という開始日付と終了日付が与えられてる場合です。

まず、日(day) を並べてみます。無限 Stream 生成して開始と終了の差で制限を書けて Stream を完結させます。

Stream.iterate(start, d->d.plusDays(1)).limit(Period.between(start, end).getDays() + 1).forEach(e->{
  System.out.print(e.getDayOfMonth() + " ");
});

以下のようになります。

22 23 24 25 26 27 28 29 30 31 1 2 3 4 5 

問題は月で、この場合の月として割り当てた時の日数の長さを求めたいのです。
無理やり、Stream の 実行を繋げて処理を書くと。。。

Stream.iterate(start, d->d.plusDays(1)).limit(Period.between(start, end).getDays() + 1)
.map(e->LocalDate.of(e.getYear(), e.getMonthValue(), 1)).collect(Collectors.groupingBy(e->e, Collectors.counting()))
.entrySet().stream().sorted((e1, e2)->e1.getKey().compareTo(e2.getKey())).forEach(e->{
   System.out.println( e.getKey().getMonthValue() + "月 : " + e.getValue() + " days");
});

長いけど、Cool に見えてきます。

12月 : 10 days
1月 : 5 days

となり、12月→1月の順番を保てます。

横長カレンダーを書く

横長カレンダーを書く jQuery ソース・・・メモ。
moment.js http://momentjs.com/ を使うことにした。jQuery は、2.x系で書いた。

moment-with-locales.js を使う。

/* datetext  = yyyy/MM/dd 日付
 * predays   = 前方日数(datetextの日付を含まないカウント)
 * afterdays = 後方日数(datetextの日付を含まないカウント)
 */
var writeCalandarHead = function(datetext, predays, afterdays){
   var basedate = moment(datetext, "YYYY/MM/DD");
   var dt = moment(datetext, "YYYY/MM/DD");
   dt.add(predays * -1 - 1, "days");
   var mary = {};
   $('thead tr:nth-child(1) th:nth-child(n+2)').remove();
   $('thead tr:nth-child(2) th').remove();
   var len = predays + afterdays + 1;
   for(var i=0; i < len; i++){
      dt = dt.add(1, "days");
      var key = (dt.month()+1) + "月";
      mary[key] = mary[key]==undefined ? 1 : mary[key] = mary[key] + 1;
      var plusclass = "";
      if (basedate.diff(dt, "days")==0){
         plusclass = ' class="today"';
      }else{
         var week = dt.format("dddd");
         if (week=="Saturday") plusclass = ' class="Saturday"';
         if(week=="Sunday") plusclass = ' class="Sunday"';
      }
      $('thead tr:nth-child(2)').append("<th" + plusclass + ">" + dt.date() + "</th>" );
   }
   Object.keys(mary).forEach(function(key){
      $('thead tr:nth-child(1)').append('<th colspan="' + mary[key] + '">' + key + '</th>');
   });
};
$(function(){
   $("input[class='datepicker default']").datepicker({
      prevText:"前月", nextText:"翌月",
      changeMonth: true,
      changeYear: true, yearRange: '-3:+4',
      onSelect: function(dateText){
         writeCalandarHead(dateText, 5, 20);
      },
   }).datepicker("setDate", moment().format('YYYY/MM/DD'));
   writeCalandarHead(moment().format('YYYY/MM/DD'), 5, 20);
});

HTMLは、、、jquery-2.x と jquery-ui を指定して、、

<input type="text" class="datepicker default" style="width: 70px">
<div>
   <table>
      <thead>
         <tr><th rowspan="2" style="width: 100px">header</th></tr>
         <tr></tr>
      </thead>
   </table>
</div>

CSS は、、

.ui-datepicker{ font-size: 80%; }
.ui-datepicker select.ui-datepicker-month, .ui-datepicker select.ui-datepicker-year{ width: auto; }
table{
   box-sizing: border-box;
   border-spacing: 0;
   border-collapse: collapse;
   white-space: nowrap;
   font-size: 14px;
}
th{ border: 1px solid #000000; }
tr:nth-child(2) th{ width: 30px; }

th.today    { background-color: #ffff60; }
th.Saturday { background-color: #b3f2ff; }
th.Sunday   { background-color: #ffb9b3; }

表示すると。

f:id:posturan:20160906110837j:plain

カスタムデータ属性で CSSを効かせる

HTML5 のカスタムデータ属性で CSSを効かせる方法、

例) td タグに、data-date="2016/09/04" にある場合、

td[data-date="2016/09/04"]{
   background-color: #ffc0cb !important;
}

のように書く。

data 属性の値はプログラムで動的に付与していくケースがほとんどと思われるので、

^= の記述で、

      td[data-date^="2016/09/"]

と書けば、2016年 9月の td タグにスタイルを充てることができる。

あるいは、jQuery を走らせて CSS をセットすれば、動的な表示に対するスタイルも充てられるだろう。