4

ウェブサイトですべてを翻訳可能にするために(検証のエラーメッセージを含む)、ほとんどすべてのフォームをリモートフォームに切り替えました。これはエラーメッセージを翻訳する機能に役立ちますが、次のような他の問題が発生しました。

  • ユーザーが送信ボタンを複数回クリックすると、アクションが複数回呼び出されます。データベースに新しいレコードを作成するためのリモートフォームがあり、ユーザーのデータが有効であると仮定すると、クリックするたびに新しいオブジェクトが追加されます(まったく同じ内容)。そのようなことが起こらないようにする方法はありますか?

リモートフォームのベストプラクティスについて読むことができる場所はありますか?マルチクリックの問題をどのように処理できますか?すべてのフォームをリモートフォームに切り替えることは非常に大きな間違いですか?

4

8 に答える 8

3

と呼ばれるrails3オプションがあります:disable_with。これを入力要素に配置して、リモートフォームの送信中に無効にし、ラベルを付け直します。これらの入力にdata-disable-withタグを追加し、rails.jsがこの機能を選択してバインドできます。

submit_tag "Complete sale", :disable_with => "Please wait..."

詳細については、こちらをご覧ください

于 2012-06-14T23:34:42.883 に答える
2

簡単で、好みに応じてさまざまな方法でそれを実現できます。

  1. ajaxリクエストを使用してフォームを手動で投稿し、応答を待つ間、フォームを無効化/非表示(または必要なもの)して、ユーザーが狂ったように投稿を続けられないようにします。サーバーから応答を受け取ったら、ユーザーが再度投稿できるようにしたり(最初にフォームをクリーンアップしたり)、他の何かを表示したり、別のページにリダイレクトしたり、必要なものにリダイレクトしたりできます。

  2. link_to:remote => trueを使用してフォームを送信し、応答を処理するためのコールバック関数を追加し、送信時にフォームを無効/非表示(または必要なもの)にします。

  3. フォームにjsリスナーを追加して、フォームが送信されたことを検出し、フォームを無効化/非表示/その他何でも

ご覧のとおり、必要なものを実現するにはさまざまな方法があります。

編集:ここでjsからのフォーム送信のバインドまたは処理に関する情報が必要な場合は、私が提案したことを実行するのに役立つ可能性のある非常に簡単で興味深い例を見つけることができます!jQuery送信

于 2012-08-21T11:03:57.567 に答える
2

私は自分自身でリモートフォームを広範囲に持っており、ほとんどの場合、それらを避けます。ただし、レイアウトやUXで、ページ全体を再読み込みしたり更新したりせずに、オンザフライのドロップダウンフォームが必要になる場合があります。

それで、これに段階的に取り組みましょう。

1.正規形の二重投稿の防止

通常のフォームでも、クリックが登録されてアクションが開始されたことが明確に示されない場合、ユーザーはボタンをダブルクリックするか、複数回クリックする可能性があります。

これを表示する方法はたくさんありますが(javascriptなど)、Railsで最も簡単な方法は次のとおりです。

= f.button  :submit, :disable_with => "Please wait..." 

これにより、最初のクリック後にボタンが無効になり、クリックが登録されてアクションが開始されたことを明確に示します。

2.リモートフォームの処理

リモートフォームの場合、それほど違いはありませんが、違いはおそらく次のとおりです。後で何が起こるか。

リモートフォームには、いくつかのオプションがあります。

  1. エラーの場合:フォームをエラーで更新します。
  2. フォームを開いたままにして、ユーザーがデータを入力し続けることができるようにします(これはあなたの場合だと思いますか?)
  3. ユーザーをある場所にリダイレクトします。

それらのケースを処理させてください。これらの3つのケースは、通常のフォームを実行する場合は完全に標準であることを理解してください。ただし、リモート呼び出しを行う場合はそうではありません。

2.1エラーの場合

リモートフォームを正しく更新するには、もう少し魔法をかける必要があります。それほど多くはありませんが、少しです。

hamlを使用すると、次のedit.js.hamlようなビューが表示されます。

:plain
  $('#your-form-id').replaceWith('#{j render(:partial => '_form') }');

これが行うこと:完全なhamlをフォームのみに置き換えます。これを機能させるには、それに応じてビューを構成する必要があります。それは難しいことではありませんが、必要なだけです。

2.2フォームのクリア

2つのオプションがあります。*エラーの場合と同様に、フォームを完全に再レンダリングします。投稿したばかりの要素ではなく、新しい要素からフォームをレンダリングするようにしてください。*代わりに次のJavaScriptを送信してください。

$('#your-form-id').reset();

これによりフォームが空白になり、通常、その後のクリックが効果的に役に立たなくなります(一部のクライアント検証では、一部のフィールドに入力するまで投稿がブロックされる可能性があります)。

2.3リダイレクト

リモートフォームを使用しているため、リダイレクトすることはできません。これはクライアント側で行われる必要があるため、少し複雑になります。

もう一度hamlを使用すると、これは次のようになります

:plain
  document.location.href = '#{@redirect_uri}';

結論

