alexfedoseev
@alexfedoseev
React & Rails Dev

Генерация (render) формы редактирования nested объекта через ajax (с jquery-fileupload-rails)?

У меня есть 2 модели:
  1. Building → has_many :building_views
  2. BuildingView → belongs_to :building

Я могу создать BuildingView только для существующего Building (через edit / update Building). Атрибуты BuildingView: изображение (image) и заголовок(title).
Изображения загружаются через Carrierwave.

Предполагаемый workflow:
  1. Пользователь заходит на страницу редактирования Building
  2. Там есть file_field, который позволяет загружать сразу несколько файлов
  3. Он выбирает файлы и они загружаются с помощью гема jquery-fileupload-rails (создавая BuildingView для каждого изображения)
  4. Сразу после загрузки они появляются на текущей странице (через ajax, без перезагрузки): превью и поле для редактирования заголовка BuildingView

C пунктами 1, 2 и 3 всё ок. Картинки грузятся, BuildingView создаются и после перезагрузки страницы они появляются на странице редактирования соответствующего Building с превью и полем редактирования заголовка (которое тоже успешно редактируется).

Проблема с пунктом номер 4: не получается вывести форму редактирования BuildingView через ajax. Я не могу понять как срендерить соответствующий партиал nested формы. Вот мои файлы:

controllers/admin/buildings_controller.rb
# other defs ...

def edit
  @building = Building.find(params[:id])
end

def update
  @building = Building.find(params[:id])
    if @building.update_attributes(building_params)
      flash[:success] = 'Woohoo!'
      redirect_to admin_buildings_url
    else
      render :edit
    end
end

# other defs ...

private
  def building_params
    params.require(:building).permit(:all_building_params,
                                      building_views_attributes: [
                                        :_destroy,
                                        :id,
                                        :building_id,
                                        :image,
                                        :title
                                      ])
  end


views/admin/buildings/edit.html.haml
%h1 Edit building
= form_for [:admin, @building] do |f|
  = render 'admin/buildings/form', f: f
  .form-element-container
    .form-element.b-form-labels  
    .form-element.b-form-fields
      = f.submit 'Edit building'
      = link_to t('admin.cancel'), :back

= form_for [:admin, BuildingView.new] do |f_bv|
  = f_bv.hidden_field :building_id, value: @building.id
  = f_bv.file_field :image, multiple: true, name: 'building_view[image]'


views/admin/buildings/_form.html.haml
// fields for Building...

#building_views
  .form-element-container
    .form-element
      %h2 Building views

= f.fields_for :building_views do |bv_form|
  = render 'admin/buildings/form_building_views', f: bv_form


views/admin/buildings/_form_building_views.html.haml
.form-element-container
  .form-element.b-form-labels Image:
  .form-element.b-form-fields= image_tag(f.object.image_url(:thumb)) if f.object.image

.form-element-container
  .form-element.b-form-labels Title:
  .form-element.b-form-fields= f.text_field :title


assets/javascript/admin/buildings.js.coffee
jQuery ->
  $('#building_view_image').fileupload(
    dataType: 'script'
  )


controllers/admin/building_views_controller.rb
class Admin::BuildingViewsController < ApplicationController

  def create
    @building_view = BuildingView.create(building_view_params)
  end

  private

    def building_view_params
      params.require(:building_view).permit(:building_id, :image)
    end

end


views/admin/building_views/create.js.haml
- if @building_view.new_record?
  == alert('Oops!');
- else
  == $('#building_views').append('#{j render('admin/buildings/form_building_views')}');


При отправке файлов я вижу в консоли ошибку:
ActionView::Template::Error (undefined local variable or method `f' for #<#<Class:0x007ffaa52faad8>:0x007ffaa7393970>):


Мне нужно передать эту самую f:
j render( partial: 'admin/buildings/form_building_views', locals: { f: ??? } )


Как мне это сделать для партиала nested формы?
  • Вопрос задан
  • 3457 просмотров
Решения вопроса 1
alexfedoseev
@alexfedoseev Автор вопроса
React & Rails Dev
В общем, так и не поняв как мне эту f передать в этот партиал (насколько я могу судить её и не передать), я решил пойти путём воссоздания этого партиала «руками».

Во-первых, я создал отдельный партиал для ajax-вставки (я даю сразу результат, ниже поясняю откуда в нём появились переменная @building и хелпер building_nested_model_count):

views/admin/buildings/_form_building_views_ajax.html.haml
.form-element-container
  .form-element.b-form-labels= image_tag(@building_view.image_url(:thumb)) if @building_view

  .form-element.b-form-fields= text_field_tag "building[building_views_attributes][#{building_nested_model_count(@building_view, @building)}][title]", nil, placeholder: 'Title'

= hidden_field_tag "building[building_views_attributes][#{building_nested_model_count(@building_view, @building)}][id]", @building_view.id


В нём я просто воссоздал форму, которую мне нужно добавить через ajax (rails с помощью хелперов её генерит автоматом, а я сгенерил её руками). Для того, чтобы оно заработало мне нужно внести изменения в три файла:

1. Определяем переменную @building

controllers/admin/building_views_controller.rb
class Admin::BuildingViewsController < ApplicationController

  def create
    @building_view = BuildingView.create(building_view_params)
    @building = @building_view.building # определяем @building
  end

  private

    def building_view_params
      params.require(:building_view).permit(:building_id, :image)
    end

end


2. Для хэша params в полях id и name формы присутствует индекс для каждого BuildingView (см. в ajax-партиал), рассчитаем его с помощью хелпера:

helpers/admin/building_views_helper.rb
module Admin::BuildingsHelper

	def building_nested_model_count(nested_var, building)
		nested_class = nested_var.class
		nested_class.where(building_id: building.id).count
	end

end


3. И наконец изменим партиал для рендера:

views/admin/building_views/create.js.haml
- if @building_view.new_record?
  == alert('Oops!');
- else
  == $('#building_views').append('#{j render('admin/buildings/form_building_views_ajax')}');


Я в силу небольшого опыта не понимаю: костыль это или решение задачи (с удовольствием послушаю мнения). Но оно работает: форма для нового BuildingView рендерится, его отредактированный title сохраняется.
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы