title: JavaFX: Best way to update GridPane after row deletion?
body: <p>I am using a GridPane (inside a ScrollPane) as a table with user entries. 
  Each row incorporates a username (Label), an icon depicting its state (ImageView) and two buttons, the second of which is used to delete the user entry.
  The two images below depict the scroll pane before and after the user entry deletion</p>
  
  <p><img src="https://i.stack.imgur.com/n8kUF.png" alt="GridPane before row (user entry) deletion"></p>
  
  <p><img src="https://i.stack.imgur.com/u77Xg.png" alt="GridPane after row (user entry) deletion"></p>
  
  <p>UPDATE: Code sample below:</p>
  
  <p>The obvious issue is that the GridPane items do not get automatically re-arranged and the deleted line still occupies space.
  Do I have to provide a method to manually make the re-arrangement or is there an easier way to go about it?</p>
  
  <pre><code>for (NonSudoUser nsuser : nonSudoUsers) { // the users I want to show in the gridpane
      // user row
      RowConstraints row = new RowConstraints(30);
      gridPaneNonSudoUsers.getRowConstraints().add(row);
  
      // code that adds username label, status icon, checkstatus button - irrelevant I think to our issue
      // adding delete user button now:
      Button btnDeleteUser = new Button("Delete User");
      btnDeleteUser.setId("btnDel"+username); // explained below
      // delete user in a seperate thread
      btnDeleteUser.setOnAction(new EventHandler&lt;ActionEvent&gt;() {
          @Override
          public void handle(ActionEvent event) {
          // confirmation dialog here - omitted for brevity               
              Task&lt;Void&gt; task = new Task&lt;Void&gt;() {
              @Override
                  public Void call() throws Exception {
                      String username = ((Button) event.getSource()).getId();
                      LinuxCommand lc = new LinuxCommand("userdel", "-r", username);
                      lc.execute();
                      return null;
                      }
                  };
                  task.setOnFailed(new EventHandler&lt;WorkerStateEvent&gt;() {
                      public void handle(WorkerStateEvent t) {
                          System.out.println("FAILURE deleting  " + username);
                      }
                  });
                  task.setOnSucceeded(new EventHandler&lt;WorkerStateEvent&gt;() {
                      public void handle(WorkerStateEvent t) {
                          Platform.runLater(new Task&lt;Void&gt;() {
                              @Override
                              public Void call() throws Exception {
                                  ArrayList&lt;Node&gt; toErase = new ArrayList&lt;Node&gt;();
                                  for (Node nodeToErase: gridPaneNonSudoUsers.getChildren()) {
                                      if (nodeToErase.getId().endsWith(username)) // explaind below
                                          toErase.add(nodeToErase);
                                  }
                                  gridPaneNonSudoUsers.getChildren().removeAll(toErase);                                  
                                  return null;
                              }
                          });
                      }
                  });
                  executorUserDeletion.submit(task);
  </code></pre>
  
  <p>The line that checks weather an element id contains the username is to know which elements to delete (upon elements addition, I append the actual username. So the GridPane row containing e.g. testuser0 entry, is composed of elements with the following ids:
  lbltestuser0, imgtestuser0, btnCheckStatustestuser0, btnDeleteUsertestuser0.
  (just a heuristic to know which row to delete each time)
  LinuxCommand is a custom class that accepts vararg String in its constructor to run the respective command.</p>