とりゅふの森

GCPデータエンジニアとして生きる

Chrome拡張機能開発に入門する②オプション画面を作成しよう

f:id:true-fly:20220115170049p:plain

こんにちは、前回はchrome拡張機能のポップアップアプリケーションを作成する記事をご紹介しました。
拡張機能アイコンをクリックすると画面が立ち上がり、情報を入力すると、Trello APIでTrelloにデータが保存される機能です。

www.true-fly.com

しかし、前回紹介したソースは、API Keyなどのユーザ固有の情報がベタ書きされたままのため、他人に配布して、せっかく開発した拡張機能を共有することができない状態でした。
ユーザ固有の情報は、設定画面などで入力を促すようにしたいところ。

ということで、本日のテーマはこちら!

chrome Storage APIを使って、Chrome拡張機能のオプション画面を作成しよう

chrome Storage APIを用いれば、chromeにデータを保存することができます。
API KEYなどはオプション画面から値を入力し、chromeにデータを保存するようにしましょう。

今回作るもの

今回は、前回作成した拡張機能に、オプション画面を追加します。 www.true-fly.com

以下のように画面上で値を入力し、chromeにデータを保存できるようにします。

f:id:true-fly:20220115173631p:plain

ディレクトリ構成

今回のディレクトリ構成は以下の通りです。
前回との差分で、options.htmlというファイルを追加しています。

.
├── css
│   ├── bootstrap.min.css
│   └── style.css
├── js
│   ├── app.js ☆変更
│   ├── bootstrap.bundle.min.js
│   ├── jquery-3.6.0.min.js
│   └── vue.js
├── manifest.json ☆変更
├── options.html ★新規追加
└── popup.html

manifest.jsonを変更する

manifest.jsonを以下に書き換えます。

{
    "name": "Trello Memo",
    "version": "1",
    "manifest_version": 2,
    "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self';",
    "permissions": [
        "storage"
    ],    
    "browser_action": {
        "default_popup": "popup.html"
    },
    "options_ui": {
        "page": "options.html",
        "open_in_tab": false
    }
}

追加したのは以下の2点です。

1点目は、chrome Storage APIの権限設定です。以下を追加することで、APIを有効化します。

    "permissions": [
        "storage"
    ],   

2点目はオプション画面の追加です。pageには画面のHTMLを設定します。

    "options_ui": {
        "page": "options.html",
        "open_in_tab": false
    }

options.htmlを作成する

以下を作成します。ベースはpopup.htmlで、API呼び出し処理はjs/app.js内のsaveOptions()で実装します。

<html>
<head>
    <link rel="stylesheet" href="css/bootstrap.min.css">
    <link rel="stylesheet" href="css/style.css">
    <title>Trello Memo Options</title>
</head>
<body>
    <div id="app">
        <div class="container-fluid">
            <div class="form-group">
                <div class="row"><div class="col-sm-5">Trello API Key</div></div>
                <div class="row">
                    <div class="col-sm-5">
                        <input id="input-title" type="password" class="form-control" v-model="trelloApiKey">
                    </div>
                </div>
                <div class="row"><div class="col-sm-5">Trello API Token</div></div>
                <div class="row">
                    <div class="col-sm-5">
                        <input id="input-title" type="password" class="form-control" v-model="trelloApiToken">
                    </div>
                </div>
                <div class="row"><div class="col-sm-5">Trello List ID</div></div>
                <div class="row">
                    <div class="col-sm-5">
                        <input id="input-title" type="text" class="form-control" v-model="trelloListId">
                    </div>
                </div>
                <br>
                <div class="row">
                    <div class="form-check form-check-inline">
                        <div class="col-sm-10">
                            <button id="button" class="form-control btn-info" v-on:click="saveOptions">Save</button>
                        </div>
                    </div>
                </div>
            </div>
        </div>

        <!-- Modal -->
        <div class="modal fade" id="message-modal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
            <div class="modal-dialog" role="document">            
                <div class="modal-content">
                    <div v-if ="isFailed()">
                        <div class="modal-header" id="error-modal">
                            {{ errorMessage }}
                            <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                                <span aria-hidden="true">&times;</span>
                            </button>
                        </div>
                    </div>
                    <div v-else>
                        <div class="modal-header" id="info-modal">
                            {{ modalMessage }}
                            <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                                <span aria-hidden="true">&times;</span>
                            </button>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <script type="text/javascript" src="js/jquery-3.6.0.min.js"></script>
    <script type="text/javascript" src="js/bootstrap.bundle.min.js"></script>   
    <script type="text/javascript" src="js/vue.js"></script>   
    <script type="text/javascript" src="js/app.js"></script>
