2012-02-08 15 views
9

Główny cel: Zezwól wtyczkom, klejnotom na dodawanie dodatkowych pól formularzy do predefiniowanych formularzy.Jak dodać dodatkowe pola formularza poza blokiem deklaracji formularza

Na przykład, aplikacja ma opracować formularz logowania:

<%= form_for(resource, :as => resource_name, ...) do |f| %> 
    <%= devise_error_messages! %> 
    ... 
<% end %>

Dział marketingu chce rozpocząć kampanię na następne 2 dni (ex: zarejestruj kodem promocyjnym i dostać dodatkowe punkty X). Musimy więc dodać dodatkowe pole promo code do WSZYSTKICH naszych formularzy rejestracyjnych.

Czy istnieje sposób, aby dodać dodatkowe pole do formularza z mojej wtyczki-szyny/kolejki, i zdefiniować metodę wywołania zwrotnego on_submit (aby podjąć działanie w sprawie dodatkowych danych pola)?

Korzyści:

  • umożliwia usunięcie funkcjonalności w 2 dni lub tydzień po prostu przez usunięcie go z pliku gem
  • gwarancji, że podstawowe funkcje witryny nie jest uszkodzony, to po prostu spada z powrotem do oryginalna funkcjonalność:
  • gwarancja, że ​​programista nie zostawił kodu gdzieś w głównej aplikacji
  • Wtyczka/railtie zajmuje się zapisywaniem/aktualizacją danych, które należą do niego

Wygląda na kod ActionView i wygląda na to, że nie ma na to żadnego sposobu. Jakie są Twoje myśli?

UWAGA: Haki Drupala o form_alter są doskonałym przykładem.

Odpowiedz

1

pomysł w etapach:

1) zdefiniowanie modelu, takiego jak AdditionalField (id, field_name, field_type, DEFAULT_VALUE, is_required)

2), a następnie tworzenie funkcji takich jak:

def self.for_form(my_form_name = nil) 
if my_form_name.nil? 
    self.all 
else 
    self.find(:all, :contitions => {:form_type => my_form_name.type} # or whatever selection criteria 
end 

3) następnie możesz powtórzyć znalezione dodatkowe pola i odpowiednio zbudować odpowiednie typy pól.

Użyłem tego rozwiązania dla strony porównawczej, w której musieli skonfigurować kwestionariusze dla każdego innego typu porównania.

Oto kod renderowania, którego użyłem, musisz go zmienić, aby pasował do Twojej sytuacji. relacje są:

convention -<booking>- user 
convention -< convention_question 
booking -< guests 
guest -< guest_answers 

QuestionsHelper

def render_guest_questions(guest, convention_question)  

    fields_for "booking[guest_answer_attributes][]", convention_question do |m| 
     case convention_question.display_type 
     when "Text" 
     '<td>' + text_field_tag("booking[guest_answer_attributes][convention_question_#{guest.id}_#{convention_question.id}]") + '</td>' 
     when "Boolean" 
     '<td>' + hidden_field_tag("booking[guest_answer_attributes][convention_question_#{guest.id}_#{convention_question.id}]", "No") + check_box_tag("booking[guest_answer_attributes][convention_question_#{guest.id}_#{convention_question.id}]", "Yes") + '</td>' 
     end 
    end 

end 

Controller

# TURN GUEST/QUESTIONS INTO guest answers 
if params[:booking] && !params[:booking].blank? && !params[:booking][:guest_answer_attributes].blank? 
    params[:booking][:guest_answer_attributes].each do |k,v| 
     handle_answers(k, v) 
    end 
end 

def handle_answers(k, v) 
    x = k.mb_chars.split(/_/) 
    g_id = x[2] 
    q_id = x[3] 
    item = GuestAnswer.find_or_create_by_guest_id_and_convention_question_id(
        {:guest_id => g_id, 
        :convention_question_id => q_id, 
        :answer => v}) 
end 
+0

W jaki sposób zapisałeś przesłane dane? – Uzbekjon

+0

Zapisałem go, tworząc tabelę user_answers. 'UserAnswer', które ma' belongs_to: user; belongs_to AdditionalField; 'z polami' user_id, additional_field_id, answer_value' – TomDunning

+0

uzbekjon - Dodałem kod użyty do projektu, który zrobiłem. Możesz zapytać o więcej, jeśli to nie ma sensu. – TomDunning

2

