Webアプリを公開しよう! Chrome Web Store/Apps入門

第6回Webアプリを作ろう#3─⁠─Options Page、Web Storage

前回は、OdometerというWebアプリへの機能追加 を通して、Background Pages、Message Passingの詳細を解説しました。今回は、Odometerにさらに機能を追加する形でOptions Page、Web Storageについて解説していきたいと思います。

追加機能の概要

多くのWebアプリでは、使いやすいようにユーザー自身が各種設定を行うことが可能になっています。Odometerでもユーザービリティ向上のためにいくつかの設定項目を用意したいと思います。

例えばOdometerが目的地までの距離を通知する間隔は、目的地から1km圏内であれば200mごとに通知するように設定されています。これらをユーザーが任意の間隔で通知を受けられるように修正します。また、通知間隔以外にもデスクトップへの通知の有効/無効の切り替えやポップアップを表示している通知時間なども同時に設定できるように修正します。

設定画面の呼び出しには、Web AppsのOptions Pageという仕組みを利用し、設定値を保存する方法としてHTML5(広義の意味でのHTML5)のAPIであるWeb Storageを利用します。

図1 Odometer
図1 Odometer
図2 Odometer設定画面
図2 Odometer設定画面

Webアプリの構成

Webアプリを構成するファイルにoptions.htmlとその関連ファイルを追加します。options.htmlは、そのまま設定画面として表示されます。

画像
manifest.json
{
  "name": "Odometer",
  "description": "距離計",
  "version": "0.3",
  "app": {
    "launch": {
      "local_path": "main.html"
    }
  },
  "icons": {
    "16": "icon_16.png",
    "48": "icon_48.png",
    "128": "icon_128.png"
  },
  "options_page": "options.html",
  "background_page": "background.html",
  "permissions": [
    "geolocation",
    "notifications",
    "background"
  ]
}

"options_page"に設定画面となる任意のHTMLファイルとして、今回追加したoptions.htmlを指定しています。

Options Page

Options Pageは、マニフェストファイルで指定されたHTMLファイルをWebアプリの設定画面として表示するだけの非常に単純な仕組みです。⁠新しいタブ」ページのWebアプリの設定メニューから「オプション」をクリックすることで表示することができます。また、Chromeブラウザの設定メニューから「ツール」「拡張機能」「オプション」リンクからも同様に表示することができます。ここで表示された設定画面は単なるHTMLファイルなので 、 Webアプリの設定項目や設定の保存/反映は自分で記述する必要があります。

図3 ⁠新しいタブ」ページからのオプションの表示
図3 「新しいタブ」ページからのオプションの表示
図4 ⁠拡張機能」ページからのオプションの表示
図4 「拡張機能」ページからのオプションの表示

Odometerの設定画面として、以下の3つの設定項目を用意しました。設定項目はフォームで作成し、後述するWeb Storageを使って設定を保存、参照します。

  • デスクトップへの通知の有効/無効
  • 通知時間(秒)
  • 目的地~1km圏内、1km~10km圏内、10km圏外でのそれぞれの通知間隔(mまたはkmで指定)

今回は利用しませんが、Fancy SettingsというChromeの設定画面によく似た設定画面を生成するフレームワークもありますので、興味のある方は利用してみてください。

図5 Fancy Settingsで生成した設定画面
図5 Fancy Settingsで生成した設定画面
options.html

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>Odometer設定</title>
    <link rel="stylesheet" href="css/reset.css">
    <link rel="stylesheet" href="css/options.css">
    <script src="js/options.js"></script>
