Rails Nested Form with Ajax - ruby-on-rails

I have an interesting problem involving an object creation step in my Rails 3 music application.
I have two models (not the actual models; but for simplicity):
Playlist
Song
Playlist has_many Songs; Song belongs_to Playlist.
Each Playlist object must have an exact number of Songs. Each Song must belong to a Playlist.
Given this, I want to create a process for creating a Playlist that also involves creating all of the necessary Songs at the same time.
Another thing is that to get Song data, the user enters a query (which I will not be saving in the Song model), which I then gather data from an API with. This is the data that should be used to make the Song object. Therefore, I can't (don't think?) use a traditional form_for.
Instead, I'm using a remote form_tag. This form asks for a query and then uses an Ajax request to fetch the data, which is put into a temporary Song object and then displayed inline on the Playlist creation page using a Song view. This form is reused for all the necessary Song objects for the Playlist.
So, the idea is that when the user has entered the required number of queries (i.e. added the required number of songs to the playlist), they are presented with a new button that enables them to submit the playlist info and continue in the process. The Playlist will then be created with all the Song objects that were created via Ajax as children.
In reality, I can't figure out a way for this to work in an elegant way. Although I create the Song objects via Ajax, they aren't saved anywhere and they aren't aware of which Playlist they're supposed to be added into (because the Playlist object doesn't exist in the database yet either.) Therefore, when I go to the next step, I'm left without all the Song data. I looked into using nested forms with accepts_nested_attributes_for, but I can't figure out a way to use it with my setup (a non-model-based form using Ajax.)
So, I'm stuck. If anyone can help, it would be very much appreciated.

I've run into problems similar to this before. For the record, I have never found the remote tags useful and never end up using them for something this complicated. You're right - nested forms with accepts_nested_attributes_for were made for this kind of thing, and there's certainly a way to get it work.
What you need to do is, after your Ajax query, generate your nested Song fields on the fly with JavaScript. For example, let's say you have:
<form...>
<input type="text" name="playlist[name]" />
<div class="songs">
</div>
</form>
You will want your Ajax callback to do something like (I'm assuming jQuery here):
<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>
There are few things going on there:
The field name "playlist[songs_attributes]..." is what tells Rails that that field belongs to a nested attribute
The num_songs index after "playlist[songs_attributes]..." is extremely important. Rather, it's extremely important that it be unique for every song. Think of it like an array index. So basing it off of the size of the every-increasing list of songs is perfect. (This is the trickiest and least-obvious part of dynamically creating nested form fields, IMO.)
My code probably isn't perfect, and I'm sure I didn't get your field names right anyway. But it should be a good start. Also, looking at the submitted form data in your app's development log is quite helpful for debugging this stuff.

Here are a few things to consider:
What about your users who don't have JavaScript available? (I've been told those people exist.)
Why do you want to couple the playlist creation process to the song creation process? What's wrong with creating a playlist as its own process? This way a playlist can be saved. Then you can create songs (since it requires gathering data from a separate API), so the songs can be saved. Then you can associate songs to a playlist. Consider saving the user's input at each step, so that if s/he has to stop in the middle, s/he won't lose all the existing data.
If you implement a not-Ajax interface first, then it may be easier for you to pull together the pieces to create your final Ajax interface. This way you have your all your necessary resources in place. The Ajax just becomes a nice glue so that there aren't so many page refreshes.

Related

Creating multiple objects of same model with a single form in rails

I want to create several instances of same model form a single form. And more importantly, the number of instances aren't known before form rendering.
I've seen several tutorials of this kind, but unfortunately those didn't suit my need. I've seen Ryan bate's nested form tutorial. But I'm not creating nested form. I've also seen some tutorials, which do create multiple objects, but the number of object's are all known in those cases. One of the tutorial is here - http://archive.railsforum.com/viewtopic.php?id=717
User will click a button and a new set of fields for a new object will be inserted just like the nested form demo from ryanb.
Here is a mockup of what I want. It's basically a very small form fit into a single line.
As i understand you need cocoon gem it allows to add form fields
It sounds like you may need to reach beyond Rails views and utilize javascript to dynamically render more "partials" when the user decides to add more fields. Something like this: Adding input elements dynamically to form
If you want to keep your view rendering logic in rails, you could make an AJAX request to your application, have it return just a partial's worth of html back, and insert the response html into your dom.

How do I get Grails g:select with multiple-selection with all selections when returning from the controller

I have a page that is a report from a database and I'm working on modifying how the filtering works. The intention is to allow the user to select possible values form a list that will be used to filter the resulting report. There are too many values to do this with checkboxes. I'm defining a multiple selection list box with this:
<g:select name="country" from="${countryDataList.KOUNTRY}" value="${params.country}" multiple="true" />
countryDataList is a List<> of objects with a name and a value which I create in the controller. I'm able to get the selected counties and process them without an issue.
But when the page returns from the controller with the filtered report, only the first selection in the list is selected. It doesn't re-select all of the items that the user selected. I am passing the params.country object back from the controller as
country:params.country
I saw some posts about this not working, but they are all from several years ago. Am I missing a vital step?
Ahh sorry, I was reading it on the phone initially and missed the point.
So what you want is a way of sending a multiple select box to a confirmation page. If I understand correctly?
Anyways how many objects in the select are we talking massive or a dozen couple of dozen or so ?
What I did was use check boxes and did a confirmation which shows the selection ticked in check boxes.. So this is the confirmation page that loads in https://github.com/vahidhedayati/mailinglist/blob/master/grails-app/views/mailingListEmail/confirmcontact.gsp
this page which is where multiple attachments selected from the schedule re-appear...
https://github.com/vahidhedayati/mailinglist/blob/master/grails-app/views/mailingListAttachments/_mailerAttachmentsDisplay.gsp.
Please note advice below is all conceptual stuff and there may be easier ways than this
Other than that You could create a taglib call on the confirmation page https://github.com/vahidhedayati/ajaxdependancyselection/blob/master/grails-app/taglib/ajaxdependancyselection/AutoCompleteTagLib.groovy#L55 which takes in your arrayList you could probably convert it to JSON pass it into the javascript that you load in within the taglib (on mine further down it loads this page in)
https://github.com/vahidhedayati/ajaxdependancyselection/blob/master/grails-app/views/autoComplete/_selectJs1.gsp#L23
and look to reselect them using javascript... as I say I haven't tested the last bit, the first bit i.e. checkbox works it is/has been in use.
Years later from you I just had the same problem. What I figured out is: it happens when params.country is an array instead of a Collection (i.e. an ArrayList).
A workaround for this if you want to stick to the array type is at the value attribute of the tag doing this: params.country?.findAll().

Need to update HABTM checkboxes based on a select menu

In my rails 3.1 app, I have a Timesheet model. A Timesheet belongs to a Track. A Track has many TimingEyes.
When I create a new Timesheet and select its Track (via a select menu), I need to dynamically display a group of checkboxes for choosing Timing Eyes that were activated that day. At the moment I can display all the timing eyes available in my database, however there are a dozen tracks each with several timing eyes. This is way too much information on the new Timesheet form.
Is there a way I can limit the group of Timing Eyes to the track_id chosen in my select menu? Would there be an advantage to making this a multistep form?
Multi step form wouldn't be a bad thing here and would remove complexity... if you want to stay with a single page form though, this sounds like a great time for some jquery.
Use .change to fire whenever the select box changes value.
When this fires use .get (ajax) to render your "checkbox" section html.
So a basic boilerplate would look something like this:
$("#trackselect").change(function () {
$.get('yoururl', function(data) {
$('#checkboxsection').html(data);
});
})
With this setup you need to define a url/page that outputs the checkboxes html for that particular track. Maybe something like (http://yourdomain.com/tracks/:id/checkboxes).
Hope this helps

How can I re-populate a list in a <div> after adding an item to it using AJAX?

Specifically, I have a number of pages in my Rails app that use the same partial. In the action handler for each page I create an array object (e.g. #list_elements) based on a database query. Each page uses a different query so that each page has different list elements in it. At the top of each page I have a form_remote_tag containing an edit field, allowing the user to add a new element in a dynamic, AJAXy fashion (think something like Twitter 'What's happening' box).
My problem is that when the AJAX command fires I need to reload the list to include the newly added item, but the contents of the list were determined by a database query. I need to remember which query applies to the current page (i.e. controller action) so that I can run it again. I thought about storing something in the rails session structure but it seems like overkill - it's like storing the current page all the time.
Anybody done anything like this and have a nice Railsy way to achieve it?
Ben
Couldn't you just re-render the partial in your rjs template?
page[:div_element].replace_html :partial => 'partial'
If you perform the query and define the array in the controller action, then an ajax call will refresh that array.

What is the best way to handle repeating forms in MVC?

The best public example that I can think of off the top of my head would be the amazon shopping cart. Where you have a page that displays multiple distinct records that can have multiple distinct fields updated.
I can't put each one in a form tag because the user may modify more than one record and then submit.
I can't just update all the records that I get back because:
1. Performance
2. Auditing
3. If someone changed the record that the user 'didn't change' when they were viewing the page and then the user submits those changes would be overwritten.
So how to best handle getting the data back and then getting which records where changed out of that?
Is that clear?
Use binding! Don't be iterating the form collection in your actions.
Steve Sanderson wrote a blog post about how to do it. I wrote a blog post on how to do it with MvcContrib.FluentHtml. Both posts are very detailed and include downloadable code.
Generate your form in a repeater, and append an ID to the form elements that increments with each new form. Save the number of repeated form elements in a hidden field. Then in your controller, read the value of this hidden field - that'll be the number of forms to read. Then, in a loop, retrieve each form's fields by specifying the name of the field, plus the loop index appended to the name, as the key.
You can use some javascript logic to detect when a form's value changes, and update a hidden field in that form's section if that occurs; or you can hide the original values inside a hidden field with each form section (although I don't recommend this as too many fields / forms will bloat your page).
one (but not necessarily the best) approach is to store which items are changed in a js-variable or something on the client side as they are changed, and then only send the data that is actually different from what the user recieved.
and as Erik stated, you could use hidden form elements to make sure that it works without js as well.

Resources