Rails 3: Làm cách nào để “redirect_to” trong lệnh gọi Ajax?


85

attempt_loginPhương thức sau được gọi bằng Ajax sau khi gửi biểu mẫu đăng nhập.

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

Vấn đề là điều redirect_tođó không hoạt động.

Bạn sẽ giải quyết điều này như thế nào?

Câu trả lời:


102

Cuối cùng, tôi chỉ thay thế

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

Với cái này:

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

và nó hoạt động tốt!


43
Một cách tiếp cận tốt hơn sẽ làrender :js => "window.location = '#{jobs_path}'"
zakelfassi

3
Nó hoạt động, nhưng sẽ tốt hơn rất nhiều nếu chuyển lại vị trí chuyển hướng với một thông báo thành công json thực tế và thực hiện chuyển hướng trên giao diện người dùng?
justinxreese

1
jobs_pathVề cơ bản không phải là cứng nhắc như URL? Nếu URL thay đổi, tên của tuyến đường cũng vậy, trừ khi bạn hết sức cẩn thận. Một giải pháp thay thế khác là render js: "window.location = '#{polymorphic_path(@job.class)}'"và sử dụng lộ trình nguồn lực được tính toán dựa trên mô hình Công việc. Điều này chỉ hoạt động nếu các tuyến của bạn tháo vát và sử dụng các quy ước đặt tên tiêu chuẩn phù hợp với mô hình của bạn. (Hoặc nếu bạn chỉ định model_name trên các mô hình của mình để chúng tạo ra các tên tuyến đường phù hợp.)
smudge

2
Tuyệt vời. Bất cứ ai có bất kỳ ý tưởng tại sao redirect_to đơn giản không hoạt động?
Tasos Anesiadis

1
@Tasos Anesiadis, redirect_to không hoạt động khi biểu mẫu là biểu mẫu Rails 'từ xa' vì trình duyệt được yêu cầu diễn giải phản hồi từ bộ điều khiển dưới dạng Javascript. Bạn có thể thấy trang redirect_to trong tab Phản hồi (qua bảng điều khiển Mạng) của Chrome DevTools nhưng thay vào đó, điều cần thiết là hướng dẫn cho trình duyệt từ trình điều khiển để tìm một trang khác. Các giải pháp window.location được cung cấp tại đây hoặc thay đổi biểu mẫu thành biểu mẫu 'cục bộ' thông thường là bắt buộc, trừ khi bạn muốn bắt đầu gửi và xử lý dữ liệu biểu mẫu theo cách thủ công thông qua fetch () và JSON.
MSC

67

Có một cách rất dễ dàng để giữ đèn flash cho lần yêu cầu tiếp theo. Trong bộ điều khiển của bạn, làm điều gì đó như

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

Các flash.keepsẽ đảm bảo đèn flash được lưu giữ theo yêu cầu tiếp theo. Vì vậy, khi root_pathđược kết xuất, nó sẽ hiển thị thông báo flash đã cho. Rails thật tuyệt vời :)


28

Tôi nghĩ rằng điều này là đẹp hơn một chút:

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


12
hơi hơi đẹp hơn:render js: "window.location.pathname = #{jobs_path.to_json}"
tokland

26

Trong một trong các ứng dụng của tôi, tôi sử dụng JSON để thực hiện chuyển hướng và dữ liệu tin nhắn flash. Nó sẽ trông giống như thế này:

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

Và ví dụ jQuery đơn giản sẽ là:

$.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
Rất nhiều thông tin tốt ở đây. Sử dụng tốt và đúng cách render: location, of the: status option và xhr? kiểm tra. Khi ngày càng có nhiều ứng dụng web áp dụng API để phục vụ các ứng dụng dành cho thiết bị di động và những thứ như vậy, tôi hy vọng sẽ thấy những thứ trong bài đăng này trở nên chuẩn hóa hơn. Chắc chắn có ủng hộ của tôi. Câu trả lời hay
TheJKFever

18

Tổng hợp các câu trả lời hay nhất trong số tất cả các câu trả lời:

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

Cảm ơn câu trả lời của bạn, tôi đã sử dụng nó. Tuy nhiên, bây giờ để thử nghiệm, khi tôi cố gắng yêu cầu hành động này dưới dạng JS, nó sẽ đưa ra cảnh báo CORS: ActionController :: InvalidCrossOriginRequest. Bạn có bất kỳ ý tưởng nào về cách tích hợp điều này trong các bài kiểm tra?
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

Tôi không muốn sửa đổi các hành động của bộ điều khiển của mình nên tôi đã nghĩ ra bản hack này:

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.