Material Galleryテンプレートを見てUIの引き出しを増やす

どうもこんばんは、みかんです。

今回は「Material Galleryテンプレートを見てUIの引き出しを増やす」という題で書いてみます。
下記ステップでやっていきます。

**！！！！とつぜんですがたいせつなおしらせ！！！！**

2020年1月27日
App Maker の提供終了に向けた対応のご案内
https://support.google.com/a/answer/9682494?p=am_announcement&visit_id=637157414793613981-426262357&rd=1


??!???!?!?!???!?

残念ながら2021年1月19日には終了してしまうそうです(涙)
移行先はAppSheetかApp EngineかGoogleフォームだそうです・・・

消すのは忍びないので記事は残して置きますが、何かの間違いじゃ無い限り役に立つことはないでしょう・・・


**！！！！おしらせはおわりです！！！！！**

* 1 下準備
* 2 動作確認してざっとイメージを掴む
* 3 Material Galleryのデータの登録方法を確認する
* 4 自分でデータを追加してみる

# 1 下準備

今回はお題の通り、Material Galleryテンプレートを見ていきます。
このテンプレートを新規作成するだけで準備は終了です。

|![ap05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26085/9e764268-c1a1-b199-edf6-90854bdabd43.png)|
|:-:|

# 2 動作確認してざっとイメージを掴む

後述しますが、UIを確認するにあたり必要なデータは初回ロード時に用意されるので、アプリを新規作成したらすぐにpreviewで動作確認が可能です。
早速previewしてみましょう。

|![gif001-compressor.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26085/87c8b0ff-d242-f337-f3e2-9c2c1ac0009a.gif)|
|:-:|

「Sticker Sheet」というページが最初に表示されます。
メニューを見てみると、「Page」「Sticker Sheet」「Admin」の3種のページへのボタンが表示されます。
メインはSticker Sheetで、PageとAdminはとりあえず無視してOKです。

UIのサンプルは、大きく３つのカテゴリで用意されています。

## User Cards

|![gif002-compressor.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26085/901fb0e6-0ec3-b8b3-052a-be7e6e5b9933.gif)|
|:-:|


ユーザーの一覧と、その詳細情報ですね。

詳細画面のリンク以外のボタンは反応しないので、連打してもOKです。

## Utility Cards

|![gif003-compressor.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26085/26461c9d-0259-29e5-f2d2-c4fe7a4c7ff4.gif)|
|:-:|

便利なカードUIが並んでいます。
Event CardとMetrics Cardの写真はデータと連動していますが、他はUI Onlyです。

## Project Cards

|![gif004-compressor.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26085/86a9f0cd-d51d-a980-9106-0f4f87509baf.gif)|
|:-:|

プロジェクト管理の観点で有用そうなUIが並んでいます。
Project Card AccordionのSelect quarterを変更すると実際にデータがフィルタリングされます。
Project Card Listの方は変更しても反映されませんが、これはonValueEditのReload Datasourceが抜けているせいでした。気にしたら負けですね。。

## その他について

ヘッダーの検索ボックスも機能します。
`@datasources.SampleData.query.filters.Project._contains`
が設定されているので、入力した文字がプロジェクト名に含まれるものだけが表示されるようになります。

また、DialogとMenuがPopupの様な動きをするにも関わらずフラグメントで作成されています。
これはこのテンプレートがまだApp MakerにPopupの機能が無かった頃の名残です。
他のテンプレートも、初期の名残が残っている部分があったりするので気をつけたほうが良いでしょう。

ちなみにアニメーションはどうしているのかというと

```css
/* MENU */
.app-Menu-MenuContainer {
  z-index: 7;
  transition: all .22s ease-in;
}
```

こんな感じでCSSが設定されています。

# 3 Material Galleryのデータの登録方法を確認する

このテンプレートでデータをどのように投入しているか確認してみます。

App Makerのアプリでは、起動時に処理を実行できる「App startup script」という設定があります。設定ページにスクリプトを書く欄があります。

|![ap01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26085/024ae299-1e49-1c3f-6569-d03e9876322a.png)|
|:-:|

これはクライアントスクリプトとして実行されます。上の例では、`loadSampleDataIfNeeded(loader)`という関数を呼んでいます。
この関数が記述されているのはSCRIPTSの「AppStart」スクリプトです。

```javascript
/**
 * This automatically adds sample records to the SampleData model if no data is present.
 * Called from the App Start script.
 * @param {AppLoader} loader - system object controlling the application's load.
 */
function loadSampleDataIfNeeded(loader) {
  loader.suspendLoad();

  google.script.run
    .withSuccessHandler(function() {
      loader.resumeLoad();
    })
    .withFailureHandler(function(error) {
      console.log('loadSampleDataIfNeeded failed ' + JSON.stringify(error));
      loader.resumeLoad();
    })
    .loadSampleDataIfNeeded();
}
```

