disabled属性の項目値はサーバへ送信されないのでリロードすると値が消えるときの対応方法

- 環境
    - CentOS Linux release 7.8.2003 (Core)
    - openjdk version "11.0.7" 2020-04-14 LTS
    - JSF 2.3.9
    - jquery 3.2.1

# 事象 : 画面がsumit後にリロードされたら非活性のラジオボタンの選択値がクリアされた
画面初期表示時にラジオボタンは初期値を設定して、ラジオボタンが未選択にならないようにした。が、リロードされたら未選択になってしまった。
![a.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159761/44fe0c7c-c824-f6d5-6f5a-6d434c79cefa.gif)

## 原因 : ラジオボタンがdisabled属性だからsubmitされずサーバに値が送られない
disabled属性でサーバ送信されない > バッキングビーンに値が設定されない > リロードしても画面に値がバインドされない
という流れでリロードするとラジオボタンの値がクリアされた。

```text:submiの処理で選択地を出力するとnullになっていた
2020-09-05T19:29:21.468+0900|情報: ラジオボタンの値：null
2020-09-05T19:29:55.496+0900|情報: ラジオボタンの値：null
2020-09-05T19:29:59.560+0900|情報: ラジオボタンの値：null
```

> 無効になった入力欄は click イベントを受け取らず、フォームと共に送信されることもありません。
> [&lt;input&gt;: 入力欄 (フォーム入力) 要素 - HTML: HyperText Markup Language | MDN](https://developer.mozilla.org/ja/docs/Web/HTML/Element/Input#htmlattrdefdisabled)

```xml:base.xhtml
<!--省略-->
<h:form id="formId">
  <h5>disabledはsubmitできない</h5>
  <h:panelGroup style="display: flex;">
    <h:selectBooleanCheckbox id="check" onchange="changeDisabled()" />
    <h:outputLabel for="check" value="チェックボックス"/>
    (
    <h:selectOneRadio id="radio" value="#{sampleBean.selected}">
      <f:selectItems value="#{sampleBean.items}" />
    </h:selectOneRadio>
    )
  </h:panelGroup>
  <h:commandButton value="submit" action="#{sampleBean.submit}" />
</h:form>
<!--省略-->
```

```java:SampleBean.java
// 省略
/** SelectItemのリスト. */
@Getter
private List<SelectItem> items;
/** 選択した値. */
@Getter
@Setter
private Integer selected;

/** Beanの初期化処理. */
@PostConstruct
public void init() {
    // ラジオボタンの初期値を設定する.
    this.selected = 0;
    // ラジオボタンの選択肢を設定する.
    setItems();
}

/** [submit]ボタン押下処理. */
public void submit() {
    System.out.println("ラジオボタンの値：" + selected);
}

/** SelectItemのリストを設定する. */
private void setItems() {
    this.items = new ArrayList<SelectItem>();
    this.items.add(new SelectItem(AnimalCode.BEAR.getNo(), AnimalCode.BEAR.getName()));
    this.items.add(new SelectItem(AnimalCode.DOG.getNo(), AnimalCode.DOG.getName()));
    this.items.add(new SelectItem(AnimalCode.CAT.getNo(), AnimalCode.CAT.getName()));
}
// 省略
```

```javascript:base.js
$(function(){
  changeDisabled();
});

/** ラジオボタンの活性非活性を切り替える. */
function changeDisabled() {
    /** @type {boolean} チェックボックスの選択状態. */
    var isChecked = $("#formId\\:check").prop("checked");
    if (isChecked) {
        $('input[name="formId\\:radio"]').removeAttr('disabled');
    } else {
        $('input[name="formId\\:radio"]').prop('disabled', true);
    }
}
```

# 対応方法
## JavaScriptで選択値がなかったら初期値を設定する
画面を表示する時にラジオボタンに選択値がなかったら初期値を設定する。
既存のプログラムを修正する場合に便利。
サーバ側の処理には影響は与えずにすむ。が、選択した値を再現できるわけではない。

```javascript:base.js
$(function(){
  setRadioDefault();
  changeDisabled();
});
// 最初と同じなので省略
/** 未選択の場合に初期値を設定する. */
function setRadioDefault() {
    /** @type {string} ラジオボタンの選択値. */
    let radioVal = $('input[name="formId\\:radio"]:checked').val();
    if (!radioVal) {
        // 未選択の場合：初期値「0：くま」を設定する.
        $('input[name="formId\\:radio"]').val(["0"]);
    }
}
```

## CSSでreadonly属性をdisable風にしてサーバに値を送る
disabled属性をやめてreadonly属性にすればサーバに値を送信できる。
見た目が非活性っぽくなるようにCSSで工夫する。

参考 : [readonlyをdisabledっぽく見せるコードは８行で書ける！ ｜ 25歳で独立したフリーランスエンジニア -じゃけぇ- のあれこれ](https://freelance-jak.com/technology/javascript/553/)

今回の場合は、ラジオボタンで不向きなのでやらなかった。
> The readonly attribute is supported by  text, search, url, tel, email, password, date, month, week, time, datetime-local, and number<input> types and the <textarea> form control elements.(省略)
> The attribute is not supported or relevant to <select> or input types that are already not mutable, such as checkbox and radio or cannot, by definition, start with a value, such as the file  input type.
> [HTML attribute: readonly - HTML: HyperText Markup Language | MDN](https://developer.mozilla.org/ja/docs/Web/HTML/Attributes/readonly)

## JavaScriptでサーバ送信直前にdisabled属性を外す
ボタンが押されたタイミングでdisabled属性を外してサーバへ値を送信しちゃう。
ボタンを押した瞬間活性化するのが見えるのが微妙なところ。

```xml:base.xhtml
<!--省略-->
  <h:commandButton value="submit" action="#{sampleBean.submit}" onclick="onclickSubmit();" />
<!--省略-->
```

```javascript
// 最初と同じなので省略
/**
 * [submit]ボタン押下処理
 * @return {boolean} サーバ処理を実行するために常にtrue.
 */
function onclickSubmit() {
    $('input[name="formId\\:radio"]').removeAttr('disabled');
    return true;
}
```

## JavaScriptでサーバ送信直前にhiddenに値をコピーしてサーバに送る
ボタンのonclickイベントで用意しておいたhidden項目に選択値をコピーして隠し持っておいてサーバへ値を送信する。

```xml:base.xhtml
<!--省略-->
  <h:commandButton value="submit" action="#{sampleBean.submit}" onclick="onclickSubmit();" />
<!--省略-->
```

```javascript
// 最初と同じなので省略
/**
 * [submit]ボタン押下処理
 * @return {boolean} サーバ処理を実行するために常にtrue.
 */
function onclickSubmit() {
    let val = $('input[name="formId\\:radio"]:checked').val();
    $('#formId\\:hidden').val(val);
    return true;
}
```