</body>
</html>

js/app.jsを変更する。

js/app.jsを以下に変更します。

let app = new Vue({
    el: '#app',
    data: {
        title: '',
        desc: '',
        modalMessage: '',
        errorMessage: '',
        trelloApiKey: '',
        trelloApiToken: '',
        trelloListId: ''

    },
    created:async function(){
        let self = this;
        await chrome.storage.sync.get(['trelloApiKey', 'trelloApiToken', 'trelloListId'], function(items) {
            self.trelloApiKey = items.trelloApiKey;
            self.trelloApiToken = items.trelloApiToken;
            self.trelloListId = items.trelloListId;
        });
    },
    methods: {
        isFailed() {
            if (this.errorMessage != '') {
                return true;
            }
            return false;
        },
        create() {
            let self = this;
            self.modalMessage = '';
            self.errorMessage = '';
            if (self.title == '') {
                self.errorMessage = 'Please enter a title!'
                $('#message-modal').modal();
                return
            }
            $.ajax({
                type:'POST',
                url: 'https://api.trello.com/1/cards?idList=' + self.trelloListId
                    + '&key=' + self.trelloApiKey
                    + '&token=' + self.trelloApiToken,
                success: function(data) {
                    self.modalMessage = 'Succeeded in creating a new card!'
                    $('#message-modal').modal();
                },
                data: { 
                    'name': self.title,
                    'desc': self.desc
                },
                dataType : 'json',
                error: function(jqXHR) {
                    console.log(jqXHR)
                    self.errorMessage = 'Failed to create a new card!'
                    $('#message-modal').modal();
                }
            });
        },
        saveOptions() {
            let self = this;
            chrome.storage.sync.set({
                trelloApiKey: self.trelloApiKey,
                trelloApiToken: self.trelloApiToken,
                trelloListId: self.trelloListId
            }, function() {
                window.close();
            });
        }
    }
});

以下の3つの変数がオプション画面で設定する変数になります。

  • trelloApiKey
  • trelloApiToken
  • trelloListId

createdで、Vueインスタンスが生成されたタイミングで、chrome.storage.sync.get()を実行し、chromeに保存されたデータを取得します。

    created:async function(){
        let self = this;
        await chrome.storage.sync.get(['trelloApiKey', 'trelloApiToken', 'trelloListId'], function(items) {
            self.trelloApiKey = items.trelloApiKey;
            self.trelloApiToken = items.trelloApiToken;
            self.trelloListId = items.trelloListId;
        });
    },

オプション画面から値を入力し、chromeに保存するのは、saveOptions()内で実装しています。
chrome.storage.sync.set()で値をchromeに保存しています。

        saveOptions() {
            let self = this;
            chrome.storage.sync.set({
                trelloApiKey: self.trelloApiKey,
                trelloApiToken: self.trelloApiToken,
                trelloListId: self.trelloListId
            }, function() {
                window.close();
            });
        }

動作確認

manifest.jsonを変更したので、拡張機能を再読み込みしてデプロイしましょう。
拡張機能のアイコンを右クリックして、「オプション」が活性化されているのが確認できます。

f:id:true-fly:20220118002410p:plain

「オプション」をクリックして、以下の画面が表示されたら無事完成です!

f:id:true-fly:20220115173631p:plain

まとめ

以上、前回に引き続き、Chrome拡張機能の開発方法について簡単にご紹介しました。
今回は超入門ということで、ちょっとしたポップアップアプリケーションを開発しましたが、Chrome拡張機能はページ情報の読み込みなど、今回ご紹介したこと以上にできることが多いので、今後も試していきたいと思います。