サーバースクリプトに処理を投げているようです。
サーバースクリプトに対する処理は非同期であるため、何も対策しなければサーバーにリクエストを出してすぐアプリの起動に移ってしまうため、都合が悪いです。
非同期処理が終わるまでの間、この初回起動処理を中断させておくために
`loader.suspendLoad()`という関数を呼び、処理を継続して良いタイミングになったら`loader.resumeLoad()`という関数を呼んでいます。

次に、サーバースクリプト側の関数を見てみます。

```javascript
/**
 * This adds sample records to the SampleData model so you can see how the UI cards work.
 * This is just one way to add data. You can also add records using the "Admin" page which
 *     uses the Form widget.
 */
function loadSampleData() {
  var avatar_1 = 'https://www.gstatic.com/appmaker/avatar-1.png';
  var avatar_2 = 'https://www.gstatic.com/appmaker/avatar-2.png';
  //<省略>
  var image_1 = 'https://www.gstatic.com/appmaker/wallpaper-kleiner-google-blue.jpg';
  var image_2 = 'https://www.gstatic.com/appmaker/wallpaper-kleiner-google-red.jpg';

  /* This is the order we will enter the records in the array below.
       [0]Project [1]Status [2]Quarter [3]Priority [4]Description [5]Link [6]UserName [7]FullName
       [8]JobTitle [9]UserImageURL [10]Date [11]ModelName [12]Tags [13]Product [14]Type [15]ProductImageURL
    */
  recordData = [
    ["Hire engineer","In-progress","Q1",0,"Front-end engineer for dev team",'http://google.com',"TaylorS","Taylor Sweet","Hiring Manager", avatar_4, new Date(2016,1,1),"SampleData","Hiring","App Maker","Operations",image_1],
    ["Give team bonuses","Review","Q1",1,"10K quarterly bonuses",'http://google.com',"TonyR","Anthony Rollins","Leader",avatar_3,new Date(2016,1,2),"SampleData","Budget","App Maker","Operations",image_2],
    //<省略>
  ];
  var records = [];
  for (var i = 0; i < recordData.length; i++) {
    var record = app.models.SampleData.newRecord();
    record.Project = recordData[i][0];
    record.Status = recordData[i][1];
    record.Quarter = recordData[i][2];
    record.Priority = recordData[i][3];
    record.Description = recordData[i][4];
    record.Link = recordData[i][5];
    record.UserName = recordData[i][6];
    record.FullName = recordData[i][7];
    record.JobTitle = recordData[i][8];
    record.UserImageURL = recordData[i][9];
    record.ModelName = recordData[i][11];
    record.Tags = recordData[i][12];
    record.Product = recordData[i][13];
    record.Type = recordData[i][14];
    record.ProductImageURL = recordData[i][15];

    records.push(record);
  }
  app.saveRecords(records);
}
```
ベタ書きでエイヤと入れているみたいですね。
他にも関数があるので確認します。

```javascript
/**
 * This automatically adds sample records to the SampleData model if no data is present.
 * Called from the App Start script.
 */
function loadSampleDataIfNeeded() {
  var query = app.models.SampleData.newQuery();
  query.limit = 1;
  var records = query.run();
  if (records.length === 0) {
    loadSampleData();
  }
}


/**
 * Deletes all records in the SampleData model.
 */
function deleteAllData() {
  var records = app.models.SampleData.newQuery().run();
  app.deleteRecords(records);
}


/**
 * Cleans up the database and re-generates sample data for SampleData model.
 */
function resetSampleData() {
  deleteAllData();
  loadSampleData();
}
```

クライアントから呼び出されている関数は`loadSampleDataIfNeeded()`ですね。
0件ならデータ無しと判断して投入しています。無駄な読込をしないように最大1件の取得としていますね。

`deleteAllData`はデータをすべて消去している様子です。一度削除対象のデータを取得する必要があるので、もし大量データを消すような処理を書く場合はパフォーマンスなどに注意してチューニングする必要がありそうですね。App Maker外からCloud SQLに直接命令送ったほうが早そうですが。

# 4 自分でデータを追加してみる

「Admin」ページに行くと、モデルに対応した入力フォーム、編集フォーム、それとデータをリセットするためのボタンがあります。

|![ap04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/26085/e86c4946-7c7d-5f8b-be84-f79e877b6d6e.png)|
|:-:|

日本語いれたらどうなるとか、長い文字を入れたらどうなるのかといったチェックは、この画面でデータを投入すればすぐ試せそうです。

Material Galleryテンプレートを実行してみて、良いなと思ったらコピペで持っていける他、作り方そのものもエディタで確認できるということで、テンプレートの中では最初に確認しておきたいものの1つになりますね。

今後、それぞれのUIについても紹介してみようと思っています。

以上です。良いApp Makerライフを!