[power apps]テニスコート申請画面を作成する Part10 入力チェックの追加(レスポンシブ対応/全手順公開)

やること

  • ErrorContainerにエラーメッセージを表示させる。
  • ユーザーの入力情報が正しいかのチェック処理を追加する。
  • エラーがある場合、次のフォーム画面に遷移しないよう、制御を掛ける。

手順

1.エラー表示用変数の用意

ErrorContainerErrorMessageは今までテスト用の文字列を設定していた。
きちんと画面上のエラーを拾えるように変数に置き換える。

Text: glbErrorMessage

また、エラーが無い時はErrorContainer自体を非表示にしたいので以下のように修正する。

Visible: Not(IsBlank(glbErrorMessage))

2.入力チェック処理の自作関数を追加

本アプリでは再利用性を高めるため、次の3つのチェック処理を自作関数として定義する。

  1. 必須チェック(入力されていなければエラー)
  2. 電話番号フォーマットチェック(フォーマットに合わなければエラー)
  3. メールアドレスフォーマットチェック(フォーマットに合わなければエラー)

また、それらのエラーメッセージを結合するための関数も一つ用意する。

  1. 「+新しいコンポーネント」を選択
  2. コンポーネント名を「InputCheckUtilDef」に変更
  3. 「+新しいカスタムプロパティ」を選択
  4. CheckIsEmptyの2つの入力パラメータと関数を以下のように定義
  5. CheckIsEmptyプロパティを以下のように定義
    文字列が空白なら、「オブジェクト名は必須項目です。」の文字列を返却する。

    If (
      IsBlank(str)
      ,
      item_name & "は必須項目です"
      ,
      ""
    )
  6. 「+新しいカスタムプロパティ」を選択
  7. CheckIsPhoneNoの2つの入力パラメータと関数を以下のように定義
  8. CheckIsPhoneNoプロパティを以下のように定義
    If (
        Not(IsBlank(str))
        ,
        If (
            /** 
          0または1から始まる9桁または10桁の数字 または
          ハイフンで結合された2桁~4桁の数字
          であればフォーマットを満たす
            */
            IsMatch(str, "^(0{1}\d{9,10})$") Or IsMatch(str, "^(\d{2,4})-(\d{2,4})-(\d{2,4})$")
            ,
            ""
            ,
            item_name & "は不正なフォーマットです"
        )
    )
    
  9. 「+新しいカスタムプロパティ」を選択
  10. CheckIsEmailの2つの入力パラメータと関数を以下のように定義
  11. CheckIsEmailプロパティを以下のように定義
    If (
        Not(IsBlank(str))
        ,
        If (
         /** 
               Emailかどうかの判定は、Power Appsの標準関数に備わっているが、
          あまり精度は良くない。(とりあえず@がついていればOKとみなされる)
            */
            IsMatch(str, Match.Email)
            ,
            ""
            ,
            item_name & "は不正なフォーマットです"
        )
    )
    
  12. 「+新しいカスタムプロパティ」を選択
    エラーメッセージ同士を結合する関数を追加する。
  13. JoinErrorMessageの2つの入力パラメータと関数を以下のように定義
  14. JoinErrorMessageのプロパティを以下のように定義
    /** 
      新しいエラーメッセージがある場合、改行して結合する
      新しいエラーメッセージがない場合、元のメッセージをそのまま返却する
        Char(10)は改行文字を表す
    */
    If (
      IsBlank(new_error_message)
      ,
      error_message
      ,
      error_message & Char(10) & "・" & new_error_message
    )
  15. アプリ側で「+挿入」 > カスタム > InputCheckUtilDef_1を選択
  16. 追加されたコンポーネントの名前をInputCheckUtilに変更

 

3.「会場選択Form」の入力チェック

検索結果のレコードが見つからなかったときのエラーチェックを追加する。

/** エラーメッセージを初期化 */
Set (glbErrorMessage, "");

/** マスタ検索 */
Set (temp会場検索結果レコード, LookUp(会場マスタ, ID = val検索条件会場ID.Text));

