Rails 3: Ajax çağrısında "redirect_to" nasıl yapılır?


85

Aşağıdaki attempt_loginyöntem, bir oturum açma formu gönderildikten sonra Ajax kullanılarak çağrılır.

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

Sorun şu ki redirect_tobu işe yaramıyor.

Bunu nasıl çözersiniz?

Yanıtlar:


102

Sonunda değiştirdim

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

Bununla:

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

ve iyi çalışıyor!


43
Daha iyi bir yaklaşımrender :js => "window.location = '#{jobs_path}'"
zakelfassi

3
Çalışıyor, ancak yönlendirme konumunu gerçek bir json başarı mesajıyla geri vermek ve yönlendirmeyi ön uçta yapmak çok daha iyi olmaz mıydı?
justinxreese

1
jobs_pathTemelde URL kadar katı değil mi? Eğer URL değişirse, ekstra dikkatli olmadığınız sürece rotanın adı da değişir. Diğer bir alternatif render js: "window.location = '#{polymorphic_path(@job.class)}'", İş modeline dayalı olarak hesaplanan, becerikli rotayı kullanmaktır. Bu, yalnızca rotalarınız becerikli ise ve modellerinizle uyumlu standart adlandırma kuralları kullanıyorsa işe yarar. (Doğru yol adları oluşturmak, böylece Yoksa modellerde MODEL_NAME belirtirseniz.)
leke

2
Harika. Basit redirect_to'nun neden çalışmadığı hakkında bir fikri olan var mı?
Tasos Anesiadis

1
@Tasos Anesiadis, tarayıcıya denetleyiciden gelen yanıtı Javascript olarak yorumlaması söylendiği için, form 'uzak' Rails formu olduğunda redirect_to çalışmaz. Redirect_to sayfasını Chrome DevTools'un Yanıt sekmesinde (Ağ paneli aracılığıyla) görebilirsiniz, ancak bunun yerine gerekli olan, denetleyiciden tarayıcıya farklı bir sayfa bulması için bir talimattır. Form verilerini fetch () ve JSON aracılığıyla manuel olarak göndermek ve işlemek istemediğiniz sürece, burada sağlanan window.location çözümleri veya formu normal bir 'yerel' forma dönüştürmek gerekir.
MSC

68

Bir sonraki istek için flaşı saklamanın çok kolay bir yolu var. Kontrol cihazınızda şöyle bir şey yapın

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

Bir flash.keepsonraki istek için flaşın saklanmasını sağlayacaktır. Böylece root_path, işlendiğinde, verilen flash mesajı gösterecektir. Rails harika :)


28

Sanırım bu biraz daha hoş:

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


12
biraz biraz daha güzel:render js: "window.location.pathname = #{jobs_path.to_json}"
tokland

26

Uygulamalarımdan birinde, yönlendirme ve flash mesaj verilerini taşımak için JSON kullanıyorum. Şöyle bir şeye benzeyecektir:

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

Ve basit bir jQuery örneği şöyle olacaktır:

$.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
Burada birçok iyi bilgi var. İyi ve doğru render: location,: status seçeneği ve xhr? Kontrol. Daha fazla web uygulaması, mobil uygulamalara hizmet etmek için API'leri benimsedikçe, bu gönderideki şeylerin daha standart hale geldiğini görmeyi umuyorum. Kesinlikle olumlu oyumu al. Harika Cevap
TheJKFever

18

Tüm yanıtların en iyilerini birleştirmek:

...
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
...

Cevabınız için teşekkürler, kullandım. Bununla birlikte, şimdi test için, bu eylemi JS olarak talep etmeye çalıştığımda, bir CORS uyarısı veriyor: ActionController :: InvalidCrossOriginRequest. Bunu testlere nasıl entegre edeceğiniz konusunda bir fikriniz var mı?
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

Denetleyici eylemlerimi değiştirmek istemedim, bu yüzden şu hack ile geldim:

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.