title: TableCell.setText(String) doesn't set the data value associated with the cell
body: <p>In my particular case I have a custom implementation of a <code>TableCell</code> that contains a <code>Button</code>. This button invokes a method that returns a String to be displayed instead of the button. The visual change is done by setting the graphic in the cell to null and setting the text to the String, using <code>TableCell.setText(String)</code>. </p>
  
  <p>What I've realized - and worked around so far, is that <code>TableCell.setText(String)</code> doesn't change the data value associated with the cell in the <code>TableView</code>. It just changes the visual representation of the cell. The underlying data structure is in my case a <code>ObservableList&lt;String&gt;</code> that represents a row, and each element in the list is, of course, cell data.</p>
  
  <p>My current solution is to set the underlying value doing this:</p>
  
  <pre><code>getTableView().getItems().get(getIndex()).set(getTableView().getColumns().indexOf(getTableColumn()), "Value");
  </code></pre>
  
  <p>And this works fine. But I mean, the code is barely readable.</p>
  
  <p>It seems like the data in the <code>TableView</code> and the <code>TableCell</code> are entirely separated, since you need to access the <code>TableView</code> to set the underlying data for a cell. There is a <code>TableCell.getItem()</code> to get the data value, but there's no <code>setItem(String)</code> method to set it.</p>
  
  <p>I hope I explained my issue good enough.</p>
  
  <p>Is there a better and prettier way to do this? Why doesn't just `TableCell.setText(String) change the data value as well?</p>
  
  <p>Edit: I'll explain what I am trying to implement:</p>
  
  <p>I basically have a table where one column contains a button that will load some arbitrary data to the column when pressed. Once the data has been loaded, the button is removed from the column and the data is displayed instead. That is basically it. This works fine unless the table is sorted/filtered. Here's a MCVE of my implementation:</p>
  
  <pre><code>import javafx.animation.Animation;
  import javafx.animation.KeyFrame;
  import javafx.animation.Timeline;
  import javafx.application.Application;
  import javafx.beans.property.BooleanProperty;
  import javafx.beans.property.SimpleBooleanProperty;
  import javafx.beans.property.SimpleStringProperty;
  import javafx.collections.FXCollections;
  import javafx.collections.ObservableList;
  import javafx.concurrent.Task;
  import javafx.scene.Scene;
  import javafx.scene.control.Button;
  import javafx.scene.control.TableCell;
  import javafx.scene.control.TableColumn;
  import javafx.scene.control.TableView;
  import javafx.stage.Stage;
  import javafx.util.Callback;
  import javafx.util.Duration;
  
  public class MCVE extends Application {
      private final BooleanProperty countLoading = new SimpleBooleanProperty(this, "countLoading", false);
  
      @Override
      public void start(Stage stage) {
          int numOfCols = 3;
  
          ObservableList&lt;ObservableList&lt;String&gt;&gt; tableData = FXCollections.observableArrayList();
  
          // Generate dummy data.
          for (int i = 0; i &lt; 100; i++) {
              ObservableList&lt;String&gt; row = FXCollections.observableArrayList();
  
              for (int j = 0; j &lt; numOfCols; j++)
                  row.add("Row" + i + "Col" + j);
  
              tableData.add(row);
          }
  
          TableView&lt;ObservableList&lt;String&gt;&gt; table = new TableView&lt;ObservableList&lt;String&gt;&gt;();
  
          // Add columns to the table.
          for (int i = 0; i &lt; numOfCols; i++) {
              if (i == 2) {
                  final int j = i;
                  table.getColumns().add(addColumn(i, "Column " + i, e -&gt; new QueueCountCell(j, countLoading)));
              } else {
                  table.getColumns().add(addColumn(i, "Column " + i, null));
              }
          }
  
          table.getItems().addAll(tableData);
  
          Scene scene = new Scene(table);
  
          stage.setScene(scene);
          stage.show();
      }
  
      /**
       * Returns a simple column.
       */
      private TableColumn&lt;ObservableList&lt;String&gt;, String&gt; addColumn(int index, String name,
              Callback&lt;TableColumn&lt;ObservableList&lt;String&gt;, String&gt;, TableCell&lt;ObservableList&lt;String&gt;, String&gt;&gt; callback) {
          TableColumn&lt;ObservableList&lt;String&gt;, String&gt; col = new TableColumn&lt;ObservableList&lt;String&gt;, String&gt;(name);
          col.setCellValueFactory(e -&gt; new SimpleStringProperty(e.getValue().get(index)));
  
          if (callback != null) {
              col.setCellFactory(callback);
          }
          return col;
      }
  
      public static void main(String[] args) {
          launch();
      }
  
      class QueueCountCell extends TableCell&lt;ObservableList&lt;String&gt;, String&gt; {
          private final Button loadButton = new Button("Load");
  
          public QueueCountCell(int colIndex, BooleanProperty countLoading) {
  
              countLoading.addListener((obs, oldValue, newValue) -&gt; {
                  if (newValue) {
                      loadButton.setDisable(true);
                  } else {
                      if (getIndex() &gt;= 0 &amp;&amp; getIndex() &lt; this.getTableView().getItems().size()) {
                          loadButton.setDisable(false);
                      }
                  }
              });
  
              final Timeline timeline = new Timeline(new KeyFrame(Duration.ZERO, e -&gt; setText("Loading .")),
                      new KeyFrame(Duration.millis(500), e -&gt; setText("Loading . .")),
                      new KeyFrame(Duration.millis(1000), e -&gt; setText("Loading . . .")),
                      new KeyFrame(Duration.millis(1500)));
  
              timeline.setCycleCount(Animation.INDEFINITE);
  
              loadButton.setOnAction(e -&gt; {
                  new Thread(new Task&lt;String&gt;() {
                      @Override
                      public String call() throws InterruptedException {
                          // Simlute task working.
                          Thread.sleep(3000);
                          return "5";
                      }
  
                      @Override
                      public void running() {
                          setGraphic(null);
                          timeline.play();
                          countLoading.set(true);
                      }
  
                      @Override
                      public void succeeded() {
                          timeline.stop();
  
                          countLoading.set(false);
  
                          setText(getValue());
                      }
  
                      @Override
                      public void failed() {
                          timeline.stop();
  
                          countLoading.set(false);
  
                          setGraphic(loadButton);
                          setText(null);
  
                          this.getException().printStackTrace();
                      }
                  }).start();
              });
          }
  
          @Override
          public final void updateItem(String item, boolean empty) {
              super.updateItem(item, empty);
  
              if (item == null || empty) {
                  setGraphic(null);
              } else {
                  setGraphic(loadButton);
              }
          }
      }
  }
  </code></pre>