Excelを超えろ!! Power Appsで一括登録フォームを作ろう！

# 2024.01.24
:::note alert
作者により、本記事のOnStartのコードは**クソコード** 認定しました。
コードの整理は[こちら](https://qiita.com/DEmodoriGatsuO/items/4d023dec427555014734)をどうぞ
:::

https://qiita.com/DEmodoriGatsuO/items/4d023dec427555014734

# はじめに
「(開発者)Power Appsで入力ミスを防げるアプリを作りました！」
「(ユーザー)えーExcelのほうが使いやすいよ」

こんな経験はありませんか？

せっかく便利なアプリケーションを作っても、**Excel運用のほうがいいんだよね～**
という一声でヤル気をなくすのはもったいない。

Excel運用に課題を感じて、Power Appsでの運用を考えている場合は、
**業務に関わるあらゆる人が、Excel運用の恩恵を超える仕組みを作るべきです！**

## Excelを超えろ!!
既存の運用から、業務に関わる多方面の人が恩恵を受けられなければ、せっかく一生懸命作ったアプリも歓迎されないものになってしまいます。

**Excel運用よりもいいじゃん！！** と思われる仕組みづくりが必要。
ということで、Excelの優位性である
**セル範囲のコピーをして入力の手間を一定数、省ける** という部分に焦点を当て、
**Power Apps**のTipsを書きます！

某SaaSの機能から着想を得た、フォームの複製機能です。

:::note warn
Excelというワードが多々出ていますが、筆者は重度のExcelジャンキーです。
:::

# 1. SharePoint Listsでデータソースを作成
まずはデータソースを作成します。
列の作成時は、英語で作成しています。

日本語で作成する場合は、

1. 一度英語で作成してから
1. 日本語に修正すること

こちらをお勧めします。

|内容|列名|列の種類|Power Appsで対応する画面|今回取り扱うところ|
|---|---|---|---|---|
|社員番号|employeeNumber|1行テキスト|03.個人情報||
|氏名|fullName|タイトル列|01.採用者登録|〇|
|生年月日|birthDate|日付と時刻|01.採用者登録|〇|
|性別|gender|選択肢|01.採用者登録|〇|
|住所|address|1行テキスト|01.採用者登録|〇|
|電話番号|phoneNumber|1行テキスト|01.採用者登録|〇|
|メールアドレス|emailAddress|1行テキスト|01.採用者登録|〇|
|部署名|departmentName|1行テキスト|02.配属登録|
|役職|position|1行テキスト|02.配属登録|
|給与|salary|数値|02.配属登録|
|入社日|hireDate|日付と時刻|03.個人情報|
|退社日|terminationDate|日付と時刻|03.個人情報|
|健康保険番号|healthInsuranceNumber|1行テキスト|03.個人情報|
|年次有給休暇残日数|annualLeaveDaysLeft|数値|03.個人情報|

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/d86fabab-31f3-c61f-752b-25e3e4e9b5db.png)


# 2. Power Appsを作成
## 1. アプリの目的・流れ

今回は
**採用〜入社情報を登録するアプリ**
上記を作成します。

:::note warn
個人の趣味で作ったもので、実際に企業で運用しているものではありません。
試したい方はお声がけください。
※まだ使える水準にはなっていません。
:::

 - データソース
     - SharePoint Lists
 - 入力インターフェース
     - Power Apps `キャンバスアプリ`

今回は`採用担当者`が入力する部分のみ解説します。

## 2. アプリの外観を作る
::: note warn
こちらの解説は省きます
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/6bb21db8-0263-454f-a576-bcc751a82137.png)

## 3. 関数の実装
アプリで解決したい課題は
**「SharePoint Lists」にデータを一括登録したい**
ということです。

Power Appsの`編集フォーム`は非常に便利ですが、
 - 一つのレコードに対し、レコードの編集

という機能を提供しています。

今回は
 1. `垂直ギャラリー`に、入力フォームを作成し
 1. 登録情報を`Collection`に格納
 1. `Collection`に格納された情報を、SharePointに一括登録します。

`Collection`を活用することによって
 **直前のレコードの複製**を実現しています。

1. レコードを追加
1. 直前の情報をコピー
1. 一部分だけ修正

