nkjmkzk.net

powered by Kazuki Nakajima

AngularJSではじめるHTML5開発 – Part6 UI Bootstrapを用いたプログレスバーとモーダルダイアログ

Part5の続きです。

今回は更新ボタンをクリックした後に経過を表示するプログレスバーと、重複送信を防止するモーダルウィンドウを合わせて実装していきます。

inprogress

まずデモで動作を確認してみましょう。

デモ

それでは実装へ。

UI Bootstrapを組み込む

UI BootstrapはAngular UIチームによって開発されているBootstrap用のコンポーネントライブラリです。

このライブラリを活用することにより、Bootstrapが元々提供しているプログレスバーとモーダルウィンドウをAngularJSと連携させて利用できるようになります。

このUI BootstrapもCDNで提供されているのでまずはそのファイルを読み込みます。

<head>
	<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.1.0/css/bootstrap.min.css"></link>
	<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
	<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.1.0/js/bootstrap.min.js"></script>
	<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.10/angular.min.js"></script>
	<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.10.0/ui-bootstrap-tpls.min.js"></script>
	<c:RemoteTK />

 

次にUI BootstrapをAngularJSに組み込みます。

UI BootstrapはAngularJSの「モジュール」として提供されています。AngularJSではこのようなモジュールを実行時に組み込むことができるようになっています。

モジュールを組み込むには、最初のangular.module()メソッドにおいて、第二引数の配列にモジュール名を指定してあげるだけです。

	var ngbootcamp = angular.module('ngbootcamp', ['ui.bootstrap']);

これでUI Bootstrapが利用できる状態となりました。

 

モーダルウィンドウを作成する

下記のモーダルウィンドウ用HTMLマークアップを<body>タグ配下の最下部に追加します。

    <!-- Modal for inProgress -->
    <script type="text/ng-template" id="T_inProgress">
        <div class="modal-header">
            <h3>
                <span ng-show="remotingProgress < 100">処理中</span>
                <span ng-show="remotingProgress == 100">完了</span>
            </h3>
        </div>
        <div class="modal-body">
            <div>{{remotingStatus}}</div>
            <progressbar ng-class="(remotingProgress < 100) ? 'progress-striped active' : 'progress'" value="remotingProgress" type="success"></progressbar>
        </div>
        <div class="modal-footer" ng-show="remotingProgress == 100">
            <button type="button" class="btn btn-success" ng-click="$close()">閉じる</button>
        </div>
    </script><!-- Modal for inProgress-->

ひとつずつみていきましょう。

 

	<script type="text/ng-template" id="T_inProgress">

このscriptタグはちょっと特殊ですね。配下に書かれているのはscriptではなくHTMLマークアップです。

後にJavascriptからこのスクリプトのidを指定して$modal.open()メソッドを実行することによって、内部に書かれたHTMLマークアップがモーダルウィンドウとして表示されることになります。

 

        <div class="modal-header">
            <h3>
                <span ng-show="remotingProgress < 100">処理中</span>
                <span ng-show="remotingProgress == 100">完了</span>
            </h3>
        </div>

header部分です。no-showによって、remotingProgressの値に応じて表示される文字列を切り替えています。remotingProgressには処理の経過が0 〜 100で代入されます。100未満であればまだ処理を実行中なので「処理中」と表示し、100であれば処理が完了したということで「完了」が表示されます。

 

        <div class="modal-body">
            <div>{{remotingStatus}}</div>
            <progressbar ng-class="(remotingProgress < 100) ? 'progress-striped active' : 'progress'" value="remotingProgress" type="success"></progressbar>
        </div>

