window.confirm()な確認ダイアログをOffice UI Fabricで実現する

# Office UI Fabricとwindow.confirm()

[Office UI Fabric](https://github.com/OfficeDev/Office-UI-Fabric)(以下"OfficeUI"と表記)のサンプルとして、[ToDoアプリ](http://furandon_pig.bitbucket.org/sample/officeui/todo_app.html)を作成しています。

 * ToDo application
   * http://furandon_pig.bitbucket.org/sample/officeui/todo_app.html

一見、Officeアプリ風な見栄えになってはいるのですが、ToDoの削除を行う際の確認ダイアログとしてwindow.confirm()を使っているため、そこだけ見栄えが異なってしまっています。

![img001.png](https://qiita-image-store.s3.amazonaws.com/0/61137/0db2d105-622f-3bcb-c3ec-7382d922d6d7.png)

できればこの確認ダイアログもOfficeUIに合わせた見栄えにしたいものです。

# OfficeUI風のconfirm()を実装する

confirm()はユーザのYes,Noの確認用ダイアログといえるので、以前のエントリ「[Office UI Fabricでダイアログボックスを使用する(暫定版)](http://qiita.com/furandon_pig/items/c5f0efe1c5102f96970c)」を応用すればOfficeUI風の確認ダイアログが作れそうです。

ダイアログボックスのサンプルは以下のURLで試せるようになっているので、アイコンをつけたりヘッダのスタイルを変えたりしてフィーリングに合うUIを検討してみます。

 * http://furandon_pig.bitbucket.org/sample/officeui/dialog.html

こんな感じですかね(これで今回の話は8割がた完了のような気がします...)。

![img003.png](https://qiita-image-store.s3.amazonaws.com/0/61137/21003714-11af-7c3d-2940-5aae36e463b6.png)

## 確認用ダイアログのHTMLコード

先のサンプルURLで検討した内容を元に、確認用ダイアログのHTMLコードを作成します。サンプルアプリは[AngularJS](https://angularjs.org/)を使用しているので、ダイアログのタイトルやメッセージは変数のバインドで実現します。

```html
<!-- confirmダイアログ -->
<div class="ms-Dialog ms-Dialog--lgHeader" style="visibility:{{showFlgConfirmDlg}}">
  <div class="ms-Overlay">
    <div class="ms-Dialog-main">
      <div class="ms-Dialog-header">
        <p class="ms-Dialog-title">
        <i class="ms-Icon ms-Icon--question"></i>
          {{confirmTitle}}
        </p>
      </div>
      <div class="ms-Dialog-inner">
        <div class="ms-Dialog-content">
          <label class="ms-Label">
            {{confirmMsg}}
          </label>
        </div>
        <div class="ms-Dialog-actions">
          <div class="ms-Dialog-actionsRight">
            <button class="ms-Dialog-action ms-Button ms-Button--primary" ng-click="closeConfirmYes()">
              <span class="ms-Button-label">Yes</span>
            </button>
            <button class="ms-Dialog-action ms-Button ms-Button" ng-click="closeConfirmNo()">
              <span class="ms-Button-label">No</span>
            </button>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>
```

## JavaScript側でのダイアログ制御方法

ダイアログの表示・非表示とYes,Noボタン押下に応じたJavaScript側の処理は以下のようになります。確認用ダイアログの処理は表示のみなので、Yes,Noに対応する処理を関数の形で渡し、押されたボタンに応じてそれぞれの処理が呼ばれるようにします。

さらに、ダイアログを表示してもそのままでは処理が進んでしまう点に注意が必要です。例えば、「ToDoの削除」ボタンを押すとToDoが1件削除されてから確認ダイアログが表示されてしまいます。対応方法は**イベントハンドラ側でfalseを返す**ことです。これにより処理が中断し、Yes,Noボタンの押下に対応する処理が行われてから実行が継続されます。
(この手法は[sasata299](https://github.com/sasata299)さんのブログエントリ、「[onsubmitやonclickで処理を中断させる方法(return false)](http://blog.livedoor.jp/sasata299/archives/51373855.html)」を参考にさせていただきました)

```js
var app = angular.module('ToDoApp', []);

app.controller('ToDoAppCtrl', ['$scope',
  function($scope) {
    // ...中略...

    // 確認用ダイアログの表示
    $scope.showConfirm = function(title, msg, ok_func, no_func) {
      $scope.confirmTitle = title;
      $scope.confirmMsg   = msg;

      $scope.confirmTrueFunc  = ok_func;
      $scope.confirmFalseFunc = no_func;

      $scope.showFlgConfirmDlg = "visible";

      return false;  /* ここでfalseを返すのがポイント */
    }

    // "Yes"ボタン押下時のハンドラ
    $scope.closeConfirmYes = function() {
      $scope.showFlgConfirmDlg = "hidden";
      if ($scope.confirmTrueFunc != undefined) {
        $scope.confirmTrueFunc();
      }
    }

    // "No"ボタン押下時のハンドラ
    $scope.closeConfirmNo = function() {
      $scope.showFlgConfirmDlg = "hidden";
      if ($scope.confirmFalseFunc != undefined) {
        $scope.confirmFalseFunc();
      }
    }

    // ページ読み込み時にダイアログを閉じる(非表示にする)
    $scope.closeConfirmNo();
  }
]);
```

さて、この確認ダイアログは以下のようにして利用します。Yes,Noボタン押下時のハンドラ関数は引数として渡すとダイアログ表示関数を呼び出した時にハンドラ関数が走ってしまいます。そのため、無名関数にラップして渡すというトリッキーな方法になっています(何かもっとスマートな方法があるような気がするのだけれど...)。

```js
    // ToDoを1件削除する
    $scope.remove = function(todo) {
      var index = lookupIndex(todo.id);

      $scope.showConfirm(
        "ToDoの削除",
        "ToDo「" + $scope.todos[index].task + "」を削除しますか？",
        function() { removeItem(index); }
      );
    }

    removeItem = function(index) {
      $scope.todos.splice(index, 1);
      // local storageへの保存処理など...
    }
```

いずれにせよ、これで確認ダイアログの実装ができました。実際に表示してみると以下のようになります。

![img002.png](https://qiita-image-store.s3.amazonaws.com/0/61137/6aec42f5-3e0e-5fb2-275e-de80252bb966.png)

これでOfficeUIな確認ダイアログが利用できるようになりました。

# まとめ

OfficeUIな確認ダイアログの実現方法をまとめてみました。今回はYes,Noの確認用ダイアログのみでしたが、テキストフィールドに入力させたり、メッセージボックス的なダイアログだったりなど、種別の異なる確認・通知用のダイアログがあるので、それらを複雑にならないよう上手いこと実現してみたいものです。

# 参考URL

 * [onsubmitやonclickで処理を中断させる方法(return false)](http://blog.livedoor.jp/sasata299/archives/51373855.html)
 * [Office UI Fabric](https://github.com/OfficeDev/Office-UI-Fabric)