/** レコードが見つからなかったとき*/
If (
  IsBlank(temp会場検索結果レコード)
  /** エラーメッセージを設定する */
  , Set(glbErrorMessage, "会場ID「" & val検索条件会場ID.Text & " 」は見つかりませんでした。");
);

 

続いて、検索結果が見つからないまま次へボタンをクリックしたときのエラーチェックを追加する。
エラーチェックはステップIDが会場選択フォームの時だけ実行されるようにする。

/** エラーメッセージを追加 */
Set (glbErrorMessage, "");

/**
 * 会場選択フォーム:入力チェック
 */
If (
    glbCurrentStepId = glb会場選択_STEP_ID
    ,
    If (
        IsBlank(val会場ID.Text)
        ,
        Set (glbErrorMessage, "会場が未選択です。会場を選択してください。");
    );
);

 

4.「日程選択Form」の入力チェック

日程選択Formで行う入力チェックは以下の2点

  1. スケジュール候補日1が未選択の場合、エラー
  2. 選択する日付が重複している場合、エラー

InputCheckUtils.JoinErrorMessageの関数を定義したことで、いちいちエラーがあったらメッセージを結合するの条件を記述しなくてよくなり、コード量を削減することができた。
また、以下のコードでそんなに難しいことはしていない。

/**
 * 日程選択フォーム:入力チェック
 */
If (
    glbCurrentStepId = glb日程選択_STEP_ID
    ,
    If (
        IsBlank(valスケジュール候補日1.Selected.日付)
        ,
        Set (glbErrorMessage, "スケジュール候補日が選択されていません。最低1つ選択してください。");
    );
    /** スケジュール日の重複チェック */
    If (
        And(Not(IsBlank(glbSelectedスケジュール候補日1)), Not(IsBlank(glbSelectedスケジュール候補日1)))
        ,
        If (
            glbSelectedスケジュール候補日1.連番 = glbSelectedスケジュール候補日2.連番
            ,
            Set (glbErrorMessage, InputCheckUtils.JoinErrorMessage(glbErrorMessage, "候補日1と候補日2は別の日を選択してください。"));
        );
    );
    If (
        And(Not(IsBlank(glbSelectedスケジュール候補日1)), Not(IsBlank(glbSelectedスケジュール候補日3)))
        ,
        If (
            glbSelectedスケジュール候補日1.連番 = glbSelectedスケジュール候補日3.連番
            ,
            Set (glbErrorMessage, InputCheckUtils.JoinErrorMessage(glbErrorMessage, "候補日1と候補日3は別の日を選択してください。"));
        );
    );
    If (
        And(Not(IsBlank(glbSelectedスケジュール候補日2)), Not(IsBlank(glbSelectedスケジュール候補日3)))
        ,
        If (
            glbSelectedスケジュール候補日2.連番 = glbSelectedスケジュール候補日3.連番
            ,
            Set (glbErrorMessage, InputCheckUtils.JoinErrorMessage(glbErrorMessage, "候補日2と候補日3は別の日を選択してください。"));
        );
    );
);

 

5.「備品選択Form」の入力チェック

備品選択Formではチェックする処理がないので飛ばす。

 

6.「代表者入力Form」の入力チェック

代表者入力Formで行う入力チェックは以下の3点であり、全て自作関数に定義してるため、後は呼び出すだけである。

  1. 全項目に対して未入力の場合、エラー
  2. 電話番号が正しいフォーマットで入力されていない場合、エラー
  3. メールアドレスが正しいフォーマットで入力されていない場合、エラー

/**
 * 代表者入力フォーム:入力チェック
 */