進捗状況を報告するメッセージとプログレスバーです。
remotingStatusには現在の処理が随時表示されます。
プログレスバーはUI Bootstrapが提供するカスタムディレクティブ、 で実装しています。このプログレスバーにはng-classに条件によって切り替わるクラス指定がおこなわれています。remotingProgressが100未満であればprogress-striped activeが、100であればprogressが適用されます。つまりremotingProgressが100未満であればプログレスバーは斜線がかったカラーでさらにそれが床屋の看板のようにグルグルと回っているように表示されます。100になるとそのグルグルを止めることで完了した感を演出しています。

 

		<div class="modal-footer" ng-show="remotingProgress == 100">
			<button type="button" class="btn btn-success" ng-click="$close()">閉じる</button>
		</div>

完了時に表示される「閉じる」ボタンを表示するエリアです。こちらもng-showによってremotingProgressが100になったときにはじめて表示されます。

 

updateGuest()にモーダルウィンドウを表示する仕組みを組み込む

udpateGuestメソッドに下記のようにモーダルウィンドウ用の仕組みを追加します。

        $scope.updateGuest = function(){
            var guest = angular.copy($scope.guest);
            delete guest.attributes;

            $modal.open({
                templateUrl: 'T_inProgress',
                backdrop: 'static',
                scope: $scope
            });

            $scope.remotingProgress = 50;
            $scope.remotingStatus = "データを更新しています...";

            $scope.force.update(
                "guest__c",
                $scope.guest.Id,
                guest,
                function(result){
                    $scope.remotingProgress = 100;
                    $scope.remotingStatus = "更新が完了しました。";
                },
                function(result){
                    console.log(result);
                }
            );
        }

ひとつずつみていきましょう。

 

			$modal.open({
				templateUrl: 'T_inProgress',
				backdrop: 'static',
				scope: $scope
			});

まず$modal.open()メソッドで更新処理を開始する前にモーダルウィンドウを表示させています。$modal.open()のオプションについてみていきましょう。

  • templateUrl => モーダルウィンドウを描写するHTMLファイルを指定します。別ファイルとして作成しそのURLを指定するというのが最終的にはスマートだと思いますが、今回はこのHTMLマークアップを同一ファイル内に<script>タグで記載しています。その場合、<script>タグのid値を指定することでモーダルウィンドウのマークアップを指定することができます。
  • backdrop => モーダルウィンドウの挙動を指定します。通常、モーダルウィンドウの背景をクリックするとモーダルウィンドウが解除されてしまうのですが、今回のユースケースでは重複送信防止のため処理が完了するまでは操作をブロックしておきたいところです。その場合はこのbackdropをstaticに設定することで希望する挙動を実現できます。
  • scope => デフォルトではモーダルウィンドウには完全に切り離されたscopeが割り当てられます。今回は処理の進捗をあらわすremotingProgressおよびremotingStatusという変数を処理実行側とモーダルウィンドウ側で共有したいため、現在のscopeの子scopeを作成してそれをモーダルウィンドウに割り当てています。(子scopeは親scopeの変数にアクセスすることができます)

 

            $scope.remotingProgress = 50;
            $scope.remotingStatus = "データを更新しています...";

今回の仕組みではremotingProgressの値は最終的にプログレスバーの長さを指定することになります。今回は初期値として50を割り当てていますのでプログレスバーは50%の長さからスタートすることになります。
remotingStatusには現在の処理状況をセットします。これはそのままプログレスバーの上に表示されることになります。

 

            $scope.force.update(
                "guest__c",
                $scope.guest.Id,
                guest,
                function(result){
                    $scope.remotingProgress = 100;
                    $scope.remotingStatus = "更新が完了しました。";
                },
                function(result){
                    console.log(result);
                }
            );

更新処理成功時のコールバックでremotingProgressに100をセットし、remotingStatusに更新完了のメッセージをセットしています。これによって処理完了後にプログレスバーが100%まで伸長し、それに連動して「閉じる」ボタンが表示されることになります。

これでプログレスバーとモーダルダイアログが完成です。
 

次回はデータ更新後に画面をリフレッシュし、サイドバーに最新情報が反映される仕組みを実装していきます。

例によって最後に現時点での全ソースコードを掲載しておきます。

