Opracuj formularz w ramach innego kontrolera

129

Używam klejnotu devise do procedur sign_in / sign_out.

Wygenerowałem pliki widoków z devise, używając rails g devise views

Zauważyłem, że istnieje plik devise / session / new.html.erb, który zawiera formularz do logowania.

Stworzyłem inny plik devise / session / _form.html.erb i zrobiłem to <%= render 'form' %>w pliku new.html.erb i to wyszło bardzo dobrze.

Teraz chciałem dołączyć ten formularz z innego kontrolera. Tak więc w kontrolerze o nazwie „main” (konkretnie na stronie widoku) „main / index.html.erb” umieściłem <%= render 'devise/sessions/form' %>plik. Wygląda na to, że włączenie działało dobrze, ale pojawia się następujący błąd.

NameError in Mains#index

Showing /home/administrator/Ruby/site_v4_ruby/app/views/devise/sessions/_form.html.erb where line #1 raised:

undefined local variable or method `resource' for #<#<Class:0x007f1aa042d530>:0x007f1aa042b870>
Extracted source (around line #1):

1: <%= form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f| %>
2:   <p><%= f.label :email %><br />
3:   <%= f.text_field :email %></p>
4: 

Wygląda na to, że część form_for (zasób, ...) jest przyczyną problemu (co działa dobrze, jeśli jestem na oryginalnej stronie devise sign_in ... Jak mogę rozwiązać ten problem w sposób railsowy?

Osobiście wolę używać funkcji „render”, aby dołączyć formularz, zamiast pisać wbudowane kody HTML.

Czy muszę określić coś (zasób) w „głównym” kontrolerze?

Będę wdzięczny za twoją pomoc. Dziękuję Ci.

user482594
źródło
Czy jest jakieś rozwiązanie ...? Od 16 listopada nie mam rozwiązania wykorzystującego renderowanie .. chociaż jeśli po prostu
wpiszę
czy znalazłeś na to rozwiązanie?
neebz
Tak, wybrałem odpowiedź, która rozwiązuje mój problem. To było prostsze niż myślałem.
user482594

Odpowiedzi:

243

Jak mówi Andres, formularz wywołuje pomocników, które są określone przez Devise i dlatego nie są obecne, gdy uzyskujesz dostęp do formularza Devise z kontrolera innego niż Devise.

Aby obejść ten problem, musisz dodać następujące metody do klasy pomocniczej kontrolera, pod którym chcesz wyświetlać formularz. Alternatywnie możesz po prostu dodać je do pomocnika aplikacji, aby były dostępne w dowolnym miejscu.

  def resource_name
    :user
  end

  def resource
    @resource ||= User.new
  end

  def devise_mapping
    @devise_mapping ||= Devise.mappings[:user]
  end

Źródło: http://pupeno.com/blog/show-a-devise-log-in-form-in-another-page/

Rupert Madden-Abbott
źródło
6
Świetny. Upewnij się, że trafia do pomocnika, a nie do kontrolera. Może to również prowadzić do ogromnych problemów, jeśli masz inne zasoby (np. Firmy, które również mogą się zalogować) i chcesz załadować ich formularz. Upewnij się, że przedefiniowałeś nazwy, a routing może również stać się problemem.
Michael Schmitz,
1
Wadą tego rozwiązania jest to, że się psuje inherited_resources.
jrhorn424
6
Aby ograniczyć zakres tych metod (i uniknąć konfliktu przestrzeni nazw z innymi klejnotami, na przykład), spróbuj dodać powyższe metody do samego kontrolera wraz z helper_method :resource_name, :resource_class, :resource, :devise_mapping( :resource_classwydaje się, że jest to wymagane w nowszych wersjach Devise).
spume
1
Jakie powinno być rozwiązanie, jeśli mamy kilka wymyślonych modeli?
yozzz
Mam dwa modele Devise i do obu potrzebuję logowania na stronie głównej. Masz jakiś pomysł, jak zrobić coś podobnego do twojej odpowiedzi?
DR_
8

Spróbuj tego też ... sprawdź to pytanie .

Źródło

<%= form_for("user", :url => user_session_path) do |f| %>
  <%= f.text_field :email %>
  <%= f.password_field :password %>
  <%= f.check_box :remember_me %>
  <%= f.label :remember_me %>
  <%= f.submit 'Sign in' %>
  <%= link_to "Forgot your password?", new_password_path('user') %>
<% end %> 
jsp
źródło
Czy nie jest to najlepsza praktyka ze wszystkich odpowiedzi?
allegutta
4

Utworzony formularz działa, gdy jest renderowany z kontrolera Devise, ponieważ „zasób” jest definiowany za pomocą Devise. Przyjrzyj się implementacji Devise SessionsController - z tego, co rozumiem, próbujesz odtworzyć „nową” akcję. Metoda „build_resource” jest prawdopodobnie tym, czego szukasz.

Warden gem gdzie obiekty „resource” pochodzą. Jeśli chcesz zagłębić się głębiej, dobrze byłoby tam zajrzeć.

Andres Freyria
źródło
Po prostu zadzwoniłem do „build_resource” z kontrolerem sieciowym, ale wywołuje błąd [niezdefiniowana zmienna lokalna lub metoda „build_resource”]. Próbowałem dołączyć wewnętrznego pomocnika devise, wstawiając „include Devise :: Controllers :: InternalHelpers” na górze „main_controller”, ale wywołuje również błąd z „AbstractController :: ActionNotFound”
user482594
3

Aby doprecyzować zaakceptowaną odpowiedź, używamy tego pomocnika, aby zezwolić na różne rodzaje zasobów:

def resource_name
  @resource_name ||= if admin_controller?
    :admin_user
  else
    :user
  end
end

def resource
  @resource ||= resource_name.to_s.classify.constantize.new
end

def devise_mapping
  @devise_mapping ||= Devise.mappings[resource_name]
end

gdzie admin_controller?jest coś, co mamy wcześniej w ApplicationControllerobsłudze przekierowań logowania:

def admin_controller?
  !devise_controller? and request.path =~ /^\/admin/
end
helper_method :admin_controller?
bbozo
źródło
2

Otrzymałem ten sam błąd, undefined local variable or method "resource"który opisałeś z jednego z moich kontrolerów, ponieważ w mojej klasie bazowej kontrolera brakowało następującego (błąd Rails-API ActionController :: API był uszkodzony):

include ActionController::Helpers

Dlatego w widoku nie można było rozwiązać metod pomocniczych z Devise.

Aby Devise działało z Rails-API, musiałem dołączyć:

class ApplicationController < ActionController::API

  include AbstractController::Rendering
  include AbstractController::Layouts
  include ActionController::MimeResponds
  include AbstractController::Translation
  include ActionController::ImplicitRender
  include ActionController::Helpers
Christopher Oezbek
źródło