Рейки 3: Як "перенаправити_ на" ​​у дзвінку Ajax?


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)}'"використання розрахованого винахідливого маршруту на основі моделі роботи. Це працює, лише якщо ваші маршрути винахідливі та використовують стандартні правила іменування, які відповідають вашим моделям. (Або якщо ви вказали ім'я_моделі на своїх моделях, щоб вони генерували правильні назви маршрутів.)
пляма

2
Приголомшливо Хто-небудь уявляє, чому просте перенаправлення_то не працює?
Tasos Anesiadis

1
@Tasos Anesiadis, redirect_to не працює, коли форма є «віддаленою» формою Rails, оскільки браузеру було наказано інтерпретувати відповідь контролера як Javascript. Ви можете побачити сторінку redirect_to на вкладці Response (через панель Network) Chrome DevTools, але замість цього потрібна інструкція браузеру від контролера про пошук іншої сторінки. Потрібні запропоновані тут рішення window.location або зміна форми на звичайну „локальну“ форму, якщо ви не хочете вручну подавати та обробляти дані форми через fetch () та JSON.
MSC

67

Існує дуже простий спосіб зберегти спалах для наступного запиту. У своєму контролері зробіть щось на зразок

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

flash.keepБуде переконатися , що спалах зберігається для наступного запиту. Отже, коли root_pathвізуалізується, він покаже подане флеш-повідомлення. Рейки чудові :)


27

Я думаю, це трохи приємніше:

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
Тут є багато хорошої інформації. Якісне та правильне використання рендеру: розташування, параметра: статус і 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.