</head>
<body>
    <header>
        <h1>Odometer設定</h1>
    </header>
    <section>
        <h2>通知設定</h2>
        <div class="internal">
            <div class="setting">
                <p>
                    <label for="notify-setting">デスクトップへ通知: </label>
                    <input type="checkbox" id="notify-setting" checked>
                </p>
                <p>
                    <label for="display-time">通知時間:</label>
                    <input type="number" min="0" step="1" value="5" id="display-time"><span class="aside">※ 0秒を指定すると通知は自動的に閉じられません</span>
                </p>
            </div>
            <div class="detail-setting">
                <table>
                    <tr>
                        <th>通知範囲</th>
                        <th>通知間隔</th>
                    </tr>
                    <tr>
                        <td>目的地~1km園内</td>
                        <td>
                            <input type="number" value="100" min="0.001" step="1" id="short">
                            <select id="short-unit">
                                <option value="m" selected>m</option>
                                <option value="km">km</option>
                            </select>
                            ごとに通知する 
                        </td>
                    </tr>
                    <tr>
                        <td>1km~10km圏内</td>
                        <td>
                            <input type="number" value="1" min="0.001" step="1" id="middle">
                            <select id="middle-unit">
                                <option value="m">m</option>
                                <option value="km" selected>km</option>
                            </select>
                            ごとに通知する 
                        </td>
                    </tr>
                    <tr>
                        <td>10km圏外</td>
                        <td>
                            <input type="number" value="5" min="0.001" step="1" id="long">
                            <select id="long-unit">
                                <option value="m">m</option>
                                <option value="km" selected>km</option>
                            </select>
                            ごとに通知する 
                        </td>
                    </tr>
                </table>
            </div>
        </div>
        <div class="buttons">
            <input type="submit" value="設定" id="apply">
            <input type="reset" value="キャンセル" id="cancel">
        </div>
    </section>
</body>
</html>

Web Storage

Web Storageとは、データをブラウザ上に保存するため仕組みです。ブラウザを閉じてもデータが保持されるlocalStorageと、同一セッションで有効でブラウザを閉じるとデータが消去されるsessionStorageの2種類があります。今回は設定を保存することが目的のため、localStorageを利用します。

データの保存と参照

データの保存と参照には、window.localStorage以下のsetItem()メソッドとgetItem()メソッドを使用します。データは、キーと値をペアとしたものになります。保存できる値は、仕様上はJavaScriptオブジェクトが可能ですが、現状の実装では文字列のみ可能となっています。localStorageとsessionStorageはデータのスコープが違うだけでAPIは同一ですので、もしsessionStorageを利用する場合でも同じ記述方法で問題ありません。また、いずれも保存できるデータ量は、5MBとなっています。

options.js(設定の保存)

// 通知範囲
var ranges = [
    'short',    // 目的地~1km圏内
    'middle',   // 1km~10km圏内
    'long'      // 10km圏外
];

~省略~

// 設定
document.getElementById('apply').addEventListener('click', function(){

    // 通知設定/通知時間
    var setting = {
        notify: document.getElementById('notify-setting').checked,
        display: parseInt(document.getElementById('display-time').value, 10)
    };

    // 通知距離
    for ( var i = 0, len = ranges.length; i < len; i++ ) {
        var distance = parseFloat(document.getElementById(ranges[i]).value, 10);
        if ( document.getElementById(ranges[i] + '-unit').value === 'm' ) {
            distance /= 1000;
        }
        setting[ranges[i]] = distance;
    }
    localStorage.setItem('setting', JSON.stringify(setting));
}, false);

「設定」ボタンのクリック時にsetting変数を用意し、設定オブジェクトとして、それぞれのフォームから取ってきた値を格納しています。この設定オブジェクトをlocalStorage.setItem()メソッドから⁠setting⁠というキーで保存しています。また、保存できる値が文字列のみとなっているため、設定オブジェクトをJSON.stringify()メソッドを使って文字列に変換して格納しています。

options.js(設定の参照)

// 初期化
function init(){
    var setting = JSON.parse(localStorage.getItem('setting'));

    // 通知設定
    document.getElementById('notify-setting').checked = setting.notify;

    // 通知時間
    document.getElementById('display-time').value = setting.display;

    // 通知距離
    for ( var i = 0, len = ranges.length; i < len; i++ ) {
        var distance = setting[ranges[i]];
        if ( distance < 1 ) {
            document.getElementById(ranges[i] + '-unit').selectedIndex = 0;   // 単位:m
            document.getElementById(ranges[i]).value = (distance * 1000);
        } else {
            document.getElementById(ranges[i] + '-unit').selectedIndex = 1;   // 単位:km
            document.getElementById(ranges[i]).value = distance;
        }
    }
}