<apex:page showHeader="false" standardStyleSheets="false" applyBodyTag="false" applyHtmlTag="false" docType="html-5.0" >
  
<html ng-app="ngbootcamp">
<head>
    <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.1.0/css/bootstrap.min.css"></link>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.1.0/js/bootstrap.min.js"></script>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.2.10/angular.min.js"></script>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-bootstrap/0.10.0/ui-bootstrap-tpls.min.js"></script>
    <c:RemoteTK />
    <script>
    var ngbootcamp = angular.module('ngbootcamp', ['ui.bootstrap']);

    ngbootcamp.controller('guestCtl', function($scope, $modal){
        $scope.updateGuest = function(){
            var guest = angular.copy($scope.guest);
            delete guest.attributes;

            $modal.open({
                templateUrl: 'T_inProgress',
                backdrop: 'static',
                scope: $scope
            });

            $scope.remotingProgress = 50;
            $scope.remotingStatus = "データを更新しています...";

            $scope.force.update(
                "guest__c",
                $scope.guest.Id,
                guest,
                function(result){
                    $scope.remotingProgress = 100;
                    $scope.remotingStatus = "更新が完了しました。";
                },
                function(result){
                    console.log(result);
                }
            );
        }

        $scope.getGuest = function(recordId){
            $scope.force.retrieve(
                "guest__c",
                recordId,
                "Id,Name,email__c",
                function(result){
                    $scope.guest = result;
                    $scope.$apply();
                },
                    function(result){
                    console.log(result);
                }
            );
        }

        $scope.getGuests = function(){
            var soql = "select Id, Name, CreatedDate from guest__c";
            $scope.force.query(
                soql,
                function(result){
                    $scope.guests = result.records;
                    $scope.$apply();
                },
                    function(result){
                    console.log(result);
                }
            );
        }

        $scope.force = new remotetk.Client();
        $scope.getGuests();
    });
    </script>
</head>
<body ng-controller="guestCtl">
    <div class="container" style="margin-top:20px;">
        <div class="row">
            <div class="col-md-4">
                <div class="panel panel-default">
                    <div class="panel-heading">
                        ゲスト
                    </div>
                    <div class="list-group">
                        <a class="list-group-item" href="#" ng-click="getGuest(guest.Id)" ng-repeat="guest in guests">{{guest.Name}}</a>
                    </div>
                </div>
            </div>
            <div class="col-md-8">
                <h1>{{guest.Name}}</h1>
                <form role="form">
                    <div class="form-group">
                        <label>ゲスト名</label>
                        <input ng-model="guest.Name" type="text" class="form-control" placeholder="ゲスト名" />
                    </div>
                    <div class="form-group">
                        <label>Email</label>
                        <input ng-model="guest.email__c" type="email" class="form-control" placeholder="Email" />
                    </div>
                    <div class="form-group">
                        <button class="btn btn-success" ng-click="updateGuest()">更新</button>
                    </div>
                </form>
            </div>
        </div>
    </div>

    <!-- Modal for inProgress -->
    <script type="text/ng-template" id="T_inProgress">
        <div class="modal-header">
            <h3>
                <span ng-show="remotingProgress < 100">処理中</span>
                <span ng-show="remotingProgress == 100">完了</span>
            </h3>
        </div>
        <div class="modal-body">
            <div>{{remotingStatus}}</div>
            <progressbar ng-class="(remotingProgress < 100) ? 'progress-striped active' : 'progress'" value="remotingProgress" type="success"></progressbar>
        </div>
        <div class="modal-footer" ng-show="remotingProgress == 100">
            <button type="button" class="btn btn-success" ng-click="$close()">閉じる</button>
        </div>
    </script><!-- Modal for inProgress-->

</body>
</html>
  
</apex:page>

 

関連情報

without comments

Written by 中嶋 一樹

2月 15th, 2014 at 4:49 pm

Leave a Reply