Drag and Drop question

689 views
Skip to first unread message

John Sanderbeck

unread,
Jul 19, 2019, 10:48:56 AM7/19/19
to Ruby on Rails: Talk
I have an app in production that I am adding a module to for Lunch Ordering

What I have is a Menu, which has Meals, and the Meals have Meal Items in Categories like Entree, Vegetable, Fruit, and Drink

What I would like to do it allow Drag and Drop to build meals, and Drag and Drop to build Menus

I made a standard Rails form with a Drag and Drop area for the meal with the items as draggable and sorted in groups on the right....

I don't think I need an Ajax call as I am building or editing the meal. What I need to be able to do is manipulate the array that gets passed back for the Meal Items...

Any suggestions or links I can look at to do this?

John Sanderbeck

Untitled picture.png


Walter Lee Davis

unread,
Jul 19, 2019, 4:56:59 PM7/19/19
to rubyonra...@googlegroups.com
What happens now when you drop the element? Do you see anything in your browser's JavaScript console indicating that the drop event fired? Did you do anything beyond CSS (to make it look a particular way) to define the draggable and droppable behaviors for these elements? Do the draggable elements have unique IDs or data-attributes or some other sort of identification scheme?

Here's where I would start looking for more information: https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API

Walter
> <Untitled picture.png>
>
>
>
> --
> You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to rubyonrails-ta...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/rubyonrails-talk/f72ba618-4da8-4e4b-a261-5d39b233bc80%40googlegroups.com.
> <Untitled picture.png>

John Sanderbeck

unread,
Jul 19, 2019, 5:11:30 PM7/19/19
to Ruby on Rails: Talk
Sorry, I should have elaborated a little more Walter...

I implemented Interact.js and used an example from a Drifting Ruby Episode...  https://www.youtube.com/watch?v=fhnHA7PWq0g

Dragging is working but drop doesn't "look" to be firing...

My form code currently is like this

<script>
    var dragMoveListener;

    dragMoveListener = function(event) {
        var target, x, y;
        target = event.target;
        x = (parseFloat(target.getAttribute('data-x')) || 0) + event.dx;
        y = (parseFloat(target.getAttribute('data-y')) || 0) + event.dy;
        target.style.webkitTransform = target.style.transform = 'translate(' + x + 'px, ' + y + 'px)';
        target.setAttribute('data-x', x);
        return target.setAttribute('data-y', y);
    };

    window.dragMoveListener = dragMoveListener;

    interact('*[data-draggable="true"]').draggable({
        inertia: false,
        autoScroll: true,
        onmove: dragMoveListener
    });


    $(document).on('load', function(){
        interact('#meal_items').dropzone({
            accept: '*[data-draggable="true"]',
            overlap: 0.75,
            ondropactivate: function(event) {},
            ondragenter: function(event) {
                event.target.classList.add('drop-target');
                event.relatedTarget.classList.add('can-drop');
                return $.get(event.relatedTarget.attributes['data-url'].value, {
                    favorite: true
                });
            },
            ondragleave: function(event) {
                event.target.classList.remove('drop-target');
                event.relatedTarget.classList.remove('can-drop');
                return $.get(event.relatedTarget.attributes['data-url'].value, {
                    favorite: false
                });
            },
            ondrop: function(event) {},
            ondropdeactivate: function(event) {
                event.target.classList.remove('drop-active');
                return event.target.classList.remove('drop-target');
            }
        });

    });
</script>
<% if action_name == "new" %>
  <div class="page-title">Creating New Meal</div>
<% else %>
  <div class="page-title">Editing Meal</div>
<% end %>

<div>
<div class="col-sm-4 col-md-4">
<%= form_for @meal do |f| %>
<% if @meal.errors.any? %>
<div id="error_explanation">
<h3><%= pluralize(@meal.errors.count, 'error') %> prohibited this meal from being saved:</h3>

<ul>
<% @meal.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>

<div>
<table>
<tr>
<th align='left'>Name:</th>
<td><%= f.text_field(:name) %></td>
</tr>
<tr>
<th align='left'>Description:</th>
<td><%= f.text_area(:description) %></td>
</tr>
<tr>
<th align='left'>Available Daily:</th>
<td><%= f.check_box(:available_daily, 'data-toggle' => "toggle", 'data-size' => "mini", 'data-on' => "Yes", 'data-off' => "No", 'data-style' => "ios") %></td>
</tr>
<tr>
<th align='left'>Not Available:</th>
<td><%= f.check_box(:not_available, 'data-toggle' => "toggle", 'data-size' => "mini", 'data-on' => "Yes", 'data-off' => "No", 'data-style' => "ios") %></td>
</tr>
<tr>
<th align='left'>Meal Items:</th>
<td>
<div id="outer-dropzone">
<div id="meal_items" class="dropzone">
                  <% unless @meal.mitems.nil? %>
                    <% @meal.mitems.each do |mealitem| %>
                      <%= content_tag :div, mealitem.name, class: 'drag-drop can-drop', data: { draggable: true, url: meal_path(mealitem) } %>
                    <% end %>
                  <% end %>
</div>
</div>
</td>
</tr>
</table>
<div class="form-submit-buttons">
<%= f.submit class: 'btn btn-success' %>
<%= link_to 'Cancel', meals_path, class: 'btn btn-danger' %>
</div>
</div>
<% end %>
</div>
<div class="col-sm-2 col-md-2 entrees">
<div class='meal_item_title'>Entrees</div>
<% Mitem.entrees.each do |food| %>
<%= content_tag :div,
                food.name,
                class: 'drag-drop entree_items',
                data: { draggable: true, url: mitem_path(food) } %>