リモートフォームを使用して2回(3回、4回、その他)の投稿を防ぐには、次のことを行う必要があります。

  • 最初のクリック後にボタンを無効にします(使用:disable_with
  • 送信に成功したらフォームをクリアします(フォームをリセットするか、新しい要素でレンダリングします)

お役に立てれば。

于 2012-08-23T23:39:10.573 に答える
1

最も簡単な解決策は、フォームごとにトークンを生成することです。次に、createアクションによって、まだ使用されていないことを確認し、レコードを作成する必要があるかどうかを判断できます。

これが私がこの機能を書く方法です。私は実際にこれをテストしていませんが、概念は機能するはずです。

1.newアクション内で、フォームリクエストを識別するためのハッシュを作成します。

def new
  @product = Product.new
  @form_token = session["form_token"] = SecureRandom.hex(15)
end

2.フォームトークンを格納するフォームに非表示フィールドを追加します。createこれは、フォームが以前に送信されていないことを確認するためのアクションでキャプチャされます。

<%= hidden_field_tag :form_token, @form_token %>

3.createアクションでは、フォームトークンがsessionparams変数の間で一致することを確認できます。これにより、これが最初の提出か2番目の提出かを確認する機会が与えられます。

def create
  # delete the form token if it matches
  if session[:form_token] == params[:form_token]
    session[:form_token] = nil
  else
    # if it doesn't match then check if a record was created recently
    product = Product.where('created_at > ?', 3.minutes.ago).where(title: params[:product][:title]).last

    # if the product exists then show it
    # or just return because it is a remote form
    redirect_to product and return if product.present?
  end

  # normal create action here ...
end

更新:私が上で説明したものには名前があり、それはシンクロナイザー(またはデジャヴ)トークンと呼ばれます。この記事で説明されているように、は二重送信を防ぐための適切な方法です。

この戦略は、フォーム送信の重複の問題に対処します。シンクロナイザートークンはユーザーのセッションで設定され、クライアントに返される各フォームに含まれます。そのフォームが送信されると、フォーム内のシンクロナイザートークンがセッション内のシンクロナイザートークンと比較されます。トークンは、フォームが最初に送信されたときに一致する必要があります。トークンが一致しない場合、フォームの送信が許可されず、エラーがユーザーに返される可能性があります。ユーザーがフォームを送信し、ブラウザの[戻る]ボタンをクリックして同じフォームを再送信しようとすると、トークンの不一致が発生する可能性があります。

一方、2つのトークン値が一致する場合、制御の流れは正確に期待どおりであると確信しています。この時点で、セッションのトークン値が新しい値に変更され、フォームの送信が受け入れられます。

于 2012-08-26T03:21:35.497 に答える
1

私はそれを言うのは嫌ですが、あなたは病気よりも悪い治療法を思いついたようです。

翻訳にi18nを使用してみませんか?それは確かに「レールウェイ」になるでしょう...

このルートを続行する必要がある場合は、Javascriptの使用を開始する必要があります。リモートフォームは通常、投票やコメントなどの小さな「AJAXy」用です。ページを離れずにオブジェクト全体を作成すると、多くのオブジェクトを連続して作成したい場合(解決しようとしている正確な問題)に役立ちます。

AJAXの使用を開始するとすぐに、JSを実行する必要があるという事実に対処する必要があります。これはクライアント側のものであり、したがってRailの専門ではありません。

後戻りできないほどこの道を進んだと感じた場合は、AJAX応答で少なくともフォームをリセットすることをお勧めします。これにより、人々が誤って同じものを複数回作成するのを防ぐことができます。

UI / UXの観点からは、オブジェクトが正常に作成されたことをユーザーに通知するフラッシュメッセージも表示される必要があります。

要約すると、時間に余裕がある場合はgitリセットしてi18nの使用を開始し、できない場合はajaxコールバックでフォームをリセットしてフラッシュメッセージを設定します。

編集:AJAXにページをリダイレクトさせることさえできると思いました(ただし、フラッシュメッセージを自分で処理する必要があります)。ただし、JavaScriptを介してリダイレクトするリモートフォームを使用するのは簡単ではありません...

于 2012-08-21T17:18:00.763 に答える
1

あなたはajaxリクエストのためにそのようなことを試すことができます。

ajaxリクエストのブロック変数をtrueに設定します

  before_filter :xhr_blocker

  def xhr_blocker
    if request.xhr? 
      if session[:xhr_blocker]
        respond_to do |format|
          format.json, status: :unprocessable_entity
        end
      else
        session[:xhr_blocker] = true 
      end      
    end
  end

アフターフィルターメソッドを使用してxhr_blocker変数をクリアします

  after_filter :clear_xhr_blocker

  def clear_xhr_blocker
    session[:xhr_blocker] = nil
  end
于 2012-08-27T11:46:20.003 に答える
1

マウスオーバーでポップアップを使用し、いくつかのリクエストをキューに入れたくないという同様の問題がありました。より詳細に制御するには、(私が行ったように)UJSの代わりにjavascript/coffeescriptを直接使用する方が簡単な場合があります。

私が解決した方法は、Ajax呼び出しを変数に割り当て、変数が割り当てられているかどうかを確認することでした。私の状況では、ajax呼び出しを中止しますが、ajax呼び出しが正常に完了したら、関数から戻って変数をnullに設定することをお勧めします。

このコーヒースクリプトの例は、「GET」を使用する私のポップアップからのものですが、理論的には「POST」または「PUT」でも同じである必要があります。

例えば

jQuery ->
  ajaxCall = null

  $("#popupContent").html "&nbsp;"

  $("#popup").live "mouseover", ->
    if ajaxCall
      return

    ajaxCall = $.ajax(
      type: "GET"
      url: "/whatever_url"
      beforeSend: ->
        $("#popupContent").prepend "<p class=\"loading-text\">Loading..please wait...</p>"

      success: (data) ->
        $("#popupContent").empty().append(data)

      complete: ->
        $"(.loading-text").remove()
        ajaxCall = null
    )

簡潔にするために、マウスアウトとタイマー処理を省略しました。

于 2012-08-24T04:35:19.883 に答える
0

ajax:complete(またはajax:successとajax:error)にバインドして、DOMをリダイレクトまたは更新し、リクエストが完了したときに必要に応じてフォームを削除/変更します。

于 2012-08-21T21:55:55.103 に答える