こういった処理は、Excelで快適にできていたポイントの一つです。
SaaSでも入っていたら嬉しくなります。

では内容を見ていきます。

### 1. App.OnStartでコレクションを設定
 - データ登録用のコレクション
 - ギャラリーに表示するラベルの情報を保持するコレクション

こういったものを宣言しています。

```plaintext:OnStart
// 増分更新用のインクリメント
Set(i,1);
// サイドバーのメニューの設定コレクション
ClearCollect(colMenu,
    Table(
        {index: 1,ico:Icon.AddUser,for:"Recruiter",name:"01.採用者登録"},
        {index: 2,ico:Icon.OfficeBuilding,for:"Manager",name:"02.配属登録"},
        {index: 3,ico:Icon.Support,for:"Payroll",name:"03.個人情報"}
    )
);
// 登録フォームの1番目のレコード
ClearCollect(colEmployee,
    Table(
        {index: i,
         employeeNumber: "",
         fullName: "",
         birthDate: "",
         gender: "",
         address: "",
         phoneNumber: "",
         emailAddress: "",
         departmentName: "",
         position: "",
         salary: "",
         hireDate: "",
         terminationDate: "",
         healthInsuranceNumber: "",
         annualLeaveDaysLeft: ""}
    )
);
// 登録フォームの定数になる数値をコレクションで宣言
ClearCollect(colConfig,
    Table(
        {
            _menu: 1,
            _items: 6,
            _itemsConfig: {
                _itemY1: 80,
                _itemY2: 130,
                _itemY3: 180,
                _itemY4: 230,
                _itemY5: 280,
                _itemY6: 330
            },        
            _labelsConfig: {
                _label1: "氏名",
                _label2: "生年月日",
                _label3: "性別",
                _label4: "電話番号",
                _label5: "メールアドレス",
                _label6: "住所"
            },
            _labelConfig: {
                x: 130,
                w: 150,
                h: 40
            },
            _inpsConfig: {
                x: 280,
                w: 320,
                h: 40
            }
        },
        {
            _menu: 2,
            _items: 3,
            _itemsConfig: {
                _itemY1: 80,
                _itemY2: 130,
                _itemY3: 180,
                _itemY4: 0,
                _itemY5: 0,
                _itemY6: 0
            },        
            _labelsConfig: {
                _label1: "部署",
                _label2: "役職",
                _label3: "給与",
                _label4: "",
                _label5: "",
                _label6: ""
            },
            _labelConfig: {
                x: 130,
                w: 150,
                h: 40
            },
            _inpsConfig: {
                x: 280,
                w: 360,
                h: 40
            }
        },
        {
            _menu: 3,
            _items: 4,
            _itemsConfig: {
                _itemY1: 80,
                _itemY2: 130,
                _itemY3: 180,
                _itemY4: 230,
                _itemY5: 0,
                _itemY6: 0
            },        
            _labelsConfig: {
                _label1: "社員番号",
                _label2: "入社日",
                _label3: "健康保険番号",
                _label4: "年次有給休暇",
                _label5: "",
                _label6: ""
            },
            _labelConfig: {
                x: 130,
                w: 150,
                h: 40
            },
            _inpsConfig: {
                x: 280,
                w: 360,
                h: 40
            }
        }
    )
);
```

:::note warn
ギャラリーに表示するラベルの情報を保持するコレクションは
作者の好みです。
:::

### 2. コレクションにレコードを登録

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/367ddcb9-c15e-cb14-fe7f-f0bc711d2ec9.png)

```plaintext:OnSelect
// トグルボタンの値に応じて文字列を取得
Switch(tglGender.Value,
    true,UpdateContext({_gender:tglGender.TrueText}),
    false,UpdateContext({_gender:tglGender.FalseText})
);
// 選択されているレコードの値を更新
UpdateIf(colEmployee,
    index = ThisItem.index,
    { fullName:inpName.Value,
      birthDate:dateBirthDay.SelectedDate,
      gender:_gender,
      phoneNumber:inpTelephone.Value,
      emailAddress:inpMail.Value,
      address:inpAddress.Value
    }
);
Notify("Update record " & ThisItem.index,NotificationType.Success);
```

https://learn.microsoft.com/ja-jp/power-platform/power-fx/reference/function-update-updateif#updateif-function