Przede wszystkim swój pomysł do izolowania tego kodu w gem/railtie/silnika jest doskonała. Myślę, że najlepszym rozwiązaniem może być łatanie małpy metodą form_for i trzymanie się w dodatkowym polu. Jeśli chodzi o wyzwalacz zgłoszony, jeśli korzystasz z Rails 3.1 i używają potoku zasobów, możesz mieć klejnot również obsługujący javascript, chociaż wymagałoby to niewielkiej zmiany w twoim application.js, aby wymagał pliku js z klejnotem, np. require 'promo/application.js, jeśli klejnot nazywa się "promo".

Spójrz na docs dla Customized Form Builder

Oto niektóre szorstki pomysł, jak to może działać, chociaż nie próbowałem tego kodu. Umieściłem to w pliku promo.rb podklasę Railtie lub Engine.

ActiveSupport.on_load(:action_view) do 

    module ActionView 
    module Helpers 
     module FormHelper 
     extend ActiveSupport::Concern 

     included do 
      alias_method_chain :form_for, :promo_code 
     end 

     module InstanceMethods 

      def form_for_with_promo_code(record, options = {}, &proc) 
      output = form_for_without_promo_code(record, options.merge(builder: FormBuilderWithPromoCode), proc) 
      # See file: actionpack-3.1.3/lib/action_view/helpers/form_helper.rb for details 
      # the output will have "</form>" as the last thing, strip that off here and inject your input field 
      promo_field = content_tag :input, name: 'promo_code' # you can also run this through the proc if you want access to the object 
      output.sub(%r{</form>$},promo_field+'</form>') 
      end 
     end 
     end 
    end 
    end 
end 

W dół drogi, zwł. Jeśli Twój dział marketingu może prowadzić więcej kampanii, możesz nawet chcieć zmienić formularze aplikacji, aby wskazać konkretnego budowniczego, który możesz zastąpić klejnotem bez łaty małpy tutaj.

+0

Dzięki za wskazanie w kierunku "Dostosowany kreator formularzy". Twoja odpowiedź jest najbliższa temu, co chcę. Myślę, że napiszę niestandardowego konstruktora formularzy, który doda ukryte pole (podobne do idei do "_metod") i doda oprogramowanie pośrednie do stojaka, które sprawdza to pole i uruchamia dodatkowe metody w moich silnikach/wtyczkach. – Uzbekjon

+0

Nurkowanie głębiej w kodzie Railsa Znalazłem metodę "extra_tags_for_form", która dodaje znaczniki 'authenticity_token'. Prześlę żądanie pobrania, które umożliwi innym metodom dodawanie dodatkowych tagów. Ale mam wrażenie, że powinni to już zrobić mądrzejsi ludzie niż ja. Czy istnieje sposób dodania znacznika do wszystkich formularzy (w jaki sposób działa 'protect_from_forgery')? – Uzbekjon

+0

@Uzbekjon Miałem tę samą myśl, ale nie mogłem znaleźć na to haka. Myślałem o hakowaniu pól, aby to zrobić, ale nie było oczywiste, że nie będzie to miało skutków ubocznych. Jestem tak zaskoczony, jak ty, że tak się nie stało. Sprawdziłem również kod 'simple_form', ale po prostu zawijam pomocników natywnych i konfiguruję własne API. –

0

w tym przypadku nowy klejnot powinien być stworzony, że będzie -

  1. add promo_code do modelu użytkownika i udostępnić go
  2. przesłanianie forma Devise wyposażonych w wejście kod promocyjny (wystarczy skopiować pasty Widok formularza i dodać promo pole)
  3. Dodaje potrzebne walidacji & dodatkowego przetwarzania (wywołania zwrotne) wewnątrz modelu użytkownika (meta-programowania na klasy użytkownika)

nie jesteśmy d Wszelkie zmiany w obrębie aplikacji głównej są bezpieczne.

+0

Tak, ta metoda sprawdzi się świetnie, jeśli masz tylko jedną "zmianę". Ale co, jeśli masz więcej niż jedną wtyczkę, która chce zmienić formularz. Metoda ta oznacza, że ​​programiści będą musieli śledzić wszystkie klejnoty i wtyczki, które obecnie zmieniają formularz. – Uzbekjon

+0

Oczywiście ścieżki zewnętrznych silników i to, co robią, należy zachować podczas dokonywania dalszych zmian. –

Powiązane problemy