Office UI Fabricのラジオボタンとドロップダウンリスト

今回はOffice UI Fabtic(以下"OfficeUI")のラジオボタンとドロップダウンリストについて解説します。どちらも複数の項目から一項目選択するためのUIなので、本記事でまとめて説明しようと思います。

## ラジオボタン

まずはラジオボタンです。表示例は以下になります。
(選択項目は[あいまいみー](http://mangalifewin.takeshobo.co.jp/rensai/imyme/)から引用しています)

![img100.png](https://qiita-image-store.s3.amazonaws.com/0/61137/ebbcb00a-69a2-3020-917f-f6d9b0292aac.png)

上記の表示例のサンプルコードは以下になります。

「ms-ChoiceFieldGroup」クラスでラジオボタンの選択項目をグルーピングして、「ms-ChoiceField」クラスの中に「ms-ChoiceField-input」を指定したラジオボタンそのものと、付随するラベルを記述する形になります。ms-ChoiceFieldクラスを指定したDIV要素がラジオボタンの一項目になるので、この要素を列挙することでラジオボタン(選択項目)が増やせます。

```html
<!DOCTYPE html>
<html ng-app="App">
  <head>
    <meta charset="utf-8" />
    <title>Radio Button Sample</title>
    <link rel="stylesheet" href="//appsforoffice.microsoft.com/fabric/1.0/fabric.min.css">
    <link rel="stylesheet" href="//appsforoffice.microsoft.com/fabric/1.0/fabric.components.min.css">
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
    <style>
      .my-sample { border: thin solid; border-radius: 4px; padding: 4px; margin-top: 10px; }
      .my-bold   { color: #e4007f; }
    </style>
  </head>
  <body ng-controller="AppController">
    <div class="ms-Grid"> <!-- ラジオボタンの選択内容で表示を変える -->
      <div class="ms-Grid-row">
        <div class="ms-Grid-col ms-u-sm6 ms-u-md8 ms-u-lg12">
          ぽのか先輩は&nbsp;<b class="my-bold">{{radioLabel}}</b>&nbsp;です。
        </div>
      </div>
    </div>
    <div class="ms-Grid"> <!-- ラジオボタンを表示する -->
      <div class="ms-Grid-row">
        <div class="ms-Grid-col ms-u-sm3 ms-u-md4 ms-u-lg6 my-sample">
          <div class="ms-ChoiceFieldGroup" ng-repeat="radio in radioButtons">
            <div class="ms-ChoiceField">
              <input id="radio{{radio.id}}" class="ms-ChoiceField-input" type="radio" name="ponokaName"
                     ng-model="ponokaName" ng-value="radio.value" ng-click="choiceRadioLabel(radio)">
              <label for="radio{{radio.id}}" class="ms-ChoiceField-field">
                <span class="ms-Label">{{radio.label}}</span>
              </label>
            </div>
          </div>
        </div>
      </div>
    </div>
    <script>
      var app = angular.module('App', []);
      app.controller('AppController', ['$scope',
        function($scope) {
          $scope.init = function() {
            $scope.radioButtons = [
              { id: 0, label: "自販機釣り銭パカパカ二郎総帥", value: 0 },
              { id: 1, label: "ポツダム宣言大好き太郎先輩",   value: 1 },
              { id: 2, label: "第三次声優ブームオタク",       value: 2 },
            ];
            $scope.choiceRadioLabel($scope.radioButtons[0]);
          }
          $scope.choiceRadioLabel = function(radio) {
            $scope.radioLabel = radio.label;
          }
          $scope.init();
        }
      ]);
    </script>
  </body>
</html>
```

ちなみに、単にms-ChoiceFieldを列挙する形でラジオボタンの選択項目を作成すると、先の表示例のように各項目が縦に配置されます。選択項目を横に並べて配置する場合は、グリッド配置を使用するなど、一手間必要になります。参考までに、以下は横に配置した場合の表示例です。
(グリッド配置については明日の記事で解説する予定です)

![img101.png](https://qiita-image-store.s3.amazonaws.com/0/61137/dbb52d9b-cc0c-8cfd-b222-881493962f25.png)

先のサンプルコードにおけるラジオボタン表示部分を以下の内容で置き換えると、選択項目が横配置になります。

```html
    <div class="ms-Grid"> <!-- ラジオボタンを表示する -->
      <div class="ms-Grid-row">
        <div class="ms-Grid-col ms-u-lg12 my-sample">
          <div class="ms-ChoiceFieldGroup" ng-repeat="radio in radioButtons">
            <div class="ms-Grid-col ms-u-sm4 ms-u-md4 ms-u-lg4">
              <div class="ms-ChoiceField">
                <input id="radio{{radio.id}}" class="ms-ChoiceField-input" type="radio" name="ponokaName"
                       ng-model="ponokaName" ng-value="radio.value" ng-click="choiceRadioLabel(radio)">
                <label for="radio{{radio.id}}" class="ms-ChoiceField-field">
                  <span class="ms-Label">{{radio.label}}</span>
                </label>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
```

## ドロップダウンリスト

続いてドロップダウンリストです。表示例は以下のようになります。

![img103.png](https://qiita-image-store.s3.amazonaws.com/0/61137/ada9d4ea-e07c-1706-e68c-2cf813ce9d8a.png)

上記のサンプルコードは以下になります。が、現状(2015年12月6日時点)のドロップダウンリストは、開発者側から見るとちょっと使いにくい……、というのが正直な感想です。

```html
<!DOCTYPE html>
<html ng-app="App">
  <head>
    <meta charset="utf-8" />
    <title>Dropdown List Sample</title>
    <link rel="stylesheet" href="//appsforoffice.microsoft.com/fabric/1.0/fabric.min.css">
    <link rel="stylesheet" href="//appsforoffice.microsoft.com/fabric/1.0/fabric.components.min.css">
    <script src="https://code.jquery.com/jquery-2.1.4.min.js"></script>
    <script type="text/javascript" src="https://rawgit.com/furandon-pig/b00192562399e16b8c0b/raw/ab78c58452ee94b8d66ee3798773a6d6e32093ef/officeui_dropdown_helper.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular.min.js"></script>
    <script type="text/javascript">
      var ddd = null;
      $(document).ready(function() {
        if ($.fn.Dropdown) {
          ddd = $('.ms-Dropdown').Dropdown();
        }
        if(fabric && fabric['Dropdown']) {
          var component, componentHolder;
          componentHolder = document.querySelector('.component-holder');

          if (componentHolder) {
            component = new fabric.Spinner(componentHolder);
          }
        }
      });
    </script>
  </head>
  <body ng-controller="AppController">
    <div class="ms-Grid"> <!-- ラジオボタンを表示する -->
      <div class="ms-Grid-row">
        <div class="ms-Grid-col ms-u-sm6 ms-u-md8 ms-u-lg12">
          <div class="ms-Grid-col ms-u-sm2 ms-u-md2 ms-u-lg2">ぽのか先輩は&nbsp;</div>
          <div class="ms-Grid-col ms-u-sm4 ms-u-md4 ms-u-lg4">
            <div class="ms-Dropdown" tabindex="0">
              <select class="ms-Dropdown-select" ng-repeat="list in lists">
                <option class="ms-Dropwodn-item" checked value="{{list.value}}">
                  {{list.label}}
                  <i class="ms-Icon ms-Icon--caretDown"></i>
                </option>
              </select>
            </div>
          </div>
          <div class="ms-Grid-col ms-u-sm2 ms-u-md2 ms-u-lg2">&nbsp;です。</div>
        </div>
      </div>
    </div>
    <script>
      var app = angular.module('App', []);
      app.controller('AppController', ['$scope',
        function($scope) {
          $scope.init = function() {
            $scope.lists = [
              { id: 0, label: "自販機釣り銭パカパカ二郎総帥", value: 0, },
              { id: 1, label: "ポツダム宣言大好き太郎先輩",   value: 1, },
              { id: 2, label: "第三次声優ブームオタク",       value: 2, },
            ];
          }
          $scope.init();
        }
      ]);
    </script>
  </body>
</html>
```

### OfficeUIドロップダウンリストの(バッド)ノウハウ

#### ドロップダウンの初期化

ボタン、テキストボックス等、これまで紹介したきたOfficeUIのコンポーネントはHTMLタグとクラスを指定するだけでそのまま使用できていたのですが、ドロップダウンリストはSELECTタグの値をOfficeUI側でコピーし、独自のポップアップメニューを表示するという実装のため、Webページの読み込み時にOfficeUI用のドロップダウンリストUIの初期化が必要となっています。

具体的には、先のドロップダウンリストのサンプルコードにおける、以下の部分で初期化を行っています。
(実はこのあたりのOfficeUIのGUI初期化処理はよく把握できていません……)

```html
    <script type="text/javascript">
      var ddd = null;
      $(document).ready(function() {
        if ($.fn.Dropdown) {
          ddd = $('.ms-Dropdown').Dropdown();
        }
        if(fabric && fabric['Dropdown']) {
          var component, componentHolder;
          componentHolder = document.querySelector('.component-holder');

          if (componentHolder) {
            component = new fabric.Spinner(componentHolder);
          }
        }
      });
    </script>
```

さらに、上記コード中の「$('.ms-Dropdown').Dropdown();」は[Office Dev Center -  Fabric Home Page](http://dev.office.com/fabric)の[Dropdownサンプル](http://dev.office.com/fabric/components/dropdown)をみると外部のJavaScriptコードを参照する形になっています(単一のHTMLに入れるにはちょっとJavaScriptの行数が多い)。

そのため、本サンプルでも外部JavaScriptとして[Gistに置いたスクリプト](https://gist.github.com/furandon-pig/b00192562399e16b8c0b)を読み込む形になっています。

加えて、ドロップダウンメニューに「▼」(ms-Icon--caretDownアイコン)を表示させるため、以下の修正を加えています。
(MSのサンプルページでは▼が表示されているのだけれど……)

```diff
--- officeui_dropdown_helper.js 2015-12-06 19:08:53.504426520 +0900
+++ officeui_dropdown_helper.js 2015-12-06 19:02:26.570557696 +0900
@@ -1,4 +1,13 @@
-                  
+/*
+ * Office UI Fabric Advent Calendar 2015用のドロップダウンボックスの
+ * サンプルプログラムで使用しているヘルパスクリプト
+ *
+ * 以下のサンプルプログラム(MIT License)のJSコードをサンプル用に一部
+ * 修正しています。
+ *   Dropdown
+ *   http://dev.office.com/fabric/components/dropdown
+ */
+
 // Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE in the project root for license information.
 
 /**
@@ -139,7 +148,9 @@
                     $(this).addClass('is-selected');
 
                     /** Update the replacement dropdown's title. */
-                    $(this).parents().siblings('.ms-Dropdown-title').html($(this).text());
+                    $(this).parents().siblings('.ms-Dropdown-title').html($(this).text()
+                        + '<i class="ms-Icon ms-Icon--caretDown" aria-hidden="true"></i>');
+                    $(this).parents().siblings('.ms-Dropdown-title').css('vertical-align', 'middle');
 
                     /** Update the original dropdown. */
                     var selectedItemText = $(this).text();
```

#### AngularJSでドロップダウン項目にng-model,ng-clickを指定したい

さらにちょっと困る点として、HTMLに記載したSELECTからOfficeUI独自のドロップダウンリスト表示のため、内部用にSELECT項目のデータ(DOM)を作成するような動作になっています。

これはAngularJSを組みわせて使用する場合、「どうやってOfficeUIが作成したDOMにng-XXを設定するか」という問題が発生します。

以前筆者が作成した[OfficeUIのGUI部品サンプルプログラム](http://furandon_pig.bitbucket.org/sample/officeui/)では、若干無理矢理な感じで対応していました。この辺りについても、後日Advent Calendarでご紹介できればと思います。

 * Office UI Fabricサンプル
   * [http://furandon_pig.bitbucket.org/sample/officeui/](http://furandon_pig.bitbucket.org/sample/officeui/)

#### 選択項目の追加はどうするの？問題

もう一つ、Webページ読み込み時にSELECT項目からOfficeUI用のSELECTデータを作成しているため、「後からSELECT項目を追加したい場合はどうするの？」問題があります。これも対応できないわけではありませんが、ちょっとバッドノウハウ感漂う方法になります。

というわけで、OfficeUIのドロップダウンリストについては、もうちょっとノウハウの溜め込みが必要そうです。

## まとめ

今日はOfficeUIのラジオボタンとドロップダウンリストについて解説しました。これで基本的なUIコンポーネントについては一通り紹介できたことになります。

明日はUIコンポーネントから少し離れて、
レスポンシブル
レスポンシブグリッドまわりの話をしたいと思います。

## 参考URL

 * [あいまいみー](http://mangalifewin.takeshobo.co.jp/rensai/imyme/)
 * [Responsive Grid](http://dev.office.com/fabric/styles#responsive-grid)
 * [Office Dev Center -  Fabric Home Page](http://dev.office.com/fabric)
 * [Fabric Tutorial Part 1](https://github.com/OfficeDev/Office-UI-Fabric/blob/master/ghdocs/TUTORIAL.md)
 * [Link and execute external JavaScript file hosted on GitHub](http://stackoverflow.com/questions/17341122/link-and-execute-external-javascript-file-hosted-on-github)