`OnStart`で、インデックスだけつけた空レコードを作成しているため、
更新処理である[Updateif関数](https://learn.microsoft.com/ja-jp/power-platform/power-fx/reference/function-update-updateif#updateif-function)を採用しています。

`Patch`関数でも実現できます。好みで選択してください。

Collectionに一度データを格納しています。
登録前に`確認`をしたりする利点があるからです。

各コントロールの`Default`(モダンコントロールではValue)に、
コレクションの値をセットすることで、登録した情報の参照ができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/0e004739-5cad-ce70-f92e-d29f37fe464b.png)

### 3. 登録画面の追加
登録画面を追加するために「+」ボタンを押します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/f9470315-3ccf-2ad9-0af8-311a315dece3.png)

```plaintext:OnSelect
Set(i,i + 1);
Collect(colEmployee,
    Table(
        { index: i,
          employeeNumber: "",
          fullName: "",
          birthDate: "",
          gender: "",
          address: "",
          phoneNumber: "",
          emailAddress: "",
          departmentName: "",
          position: "",
          salary: "",
          hireDate: "",
          terminationDate: "",
          healthInsuranceNumber: "",
          annualLeaveDaysLeft: ""
        }
    )
);
```

増分更新の変数である`i`を用いることで、登録フォームを増やします。
Excelに例えると`行の追加`です。

これにより、ギャラリーに登録フォームを増やすことができました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/fecf7cb0-a490-c73e-916c-9a10fe678e42.png)

### 4. 直前のレコードの複製
この画面の中の`Copy`のIconに下記の式を入力しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/bc4c3266-4319-6b9b-cf49-b86bf2511d7a.png)


```plaintext:OnSelect
UpdateContext({LastRecord:First(LastN(colEmployee,2))});
UpdateIf(colEmployee,
    index = ThisItem.index,
    { fullName:LastRecord.fullName,
      birthDate:LastRecord.birthDate,
      gender:LastRecord.gender,
      phoneNumber:LastRecord.phoneNumber,
      emailAddress:LastRecord.emailAddress,
      address:LastRecord.address
    }
);
Notify("Copy & Update this record from last record",NotificationType.Success);
```

直前のレコードのコピー機能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/2a57bab7-2422-15bc-8c5d-ac1963a47491.png)

`Visible`に式を埋め込むことにより、
コピー元がない最初のレコードで誤登録することを防ぎます。

```plaintext:Visible
!(ThisItem.index = First(colEmployee).index)
```

### 5. SharePointにアップロード

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/d8e30686-41e4-98a9-bc2f-d7943dc46a2b.png)

```plaintext:OnSelect
ForAll(colEmployee,
    Patch(recruit,
        { タイトル:ThisRecord.fullName,
          birthDate:DateValue(ThisRecord.birthDate),
          gender:{Value:ThisRecord.gender},
          phoneNumber:ThisRecord.phoneNumber,
          emailAddress:ThisRecord.emailAddress,
          address:ThisRecord.address}
    )
);
Notify("Save to SharePoint",NotificationType.Success);
```

https://learn.microsoft.com/ja-jp/power-apps/maker/canvas-apps/create-update-records-bulk

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/f1981f9d-1e03-de71-b00d-5d0e33263171.png)

:::note warn
`選択肢`は、Object型 {}
`日付と時刻`は、date型のデータに直すことがポイントです！
:::


## その他関数
### （必要な場合）選択されているレコードを削除

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2734332/2cf63035-a47d-6f55-babb-2a8358ce6f96.png)

```plaintext:OnSelect
RemoveIf(colEmployee,index = ThisItem.index);
Set(i,Last(colEmployee).index + 1);
```

https://learn.microsoft.com/ja-jp/power-platform/power-fx/reference/function-remove-removeif#removeif-function
# 最後に
Power Appsの関数は、Excelの関数に似ているものが多く、非常に使いやすいです。
しかしながらデータ型が、レコード・テーブルといった用語で表現されており、
式の`どの部分に`、`どのような引数を`設定することが正解なのか、
わかりづらい部分も最初はあります。

データ型を意識して作成を進めることにより、
- 効果的な機能
- 効率的な開発

こうしたことが実現できますので、どんどんトライしていきましょう！

**それではGoodなPower Lifeを！**