Draggable list with propel, slim, and ajax

... by Bittle in Slim 3 Framework February 24, 2019

Problem:

Make a program that has draggable <li> objects, that when dragged changes its state in the database.

Solution:

Your database needs an attribute to sort by, I will do this with placement_index


Make sure you include draggable.js, and jquery (ajax comes with the jquery bundle). Also install slim and Propel through composer installation. Once you have everything installed lets start on our view, mine only consists of the list and some css

<!DOCTYPE html>
<html>
   <head>
      <title>Sortable</title>
      <script src = "https://code.jquery.com/jquery-3.3.1.min.js"></script>
      <script src = "https://code.jquery.com/ui/1.10.4/jquery-ui.js"></script>

      <style>
         #sortable { list-style-type: none; margin: 0;
            padding: 0; width: 25%; }
         #sortable li { margin: 0 3px 3px 3px; padding: 0.4em;
            padding-left: 1.5em; font-size: 17px; height: 16px; }
         .default {
            background: #21a2f9;
         }
      </style>
   </head>

   <body>
      <ul id = "sortable">
          <?php foreach ($items as $item) { ?>
            <li data-id="<?=$item->getId()?>" class = "default"><?=$item->getText()?></li>
          <?php }?>
      </ul>
   </body>
</html>

Now we can call the backend with ajax (place a script section before your </body> tag)

<script>
         $(function() {
            $( "#sortable" ).sortable({
              update: function(item, ui) {
                item = $(ui.item);

                // make the call
                $.ajax({
                  url: "<?=$router->pathFor('post-list')?>",
                  method: 'post',
                  data: {
                    id: item.attr('data-id'),
                    new_index: item.index()+1
                  },
                  success: function(data) {
                    console.log(data);
                  }, done: function(data){
                    console.log(data);
                  }
                });

              }
            });
         });
      </script>

Now to get this working we must have initial data to pass to the front end (as you can see in the body of html posted above) and also be able to accept the post request from the ajax. This is where Slim and Propel come into play. Have a route to show the data

$app->get('/', function ($request, $response, $args) {
    return $this->view->render(
      $response,
      'home.php',
      ['router'=>$this->router, 'items'=>\ItemQuery::create()->orderByPlacementIndex()]
  );
});

And have a route to accept the ajax call and change the placementIndex value in the database.

$app->post('/post-list', function ($request, $response, $args) {
    $post_params = $request->getParsedBody();
    $new_index = $post_params['new_index'];
    $items = \ItemQuery::create()->orderByPlacementIndex()->find();

    $index = 1;
    foreach ($items as $item) {
        if ($new_index == $index && $item->getId() != $post_params['id']) {
            // this is were the item is supposed to go, so just place the item
            // we are looking at in the next index
            $item->setPlacementIndex(++$index);
        }
        if ($item->getId() == $post_params['id']) {
            // found the item we were looking for
            $item->setPlacementIndex($new_index);
            if ($new_index <= $index) {
                // if we moved item back we have to increment the index to
                // push other elements one back
                $index++;
            }
        } else {
            // base case, just set index as incremental
            $item->setPlacementIndex($index++);
        }
    }

    $items->save();

    return $response->withJson(['return data here']);
})->setName('post-list');

And thats it! Move your list objects and check your database to see the magic happen.

Comments (0)

Search Here