If (
    glbCurrentStepId = glb代表者入力_STEP_ID
    ,
    /** 必須チェック */
    Set (glbErrorMessage, 関数Util.JoinErrorMessage(glbErrorMessage, 関数Util.CheckIsEmpty(val代表者名_姓.Text, "代表者名(姓)")));
    Set (glbErrorMessage, 関数Util.JoinErrorMessage(glbErrorMessage, 関数Util.CheckIsEmpty(val代表者名_名.Text, "代表者名(名)")));
    Set (glbErrorMessage, 関数Util.JoinErrorMessage(glbErrorMessage, 関数Util.CheckIsEmpty(val代表者名_せい.Text, "代表者名(せい)")));
    Set (glbErrorMessage, 関数Util.JoinErrorMessage(glbErrorMessage, 関数Util.CheckIsEmpty(val代表者名_めい.Text, "代表者名(めい)")));
    Set (glbErrorMessage, 関数Util.JoinErrorMessage(glbErrorMessage, 関数Util.CheckIsEmpty(val代表者電話タイプ.SelectedText.Value, "代表者電話タイプ")));
    Set (glbErrorMessage, 関数Util.JoinErrorMessage(glbErrorMessage, 関数Util.CheckIsEmpty(val代表者メールアドレス.Text, "代表者メールアドレス")));
    Set (glbErrorMessage, 関数Util.JoinErrorMessage(glbErrorMessage, 関数Util.CheckIsEmpty(val代表者電話番号.Text, "代表者電話番号")));
   
    /** フォーマットチェック */
    Set (glbErrorMessage, 関数Util.JoinErrorMessage(glbErrorMessage, 関数Util.CheckIsPhoneNo(val代表者電話番号.Text, "代表者電話番号")));
    Set (glbErrorMessage, 関数Util.JoinErrorMessage(glbErrorMessage, 関数Util.CheckIsEmail(val代表者メールアドレス.Text, "代表者メールアドレス")));
);

 

加えて、もう一点入力チェックが必要となる。
それが、スケジュールマスタの空き数の確認である。
日程選択フォームでスケジュール候補日1~3のプルダウンから日付を選択してから、「登録する」のボタンをクリックするまでの間に、空き数が「0」になってしまっている可能性もある。
その場合は、空きがなくなってしまった旨のメッセージを表示させる必要がある。
空きの有無についてはプルダウンで選択された日付で、マスタ検索を行い空き数が「0」かどうかで判定する。

以下に入力チェックを追加しており、行数は多いが同じ処理を3回記述しているだけなので、「glbSelectedスケジュール候補日1」の部分だけ理解できればよい。

/**
 * 代表者入力フォーム:入力チェック
 */
If (
    glbCurrentStepId = glb代表者入力_STEP_ID
  ,
    If (
        Not(IsBlank(glbSelectedスケジュール候補日1))
        , 
        /** スケジュールマスタを検索し、空き数が「0」であれば */
        If (
            Int(LookUp(スケジュールマスタ,  Int(連番) = Int(glbSelectedスケジュール候補日1.連番)).空き数) = 0
            ,
            /** エラーメッセージを表示する */
            Set(glbErrorMessage, InputCheckUtils.JoinErrorMessage(glbErrorMessage, "スケジュール候補日1は別のユーザーの申請により空きがなくなりました。日にちを変更してください"));
            Set(glbSelectedスケジュール候補日1, Blank());
        );
    );
    If (
        Not(IsBlank(glbSelectedスケジュール候補日2))
        , 
        If (
            Int(LookUp(スケジュールマスタ,  Int(連番) = Int(glbSelectedスケジュール候補日2.連番)).空き数) = 0
            ,
            Set(glbErrorMessage, InputCheckUtils.JoinErrorMessage(glbErrorMessage, "スケジュール候補日2は別のユーザーの申請により空きがなくなりました。日にちを変更してください"));
            Set(glbSelectedスケジュール候補日2, Blank());
        );
    );
    If (
        Not(IsBlank(glbSelectedスケジュール候補日3))
        , 
        If (
            Int(LookUp(スケジュールマスタ,  Int(連番) = Int(glbSelectedスケジュール候補日3.連番)).空き数) = 0
            ,
            Set(glbErrorMessage, InputCheckUtils.JoinErrorMessage(glbErrorMessage, "スケジュール候補日3は別のユーザーの申請により空きがなくなりました。日にちを変更してください"));
            Set(glbSelectedスケジュール候補日3, Blank());
        );
    );
);

7.画面遷移の制御

最後に画面遷移の制御をする。
このままではエラーの有無によらず次の画面に進んでしまうので、エラー時に処理が走らないよう、登録処理をIf文で囲う。

If (
  glbErrorMessage = ""
  ,
  / ** 既存の登録処理(略) */
);

