Вложенная форма Rails с Ajax

У меня есть интересная проблема, связанная с этапом создания объекта в моем музыкальном приложении Rails 3.

У меня есть две модели (не настоящие модели, а для простоты): Playlist Song

Плейлист имеет_много песен; Песня принадлежит плейлисту.

Каждый объект списка воспроизведения должен иметь точное количество песен. Каждая песня должна принадлежать плейлисту.

Учитывая это, я хочу создать процесс создания списка воспроизведения, который также включает в себя одновременное создание всех необходимых песен.

Другое дело, что для получения данных песни пользователь вводит запрос (который я не буду сохранять в модели песни), с помощью которого я затем собираю данные из API. Это данные, которые следует использовать для создания объекта Song. Поэтому я не могу (не думаю?) использовать традиционный form_for.

Вместо этого я использую удаленный form_tag. Эта форма запрашивает запрос, а затем использует запрос Ajax для извлечения данных, которые помещаются во временный объект Song, а затем отображаются на странице создания списка воспроизведения с использованием представления Song. Эта форма повторно используется для всех необходимых объектов Song для списка воспроизведения.

Итак, идея состоит в том, что когда пользователь вводит необходимое количество запросов (то есть добавляет необходимое количество песен в список воспроизведения), ему предоставляется новая кнопка, которая позволяет ему отправить информацию о списке воспроизведения и продолжить процесс. Затем будет создан список воспроизведения со всеми объектами Song, которые были созданы с помощью Ajax в качестве дочерних элементов.

На самом деле, я не могу понять, как это работает элегантно. Хотя я создаю объекты Song с помощью Ajax, они нигде не сохраняются, и они не знают, в какой список воспроизведения они должны быть добавлены (поскольку объект списка воспроизведения еще не существует в базе данных). Следовательно, когда я перехожу к следующему шагу, я остаюсь без всех данных песни. Я изучил использование вложенных форм с accepts_nested_attributes_for, но я не могу найти способ использовать его с моей настройкой (форма, не основанная на модели, с использованием Ajax).

Итак, я застрял. Если кто-то может помочь, это было бы очень признательно.


person Chris    schedule 23.12.2010    source источник


Ответы (2)


Я уже сталкивался с подобными проблемами. Для справки, я никогда не находил удаленные теги полезными и никогда не использовал их для чего-то такого сложного. Вы правы - вложенные формы с accepts_nested_attributes_for были созданы для таких вещей, и, безусловно, есть способ заставить их работать.

Что вам нужно сделать, так это после вашего Ajax-запроса сгенерировать ваши вложенные поля Song на лету с помощью JavaScript. Например, допустим, у вас есть:

<form...>
  <input type="text" name="playlist[name]" />
  <div class="songs">
  </div>
</form>

Вы хотите, чтобы ваш обратный вызов Ajax делал что-то вроде (здесь я предполагаю jQuery):

<script type="text/javascript">
  $(function() {
    var songs = $('form .songs') // Store a reference to the DOM song container
    // Insert Ajaxy goodness for song lookup here.
    // When it's finished, it should call add_song and pass in any data it needs
  })

  function add_song(song_name, artist_name, artist_id) {
    var num_songs = $(songs).size()
    $(songs).append('<p>'+song_name+' by '+artist_name+
      '<input type="hidden" name="playlist[songs_attributes]['+num_songs+'][name]" value="'+song_name+'" />' +
      '<input type="hidden" name="playlist[songs_attributes]['+num_songs+'][artist_id]" value="'+artist_id+'" /></p>')
  }
</script>

Там мало что происходит:

  1. Имя поля "playlist[songs_attributes]..." сообщает Rails, что это поле принадлежит вложенному атрибуту.
  2. Индекс num_songs после "playlist[songs_attributes]..." чрезвычайно важен. Скорее, чрезвычайно важно, чтобы он был уникальным для каждой песни. Думайте об этом как об индексе массива. Так что идеально, если исходить из размера постоянно растущего списка песен. (Это самая сложная и наименее очевидная часть динамического создания вложенных полей формы, IMO.)

Мой код, вероятно, не идеален, и я уверен, что все равно не правильно понял ваши имена полей. Но это должно быть хорошим началом. Кроме того, просмотр отправленных данных формы в журнале разработки вашего приложения весьма полезен для отладки этого материала.

person bioneuralnet    schedule 23.12.2010
comment
Спасибо! Это выглядит на пути к тому, что мне нужно. Я попробую. :) - person Chris; 24.12.2010
comment
Я понял, что использование скрытых полей таким образом позволит пользователю редактировать данные, чего я не хочу. В качестве обходного пути я попытался сохранить запросы в скрытых полях. Однако это вынуждает меня обновлять внешние данные в методе «создания» плейлиста, что происходит медленно. Все было бы проще, если бы я сначала создал и сохранил список воспроизведения, а затем создал и сохранил каждую песню по мере ее ввода пользователем. Однако я не хочу, чтобы списки воспроизведения не содержали определенного количества песен. Хотелось бы, чтобы было какое-то временное хранилище БД. - person Chris; 24.12.2010

Вот несколько вещей, которые следует учитывать:

  • А как насчет ваших пользователей, у которых нет JavaScript? (Мне сказали, что такие люди существуют.)
  • Почему вы хотите связать процесс создания плейлиста с процессом создания песни? Что плохого в создании плейлиста как отдельного процесса? Таким образом можно сохранить плейлист. Затем вы можете создавать песни (поскольку для этого требуется сбор данных из отдельного API), чтобы песни можно было сохранить. Затем вы можете связать песни с плейлистом. Рассмотрите возможность сохранения ввода пользователя на каждом шаге, чтобы, если ему/ей придется остановиться на середине, он/она не потерял все существующие данные.
  • Если вы сначала реализуете не-Ajax-интерфейс, вам может быть проще собрать воедино части, чтобы создать окончательный Ajax-интерфейс. Таким образом, у вас есть все необходимые ресурсы на месте. Ajax просто становится хорошим клеем, так что не так много обновлений страниц.
person monocle    schedule 24.12.2010
comment
Хорошие моменты, хотя я уже рассмотрел их. Приложение в целом по другим причинам требует JavaScript, поэтому я не слишком беспокоюсь о поддержке пользователей, у которых он не включен. И причина, по которой я хочу, чтобы процессы были связаны, заключается в том, что объекты обязательно взаимосвязаны. То есть мне не нужен плейлист, в котором нет всех «x» песен. - person Chris; 24.12.2010