<% end %>
</div>
<div class="col-sm-2 col-md-2 vegetables">
<div class='meal_item_title'>Vegetables</div>
<% Mitem.vegetables.each do |food| %>
<%= content_tag :div,
                food.name,
                class: 'drag-drop vegetable_items',
                data: { draggable: true, url: mitem_path(food) } %>
<% end %>
</div>
<div class="col-sm-2 col-md-2 fruits">
<div class='meal_item_title'>Fruits</div>
<% Mitem.fruits.each do |food| %>
<%= content_tag :div,
                food.name,
                class: 'drag-drop fruit_items',
                data: { draggable: true, url: mitem_path(food) } %>
<% end %>
</div>
<div class="col-sm-2 col-md-2 drinks">
<div class='meal_item_title'>Drinks</div>
<% Mitem.drinks.each do |food| %>
<%= content_tag :div,
                food.name,
                class: 'drag-drop drink_items',
                data: { draggable: true, url: mitem_path(food) } %>
<% end %>
</div>
</div>



Hasan Diwan

unread,
Jul 19, 2019, 6:25:43 PM7/19/19
to rubyonra...@googlegroups.com
Add a few Console.log lines to determine where it's dying? -- H

--
You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rubyonrails-ta...@googlegroups.com.


--
If you wish to request my time, please do so using bit.ly/hd1AppointmentRequest.
Si vous voudrais faire connnaisance, allez a bit.ly/hd1AppointmentRequest.

Sent from my mobile device
Envoye de mon portable

John Sanderbeck

unread,
Jul 20, 2019, 12:25:20 PM7/20/19
to Ruby on Rails: Talk
Ok. Changed to Document.ready and got the drag and drops firing...   

Also changed the data URl to be a data ID which is the ID of the meal item.

The part I am unsure of is assigning the mitem array that gets passed back in the form...

I know this should be simple and as you can tell I am a total newbie with this...  :-)

John

Walter Lee Davis

unread,
Jul 20, 2019, 8:59:21 PM7/20/19
to rubyonra...@googlegroups.com
I'm not sure how your form is constructed at the moment. But if you were passing the result of the dragged items back to a meals_controller, you could assign the array of values to `menu_item_ids` and the child menu items would be created and assigned. You'll have to ensure that you whitelist that attribute in a special way:

def meal_params
params.permit(:foo, :bar, :baz, menu_item_ids: [])
end

You'll want to put this (and any other array-shaped parameters) at the end of the list of permitted params, and you have to cast it to an array as I've done here.

If you are sending the form to the menus_controller, then you've got a doubly-nested assignment going on, and you'll probably want to use accepts_nested_parameters_for in your model relationship between menu and meal. I would do that if I was building N number of meals within one menu form. If you're trying to get this going first, I would just save each meal individually first, and then, once it's working at that level, try to further complicate it by nesting forms.

Walter
> --
> You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to rubyonrails-ta...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/rubyonrails-talk/f0ec2c68-3c7d-40db-85f4-52ff4ab3866c%40googlegroups.com.

John Sanderbeck

unread,
Jul 20, 2019, 9:24:59 PM7/20/19
to Ruby on Rails: Talk
I understand that part Walter.  I have done many nested forms. However, this is the first one with a drag and drop feature.

The part I am struggling with is how to populate the array that gets passed back through the drag and drop events.

I have the strong params setup and could easily just add a simple nested form, but I wanted to add the drag and drop as a feature so they could build the meals easily with an IPad.

John
> To unsubscribe from this group and stop receiving emails from it, send an email to rubyonra...@googlegroups.com.

Walter Lee Davis

unread,
Jul 21, 2019, 5:40:55 PM7/21/19
to rubyonra...@googlegroups.com
You'll have to assign the values you get back (in JavaScript) to a properly-named form element in your form. Try making a hidden input in the form with the name set to meal[meal_item_ids]. Then locate that field in JavaScript in your callback function (which fires after the drop event) and set the value of that hidden field to the array of values you get in your callback.

Walter
> To unsubscribe from this group and stop receiving emails from it, send an email to rubyonrails-ta...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/rubyonrails-talk/faf99154-a737-4d91-a7e9-95b973e7ed69%40googlegroups.com.

te...@datatravels.com

unread,
Aug 3, 2019, 4:10:47 PM8/3/19
to rubyonra...@googlegroups.com

Sounds like you need some Javascript. 

You should be able to use Javascript to manipulate a hidden field that will contain an array of items, or their ids. You will need to get your hands dirty with Javascript, I see no other "DOM-oriented" way to do it. 

if you're not opposed to Jquery I'd just recommend jQuery UI's draggable which implements many hooks that will be good for you


-Jason

John Sanderbeck

unread,
Aug 4, 2019, 9:08:21 AM8/4/19
to Ruby on Rails: Talk
Thank you all...  I got the drag and drop working but ran into many other complications from the drag and drop and decided it was too complicated for what I was trying to achieve...

Snap to positions, alignment of objects, and on and on.  Looked cool but not worth the extra code...  :-) 

John

On Friday, July 19, 2019 at 10:48:56 AM UTC-4, John Sanderbeck wrote:
Reply all
Reply to author
Forward
0 new messages