web-dev-qa-db-ja.com

送信ボタンをレンダリングして、最初は無効にしてJavaScriptで有効にする方法を教えてください。

React JSフロントエンドコンポーネントを含むフォームがあります。コンポーネントは必要な情報を収集します。Reactコンポーネントは、非表示のフォームフィールド。

ユーザーがコンポーネントを介して必要なデータの入力をバイパスできないようにするには、次の要件を満たす必要があります。

  • Reactコンポーネントが読み込まれる前に、[送信]ボタンを無効にする必要があります。これにより、ユーザーがJSを無効にするか、ページの読み込みが完了する前にボタンをクリックするだけでフォームを送信できないようになります。
  • Reactコンポーネントが読み込まれた後、必要な値がクリアされるたびに、[送信]ボタンを無効にする必要があります。
  • 必要な値を入力したら、[送信]ボタンを有効にする必要があります。
  • 非表示の値が空の場合、フォームを送信できないようにする必要があります。

私はこのようにフォームをレンダリングしようとしました:

_  public function buildForm(array $form, FormStateInterface $form_state) {
    $form = [];

    $form['selected_values'] = [
      '#type' => 'hidden',
    ];

    $form['actions'] = [
      '#type'   => 'actions',
      '#weight' => 100,
    ];

    $form['actions']['submit'] = [
      '#type'         => 'submit',
      '#value'        => $this->t('Submit'),
      '#button_type'  => 'primary',
      '#disabled'     => TRUE, // Controlled by JS on the frontend
    ];

    return $form;
  }
_

そしてJavaScriptでは、次のようなonValueChange()イベントコールバックがあります。

_function onValueChange(value) {
  if (value.trim() === '') {
    $(submitButton).prop('disabled', true);
  }
  else {
    $(submitButton)
      .prop('disabled', false)
      .removeClass('is-disabled');
  }
}
_

私も試しました:

_  public function buildForm(array $form, FormStateInterface $form_state) {
    $form = [];

    $selected_values = form_state->getValue('selected_values');

    $form['selected_values'] = [
      '#type' => 'hidden',
    ];

    $form['actions'] = [
      '#type'   => 'actions',
      '#weight' => 100,
    ];

    $form['actions']['submit'] = [
      '#type'         => 'submit',
      '#value'        => $this->t('Submit'),
      '#button_type'  => 'primary',
      '#disabled'     => !empty($selected_values),
    ];

    return $form;
  }

_

これらのアプローチはどちらも同じ問題を示しています。「送信」をクリックしてもフォームは送信されません。フォームを再構築するようです。 2番目の方法では、フォームのレンダリング中、選択された入力値は常に空のように見えます。 afterフォームが構築されるまで、フォームの状態に適用されません。

必要な値を要求しながら、「送信」ボタンが無効になっていることを確認するにはどうすればよいですか?

2
GuyPaddock

DrupalフォームAPIにボタンが入力を期待していないことを伝えるため、送信ボタンで#disabledを使用することは間違った解決策です。つまり、Drupalボタンがまったく存在しないかのように動作します。したがって、送信ボタンをクリックしても効果はありません。通常、巧妙なユーザーが単にバックエンドロジックを無効にするだけなので、セキュリティの観点からこれはすべて意味があります。開発者ツールを使用して、無効または読み取り専用のフォーム要素を強制的に有効にします。

代わりに、解決策は2つあります。

  1. HTML属性を介してボタンを無効としてレンダリングします。これにより、ボタンはブラウザーでのみ無効になりますが、バックエンドではアクティブなボタンと見なされます。
  2. 非表示フィールドを'#required' => TRUEとしてマークすると、ユーザーがDev Toolsに細心の注意を払っていてもフォームをバイパスすることができなくなります。

実際には次のようになります。

  public function buildForm(array $form, FormStateInterface $form_state) {
    $form = [];

    $form['selected_values'] = [
      // Though this field is hidden, it's required. So, the title is still
      // shown if/when the field is ever NULL during submission.
      '#title'    => $this->t('File selection'),
      '#type'     => 'hidden',
      '#required' => TRUE,
    ];

    $form['actions'] = [
      '#type'   => 'actions',
      '#weight' => 100,
    ];

    $form['actions']['submit'] = [
      '#type'         => 'submit',
      '#value'        => $this->t('Submit'),
      '#button_type'  => 'primary',
      '#attributes' => [
        // This button is programmatically enabled/disabled via JS on the
        // frontend.
        'disabled' => 'disabled',
    ];

    return $form;
  }

ボタンを有効にするためのフロントエンドロジックは変わりません。ここでの違いは、ボタンはバックエンドフォームで無効になるのではなく、レンダリングされたHTMLでのみ無効になることです。さらに、狡猾なユーザーがHTML DOM要素のdisabled属性を削除してボタンを有効にすると、次のような検証エラーが発生します。

ファイル選択フィールドは必須です。

それとは別に、2番目のアプローチが入力値に基づいて「送信」ボタンを有効/無効にできない理由は、Drupalが入力値を処理する前に最初にフォームを作成して、入力を処理するためにどのフォーム要素を呼び出す必要があるか。したがって、フォームビルダーが呼び出された時点では、フォーム状態値にはまだ何もありません。仮説的には、$form_state->getUserInput()を介して生の入力を取得できる可能性があります次に、それに基づいてボタンを有効にするかどうかを制御しますが、このアプローチはセキュリティに影響を与える可能性があります。

2
GuyPaddock