8.次へボタンの振り返り

最終的な「次へ」ボタンのOnSelectのソースコードは以下の通り。
分量はあるが、全て解説済みである。

/** エラーメッセージの初期化 */
Set (glbErrorMessage, "");

/**
 * 会場選択フォーム:入力チェック
 */
If (
    glbCurrentStepId = glb会場選択_STEP_ID
    ,
    If (
        IsBlank(val会場ID.Text)
        ,
        Set (glbErrorMessage, "会場が未選択です。会場を選択してください。");
    );
);

/**
 * 日程選択フォーム:入力チェック
 */
If (
    glbCurrentStepId = glb日程選択_STEP_ID
    ,
    If (
        IsBlank(valスケジュール候補日1.Selected.日付)
        ,
        Set (glbErrorMessage, "スケジュール候補日が選択されていません。最低1つ選択してください。");
    );
    // スケジュール日の重複チェック
    If (
        And(Not(IsBlank(glbSelectedスケジュール候補日1)), Not(IsBlank(glbSelectedスケジュール候補日1)))
        ,
        If (
            glbSelectedスケジュール候補日1.連番 = glbSelectedスケジュール候補日2.連番
            ,
            Set (glbErrorMessage, InputCheckUtils.JoinErrorMessage(glbErrorMessage, "候補日1と候補日2は別の日を選択してください。"));
        );
    );
    If (
        And(Not(IsBlank(glbSelectedスケジュール候補日1)), Not(IsBlank(glbSelectedスケジュール候補日3)))
        ,
        If (
            glbSelectedスケジュール候補日1.連番 = glbSelectedスケジュール候補日3.連番
            ,
            Set (glbErrorMessage, InputCheckUtils.JoinErrorMessage(glbErrorMessage, "候補日1と候補日3は別の日を選択してください。"));
        );
    );
    If (
        And(Not(IsBlank(glbSelectedスケジュール候補日2)), Not(IsBlank(glbSelectedスケジュール候補日3)))
        ,
        If (
            glbSelectedスケジュール候補日2.連番 = glbSelectedスケジュール候補日3.連番
            ,
            Set (glbErrorMessage, InputCheckUtils.JoinErrorMessage(glbErrorMessage, "候補日2と候補日3は別の日を選択してください。"));
        );
    );
);

/**
 * 代表者入力フォーム:入力チェック
 */
