0

I'm developing the front-end of an app to track tournaments. In one page, the user needs to update the tournament result by dragging a player from a list on the left inside the final results table on the right, that I'll send to a PHP backend for processing (I'm not developing the backend, just need to send the correct data via POST). My requirements are:

  1. Have a list of players
  2. Have a results table with fixed positions in each row (one row for 1st, one row for 2nd).
  3. Each position will have 4 related inputs, all named position-1-NAME, in the same row, but different columns
  4. The user should be able to drag players into each position
  5. One player can only be in one position, and each position can only have one player
  6. When a player is placed in the table, he should be removed from the list

I've tried several approaches but the one that came close is using the Dragula JS plugin.

Then, I create the list of players with an ul with the id to make it a container in Dragula:

 <ul id="players-container">
   <li>
     Player 1
   </li>
 </ul>

Then, I have a table inside a form tag, and the name of the fields are all named according to the position, like position-1-name-of-the-input:

<form method="POST" action="file_to_process_form.php">
  <thead>
   <th>Position</th>
   <th>Player</th>
   ...
  </thead>
  <tbody>
     <tr>
       <td>
         1st Position
       </td>           
       <td class="target">
         <input type="text" name="position-1-player-name" value="" hidden>
       </td>
       <td>
         ...other inputs 
       </td>
     </tr>
  </tbody>

Then, in JavaScript, I made a hack to grab the text() from the li being dragged and put it inside the value attribute of the input of the table, so that I can send the name of the player in the first position to the backend. The code:

dragula([document.querySelector('#players-container'), document.querySelector('.target')], {
  isContainer: function(el){
    return el.classList.contains('target');
  },
  revertOnSpill: true
}).on('drop', function(el, target, source, sibling){
  elValue = $(el).text();
  $(target).find('input').attr('value', elValue);
});

So far, this is working with some bugs:

  1. I can drop two players in the same td, and they will show both dragged li in the UI, but the form will only have the value of the last dropped player.

  2. If I drag the player into all positions, one after another, all inputs will have the value of his name until I drag another player into that position.

So, I want to accomplish a couple of things:

  1. Allow only one player inside each td class="target". I've tried to disable the td by removing the .target class but then I can't move the player again after being dropped.

  2. Remove the value of the input when I drag the player out.

I know this is a bit of a hack but I don't have a lot of experience with JS/jQuery and this is the only solution that remotely works so far.

My questions:

  1. How can I prevent multiple players form being dragged to the same td with Dragula or JS / jQuery code?

  2. [SOLVED -- see edit] How can I remove the value of the input when I drag a player OUT of the td?

  3. Do you know a better way to implement these requirements to recommend?

EDIT

I've managed to solve the question 2 by adding the following code:

dragulaObject.on('drag', function(el, source) {
   $(source).find('input').attr('value', '');
});
Hugo Carlos
  • 401
  • 3
  • 22

1 Answers1

0

So, after I posted the question I managed to hack away some solutions to the bugs/questions 1 and 2:

To prevent more than one player on the same position, I added the following code to the options in the dragula object initialization:

accepts: function(el, target, source, sibling) {
    if($(target).attr('class') == 'target' && $(target).find('li').length <= 1) {
      return true;
    } else {
      return false;
    }
  }

The first condition is there to allow the player to be dragged back to the list after being put in one position, since the list contains more than 1 li and, without the first condition, would invalidate the return to list drag.

There are still some animation bugs (the player being dragged briefly shows inside the position with another player, but the drag is not completed when dropped).

To solve bug/question 2, I added the following code (the same as edited in the original answer):

dragulaObject.on('drag', function(el, source) {
   $(source).find('input').attr('value', '');
});

So, the full code for the Dragula object is:

  dragula([document.querySelector('#players-container'), document.querySelector('.target')], {
      isContainer: function(el){
        return el.classList.contains('target');
      },
      revertOnSpill: true,
      accepts: function(el, target, source, sibling) {
        if($(source).attr('class') == 'target' || $(target).find('li').length <= 1) {
          return true;
        } else {
          return false;
        }
      }
  }).on('drop', function(el, target, source, sibling){
    elValue = $(el).text();
    $(target).find('input').attr('value', elValue);
  }).on('drag', function(el, source) {
    $(source).find('input').attr('value', '');
  });

Not the prettiest code, but it did solve the problem. Hope it helps others trying to do similar things.

Hugo Carlos
  • 401
  • 3
  • 22