設定画面のイニシャルセットとして、保存されている設定をlocalStorage.getItem()メソッドで参照して各フォームへセットしています。今度は逆に、保存されている値が文字列となっているためJSON.parse()メソッドを使ってJavaScriptオブジェクトに変換しています。

ここでは、getItem()メソッドとsetItem()メソッドを使っていますが、ほかにも以下のような記述方法でも同様のことができます。お好みの方法で記述してください。


//データの保存(setItemと同じ意味)
localStorage.setting = JSON.stringfy(setting);
localStorage["setting"] = JSON.stringfy(setting);

//データの参照(getItemと同じ意味)
var setting = JSON.parse(localStorage.setting);
var setting = JSON.parse(localStorage["setting"]);
表1 localStorage/sessionStorage
メソッド/プロパティ説明
lengthストレージの長さを返す
key(n)n番目のキーを返す
getItem(key)キーに対応する値を返す
setItem(key, value)キーと値のペアを保存する
removeItem(key)キーと対応する値を削除する
clear()ストレージを消去する

設定の反映

実際に設定された値を使って通知を行うように変更します。設定値をキャッシュする方法もありますが、今回は設定が変更された際にすぐに反映されるように都度参照しています。

background.js(デフォルト設定)

// デフォルト設定
if ( !localStorage.getItem('setting') ) {

    var setting = {
        notify: true,   // 通知設定
        display: 5,     // 通知時間(sec)
        short: 0.2,     // 目的地~1km圏内: 200m
        middle: 1,      // 1km~10km圏内: 1km
        long: 5         // 10km圏外: 5km
    };
    localStorage.setItem('setting', JSON.stringify(setting));
}

まだ何も設定されていない最初のアクセスの際にデフォルト値を利用するようにします。⁠setting⁠をキーとした値がなければ改めてデフォルト設定を保存しています。

background.js(デスクトップへの通知部分を抜粋)

/*
 * 自動更新を開始する
 */
var watchId = 0;    //自動更新停止用のID
function startWatchPosition(sender, sendResponse){
    
    ~省略~

    // デスクトップに通知
    var setting = JSON.parse(localStorage.getItem('setting'));
    if ( setting.notify ) {

        // 距離によってしきい値を変える
        var threshold = 0;
        if ( distance < 1 ) {

            // 目的地~1km圏内
            threshold = setting.short;

        } else if (distance < 10 ) {

            // 1km~10km圏内
            threshold = setting.middle;

        } else {

            // 10km圏外
            threshold = setting.long;
        }

        // 一度通知した距離は再通知しない
        var notifiedKey = Math.floor(distance / threshold) * threshold;
        if ( !notified[notifiedKey]  ) {
            notify('目的地までの距離', '約 ' + distance + ' km', setting.display);
            notified[notifiedKey] = true;
        }
    }
    ~省略~
}

以前より変更された部分は、デスクトップへの通知を行う前に設定を取得し、デスクトップへの通知自体を行うかどうか決定するように処理を追加しています。また、これまで固定値で判定していた箇所を設定値で判定するように変更しています。

これで、Odometerに設定画面が追加され、ユーザー自身で各種設定を行えるようになりました。Web Storageは、Webアプリの設定に限らず、各種データを保存する方法として利用できるので是非活用してください。

まとめ

今回は、Odometerに設定画面を追加し、Options PageとWeb Storageの詳細を解説しました。次回も引き続きさまざまなAPIを解説していきたいと思います。

crxファイルはzipファイルですので、右クリックからのダウンロード後に拡張子をzipに変えていただければ中身を参照できます。Odometerのコード量が増えてきたため、参考のためのソースコードは上記ファイルより取得してくださるようお願いします。

おすすめ記事

記事・ニュース一覧