RoR: как обрабатывать отправку пользовательских вложенных форм - PullRequest
0 голосов
/ 27 августа 2010

Мне удалось пройти почти весь путь к счастью с помощью моей пользовательской формы в ruby-on-rails, но последний шаг отсутствует, и невозможно найти ответ в сети из-за слишком большого числа общих слов.

Я считаю, что ответы на мои вопросы тривиальны для людей, которые какое-то время делали RoR, но имейте в виду, что изложение вопроса будет несколько сложным.

Давайте посмотрим эквивалентную проблему!

Схема:

  • publishers (id, name, address)

  • books (id, title, publisher_id, publishing_year, unit_price, qty)

  • sell_log (id, user_id, timestamp, book_id, qty, unit_price, comment)

Настраиваемое действие:

  • Имя: Продать (контекст: книга)

  • Ввод: qty, comment, (неявный ввод: book.id, timestamp; производный ввод: user_id, book.unit_price, book.qty)

  • Результат:

    • sell_log добавлен

    • books.qty уменьшилось

  • Возможные ошибки:

    • Кол-во неположительное или нецелое число.

    • Количество при вводе пользователем больше доступного количества (book.qty)

(К вашему сведению: вопрос не в дизайне базы данных.)

Таким образом, у нас есть настраиваемая форма (идентификатор скрытой книги; кол-во, комментарий), которую мы хотим реализовать как действие, подобное поведению «Редактировать» книги (update). Что сделано (это почти все):

- books_controller.rb: добавлен столбец custom_qty_display.

- books_helper.rb:

def custom_qty_display_column(record)
  record.qty.to_label + " ["
  link_to( "Sell..." \
            , { :controller => "books", :action => "sell_form", :id => record.id, :page => false } \
            , { :position => "replace", :inline => true, :class => "action" } \
          ) \
  + "]"
end

- views / books / sell_form.erb (только ключевые сведения)

<%
  form_remote_tag( \
    :url => { :controller => :books, :action => :sell, :id => params[:id] } \
  ) do
%>
...
<%= submit_tag 'Submit' %>
<%= link_to as_(:cancel), main_path_to_return, :class => 'cancel' %>
<% end %>
<div id="as_books-messages" class="messages-container" />

- books_controller.rb:

def sell
  errors = [] # We will collect error messages here
  # Checking parameters ...
  # Checking of available qty ...
  # If "errors" is still empty here, perform the action
  # Produce the output according to the above:
  if request.xhr?
    if errors.empty?
      # Q1: rendering of javascript which replaces the form with the modified row in the table.
    else
      # Q2: rendering of javascript which provides the "errors" for the user
    end
  else
    if errors.empty?
      index
    else
      # Q3: Redisplay the form and errors
    end
  end
end

Текущий прогресс

Когда я нажимаю ссылку "Продать ..." в записи списка книг, запись исчезает, вместо нее появляется пользовательская форма. В форме отлично работает ссылка «Отмена» (и кнопка [X]); кнопка SUBMIT работает (действие выполнено успешно, если ввод правильный).

Чего нет, так это того, что форма остается на месте. Теоретически я должен вернуть соответствующий javascript в местах, отмеченных Q1, Q2 и Q3. Я не хочу перепроектировать вещи и писать javascripts вручную, потому что при обновлении фреймворка я был бы вынужден повторить этот шаг. Я хочу создать необходимые javascript-скрипты наилучшим образом с точки зрения простоты и удобства обслуживания. Как я полагаю, теперь моя концепция неплоха.

Информация о версии

  • JRuby 1.5.0
  • драгоценные камни
    • рельсы 2.3.4
    • activerecord 2.3.4
    • activesupport 2.3.4

(Скажите, если что-нибудь еще нужно)

Частичный результат

# ...
if errors.empty?
  render :action => 'on_update.js'
else
  # ...
end
# ...

Ответы [ 2 ]

1 голос
/ 28 августа 2010

Какую версию Rails использует приложение? Какую библиотеку javascript использует приложение? Являются ли Book и SellLog RESTful ресурсами?

Это причина, по которой вы не используете link_to_remote в блоках помощника и respond_to в действиях контроллера?

С Rails 2.3, используя прототип, я делаю это так:

in controller:

def sell 
  # …
  # book / quantity / errors
  # …
  respond_to do |format|
    if errors.empty?
      format.js {render :update do |page|
        page.replace_html '_ID_OF_DOM_OBJECT_CONTAINING_ROW_INFO_', :partial => '_PARTIAL_FOR_ROW_INFO_LAYOUT_' 
      end}
      format.html { redirect_to :action => _WHATEVER_THE_SUCCESS_ACTION_IS_ }
    else
      format.js {render :update do |page|
        page.replace_html 'form', :partial => '_DOM_ID_OF_FORM_' 
      end}
      format.html { render :action => _WHATEVER_ACTION_DISPLAYED_THE_FORM_ }
    end
  end
end

_PARTIAL_FOR_ROW_INFO_LAYOUT_ будет иметь:

<div id="_ID_OF_DOM_OBJECT_CONTAINING_ROW_INFO_">
  <!-- row display markup -->
</div>

Многое из того, что вы делаете, будет проще, если вы будете следовать соглашениям Rails, но я не знаю вашу версию Rails, DSL приложения или какие плагины и / или гемы использует ваше приложение, чтобы объяснить текущий шаблон в ваш контроллер.

0 голосов
/ 02 сентября 2010

Шаг # 1: Вы должны изменить link_to в вашем помощнике, чтобы включить eid

link_to( "Close..." \
    , { :controller => "books", :action => "sell_form", :id => record.id, :page => false, :eid => @controller.params[:eid] } \
    , { :position => "replace", :inline => true, :class => "action" } \
  )

Шаг # 2: Вы должны изменить sell_form.erb и установить parent_model,parent_column, nested и eid в url.Вы должны установить для update значение book_list и сгенерировать правильный HTML-идентификатор для формы с помощью element_from_id().

<%
  form_remote_tag( \
    :url => { :controller => :books, :action => :sell, :id => params[:id], :parent_column => "books", :parent_model => "Publisher", :nested => "true", :eid => params[:eid] } \
    , :update => :book_list \
    , :html => { :id => element_form_id(:action => :update) } \
  ) do
%>

Шаг # 3: Измените часть if request.xhr? наследующий простой код.(Не полностью протестировано, лучший вариант работает правильно.)

if request.xhr?
  if @record.valid?
    render :action => 'on_update.js'
  else
    render :action => 'form_messages.js'
  end
else
  if @record.valid?
    return_to_main
  else
    render :action => 'sell_form'
  end
end
...