If (
    glbCurrentStepId = glb代表者入力_STEP_ID
    ,
    /** 必須チェック */
    Set (glbErrorMessage, InputCheckUtils.JoinErrorMessage(glbErrorMessage, InputCheckUtils.CheckIsEmpty(val代表者名_姓.Text, "代表者名(姓)")));
    Set (glbErrorMessage, InputCheckUtils.JoinErrorMessage(glbErrorMessage, InputCheckUtils.CheckIsEmpty(val代表者名_名.Text, "代表者名(名)")));
    Set (glbErrorMessage, InputCheckUtils.JoinErrorMessage(glbErrorMessage, InputCheckUtils.CheckIsEmpty(val代表者名_せい.Text, "代表者名(せい)")));
    Set (glbErrorMessage, InputCheckUtils.JoinErrorMessage(glbErrorMessage, InputCheckUtils.CheckIsEmpty(val代表者名_めい.Text, "代表者名(めい)")));
    Set (glbErrorMessage, InputCheckUtils.JoinErrorMessage(glbErrorMessage, InputCheckUtils.CheckIsEmpty(val代表者電話タイプ.SelectedText.Value, "代表者電話タイプ")));
    Set (glbErrorMessage, InputCheckUtils.JoinErrorMessage(glbErrorMessage, InputCheckUtils.CheckIsEmpty(val代表者メールアドレス.Text, "代表者メールアドレス")));
    Set (glbErrorMessage, InputCheckUtils.JoinErrorMessage(glbErrorMessage, InputCheckUtils.CheckIsEmpty(val代表者電話番号.Text, "代表者電話番号")));
   
    /** フォーマットチェック */
    Set (glbErrorMessage, InputCheckUtils.JoinErrorMessage(glbErrorMessage, InputCheckUtils.CheckIsPhoneNo(val代表者電話番号.Text, "代表者電話番号")));
    Set (glbErrorMessage, InputCheckUtils.JoinErrorMessage(glbErrorMessage, InputCheckUtils.CheckIsEmail(val代表者メールアドレス.Text, "代表者メールアドレス")));

    If (
        Not(IsBlank(glbSelectedスケジュール候補日1))
        , 
        If (
            Int(LookUp(スケジュールマスタ,  Int(連番) = Int(glbSelectedスケジュール候補日1.連番)).空き数) = 0
            ,
            Set(glbErrorMessage, InputCheckUtils.JoinErrorMessage(glbErrorMessage, "スケジュール候補日1は別のユーザーの申請により空きがなくなりました。日にちを変更してください"));
            Set(glbSelectedスケジュール候補日1, Blank());
        );
    );
    If (
        Not(IsBlank(glbSelectedスケジュール候補日2))
        , 
        If (
            Int(LookUp(スケジュールマスタ,  Int(連番) = Int(glbSelectedスケジュール候補日2.連番)).空き数) = 0
            ,
            Set(glbErrorMessage, InputCheckUtils.JoinErrorMessage(glbErrorMessage, "スケジュール候補日2は別のユーザーの申請により空きがなくなりました。日にちを変更してください"));
            Set(glbSelectedスケジュール候補日2, Blank());
        );
    );
    If (
        Not(IsBlank(glbSelectedスケジュール候補日3))
        , 
        If (
            Int(LookUp(スケジュールマスタ,  Int(連番) = Int(glbSelectedスケジュール候補日3.連番)).空き数) = 0
            ,
            Set(glbErrorMessage, InputCheckUtils.JoinErrorMessage(glbErrorMessage, "スケジュール候補日3は別のユーザーの申請により空きがなくなりました。日にちを変更してください"));
            Set(glbSelectedスケジュール候補日3, Blank());
        );
    );
);

/**
 * 入力チェック後、画面遷移 or 登録処理
 */
If (
    glbErrorMessage = ""
    ,
    If (
        glbCurrentStepId <> glb代表者入力_STEP_ID
        ,
        /** 代表者入力画面でない場合 */
        Set (glbCurrentStepId, glbCurrentStepId + 1)
        ,
        Patch(
            テニス会場レンタル申請データ
            , Defaults(テニス会場レンタル申請データ)
            , 会場選択Form.Updates
            , 日程選択Form.Updates
            , 備品選択Form.Updates
            , 代表者入力Form.Updates
        );
        UpdateIf(
            スケジュールマスタ
            , Int(連番) = Int(glbSelectedスケジュール候補日1.連番)
                Or Int(連番) = Int(glbSelectedスケジュール候補日2.連番) 
                Or Int(連番) = Int(glbSelectedスケジュール候補日3.連番)
            , {空き数: 空き数 - 1}
        );
            
        /** 初期化 */
        ResetForm(日程選択Form);
        ResetForm(備品選択Form);
        ResetForm(代表者入力Form);
        ResetForm(会場選択Form);
        Set(cond会場ID, Blank());
        Set(temp会場検索結果レコード, Blank()); 
        Set(glbSelectedスケジュール候補日1, Blank());
        Set(glbSelectedスケジュール候補日2, Blank());
        Set(glbSelectedスケジュール候補日3, Blank());
        Set(glbCurrentStepId, glb会場選択_STEP_ID);
        Notify("完了");
    );
);

 

9.動作確認

会場選択フォーム

会場マスタにない会場IDを入力して検索ボタンをクリック。

 

会場を選択しないまま、次へボタンをクリック。

 

日程選択フォーム

スケジュール候補日1を選択せずに、次へボタンをクリック

 

スケジュール候補日1~3を重複して選択し、次へボタンをクリック。

代表者入力フォーム

未入力状態で登録するボタンをクリック。

 

不正なフォーマットのメールアドレス電話番号を入力し登録するボタンをクリック。

 

SharePointからスケジュールマスタのエクセルファイルを開き、手動で空き数を「0」に設定してから、登録するボタンをクリック。

 

記事一覧