Rails 3: จะ“ redirect_to” ใน Ajax call ได้อย่างไร?


85

attempt_loginวิธีการต่อไปนี้เรียกว่าโดยใช้ Ajax หลังจากส่งแบบฟอร์มการเข้าสู่ระบบ

class AccessController < ApplicationController
  [...]
  def attempt_login
    authorized_user = User.authenticate(params[:username], params[:password])

    if authorized_user
      session[:user_id] = authorized_user.id
      session[:username] = authorized_user.username
      flash[:notice] = "Hello #{authorized_user.name}."
      redirect_to(:controller => 'jobs', :action => 'index')
    else
      [...]
    end
  end
end

ปัญหาคือredirect_toไม่ได้ผล

คุณจะแก้ปัญหานี้อย่างไร?

คำตอบ:


102

สุดท้ายก็เปลี่ยนใหม่

redirect_to(:controller => 'jobs', :action => 'index')

ด้วยสิ่งนี้:

render :js => "window.location = '/jobs/index'"

และใช้งานได้ดี!


43
แนวทางที่ดีกว่าคือrender :js => "window.location = '#{jobs_path}'"
zakelfassi

3
มันใช้งานได้ แต่จะดีกว่าไหมหากส่งผ่านตำแหน่งการเปลี่ยนเส้นทางกลับด้วยข้อความแสดงความสำเร็จ json จริงและทำการเปลี่ยนเส้นทางที่ส่วนหน้า
justinxreese

1
jobs_pathโดยพื้นฐานแล้วไม่เข้มงวดเท่า URL ใช่หรือไม่ หาก URL เปลี่ยนชื่อเส้นทางก็เช่นกันเว้นแต่คุณจะระมัดระวังเป็นพิเศษ อีกทางเลือกหนึ่งคือrender js: "window.location = '#{polymorphic_path(@job.class)}'"และใช้เส้นทางทรัพยากรที่คำนวณตามรูปแบบงาน วิธีนี้ใช้ได้เฉพาะในกรณีที่เส้นทางของคุณมีทรัพยากรและใช้รูปแบบการตั้งชื่อมาตรฐานที่สอดคล้องกับโมเดลของคุณ (หรือหากคุณระบุ model_name ในโมเดลของคุณเพื่อให้สร้างชื่อเส้นทางที่ถูกต้อง)
เลอะเทอะ

2
น่ากลัว ใครมีความคิดว่าทำไม redirect_to แบบธรรมดาใช้ไม่ได้
Tasos Anesiadis

1
@Tasos Anesiadis, redirect_to ไม่ทำงานเมื่อฟอร์มเป็นแบบฟอร์ม Rails 'ระยะไกล' เนื่องจากเบราว์เซอร์ได้รับแจ้งให้ตีความการตอบสนองจากคอนโทรลเลอร์เป็น Javascript คุณสามารถดูหน้า redirect_to ได้ในแท็บการตอบกลับ (ผ่านแผงเครือข่าย) ของ Chrome DevTools แต่สิ่งที่จำเป็นคือคำสั่งไปยังเบราว์เซอร์จากคอนโทรลเลอร์เพื่อไปหาหน้าอื่น จำเป็นต้องมีโซลูชัน window.location ที่ให้ไว้ที่นี่หรือเปลี่ยนฟอร์มเป็นแบบฟอร์ม 'local' ปกติเว้นแต่คุณต้องการส่งและประมวลผลข้อมูลฟอร์มด้วยตนเองผ่าน fetch () และ JSON
MSC

68

มีวิธีที่ง่ายมากในการเก็บแฟลชสำหรับการร้องขอครั้งต่อไป ในคอนโทรลเลอร์ของคุณให้ทำสิ่งที่ชอบ

flash[:notice] = 'Your work was awesome! A unicorn is born!'
flash.keep(:notice)
render js: "window.location = '#{root_path}'"

flash.keepจะให้แน่ใจว่าแฟลชจะถูกเก็บไว้สำหรับการร้องขอต่อไป ดังนั้นเมื่อroot_pathแสดงผลมันจะแสดงข้อความแฟลชที่กำหนด รางน่ากลัว :)


28

ฉันคิดว่านี่ดีกว่าเล็กน้อย:

render js: "window.location.pathname='#{jobs_path}'"


12
ดีกว่าเล็กน้อย:render js: "window.location.pathname = #{jobs_path.to_json}"
tokland

26

ในแอปหนึ่งของฉันฉันใช้ JSON เพื่อดำเนินการกับข้อมูลการเปลี่ยนเส้นทางและข้อความแฟลช มันจะมีลักษณะดังนี้:

class AccessController < ApplicationController
  ...
  def attempt_login
    ...
    if authorized_user
      if request.xhr?
        render :json => {
          :location => url_for(:controller => 'jobs', :action => 'index'),
          :flash => {:notice => "Hello #{authorized_user.name}."}
        }
      else
        redirect_to(:controller => 'jobs', :action => 'index')
      end
    else
      # Render login screen with 422 error code
      render :login, :status => :unprocessable_entity
    end
  end
end

และตัวอย่างง่ายๆของ jQuery จะเป็น:

$.ajax({
  ...
  type: 'json',
  success: functon(data) {
    data = $.parseJSON(data);
    if (data.location) {
      window.location.href = data.location;
    }
    if (data.flash && data.flash.notice) {
      // Maybe display flash message, etc.
    }
  },
  error: function() {
    // If login fails, sending 422 error code sends you here.
  }
})

1
ข้อมูลดีๆมากมายที่นี่ การใช้ render: location, of the: status option และ xhr? ตรวจสอบ. เนื่องจากเว็บแอปพลิเคชันจำนวนมากขึ้นใช้ API เพื่อให้บริการแอปบนอุปกรณ์เคลื่อนที่และสิ่งนี้ฉันหวังว่าจะได้เห็นสิ่งต่างๆในโพสต์นี้เป็นมาตรฐานมากขึ้น มีการโหวตเพิ่มขึ้นแน่นอน คำตอบที่ดี
TheJKFever

18

การรวมคำตอบที่ดีที่สุด:

...
if request.xhr?
  flash[:notice] = "Hello #{authorized_user.name}."
  flash.keep(:notice) # Keep flash notice around for the redirect.
  render :js => "window.location = #{jobs_path.to_json}"
else
...

ขอบคุณสำหรับคำตอบฉันใช้แล้ว อย่างไรก็ตามตอนนี้สำหรับการทดสอบเมื่อฉันพยายามร้องขอการดำเนินการนี้เป็น JS จะมีคำเตือน CORS: ActionController :: InvalidCrossOriginRequest คุณมีความคิดที่จะรวมสิ่งนี้ในการทดสอบหรือไม่?
V. Déhaye

1
def redirect_to(options = {}, response_status = {})
  super(options, response_status)
  if request.xhr?
    # empty to prevent render duplication exception
    self.status = nil
    self.response_body = nil
    path = location
    self.location = nil

    render :js => "window.location = #{path.to_json}"
  end
end

0

ฉันไม่ต้องการแก้ไขการทำงานของคอนโทรลเลอร์ดังนั้นฉันจึงคิดแฮ็คนี้ขึ้นมา:

class ApplicationController < ActionController::Base
  def redirect_to options = {}, response_status = {}
    super

    if request.xhr?
      self.status        = 200
      self.response_body = "<html><body><script>window.location.replace('#{location}')</script></body></html>"
    end
  end
end
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.