I am trying to find a way to populate my tablePrice column with prices of specific menu items. The code shown below works fine but I had to create a price variable in the MenuItem class which was not previously there. MenuItem and PricedMenuItem, as well as other classes are generated from a UML domain model for a restaurant management system. This current method is discouraged as I am modifying the domain model.
The commented section shows how far I got with an error on the setCellValueFactory(). Is there a way for a TableView to contain columns of different classes? If so, can someone please assist me in filling in the column directly from the PricedMenuItem class?
MenuItem has a private name and itemCategory enum, as well as a getCurrentPricedMenuItem() method.
PricedMenuItem has a private price as well as a getPrice() method.
#FXML private TableView<MenuItem> tableView;
#FXML private TableColumn<MenuItem, String> tableName;
#FXML private TableColumn<MenuItem, Double> tablePrice;
#FXML private TableColumn<MenuItem, ItemCategory> tableCategory;
#Override
public void initialize(URL location, ResourceBundle resources) {
tableName.setCellValueFactory(new PropertyValueFactory<MenuItem, String>("Name"));
tableCategory.setCellValueFactory(new PropertyValueFactory<MenuItem, ItemCategory>("itemCategory"));
tablePrice.setCellValueFactory(new PropertyValueFactory<MenuItem, Double>("price"));
/*way to retrieve price directly from PMI
tablePrice.setCellValueFactory(new Callback<CellDataFeatures<MenuItem, String>, ObservableValue<String>>() {
#Override
public ObservableValue<String> call( CellDataFeatures<MenuItem, String> c) {
return new SimpleStringProperty(c.getValue().getValue().getCurrentPricedMenuItem().getPrice()+"");
}
});
*/
categoryDropDown1.getItems().setAll(ItemCategory.values());
categoryDropDown2.getItems().setAll(ItemCategory.values());
tableView.setItems(loadCurrentMenuItems());
updateBox("Select a menu item to edit.", Color.BLACK);
}
Declare tablePrice as
#FXML private TableColumn<MenuItem, Number> tablePrice;
(see JavaFX Properties in TableView for why).
Then the cell value factory should return a Property<Number> (yours is returning a Property<String>), so you need
tablePrice.setCellValueFactory(new Callback<CellDataFeatures<MenuItem, Number>, ObservableValue<Number>>() {
#Override
public ObservableValue<Number> call( CellDataFeatures<MenuItem, Number> c) {
return new SimpleDoubleProperty(c.getValue().getCurrentPricedMenuItem().getPrice());
}
});
or, using lambda expressions to get rid of most of the boilerplate code
tablePrice.setCellValueFactory(cellData ->
new SimpleDoubleProperty(cellData.getValue().getCurrentPricedMenuItem().getPrice()));
(I'm pretty sure you should not have two getValue() calls in there... but you didn't post your model classes.)
Related
Can TableView commit changes to generic fields like String or Integer without extra step with setOnEditCommit?
Currently I use such structure:
#FXML
TableColumn<User, String> _tableStateUsername;
#Override
public void initialize(URL location, ResourceBundle resources) {
_tableStateUsername.setCellFactory(TextFieldTableCell.<User, String>forTableColumn(new DefaultStringConverter()));
_tableStateUsername.setCellValueFactory(new PropertyValueFactory<User, String>("username"));
_tableStateUsername.setOnEditCommit(new EventHandler<TableColumn.CellEditEvent<User, String>>() {
#Override
public void handle(TableColumn.CellEditEvent<User, String> event) {
event.getRowValue().setUsername(event.getNewValue());
}
});
and it works fine, but I feel that it can be simplified (without using SimpleString)
I tried binding Value factory to field but no luck:
_tableStateUsername.setCellValueFactory(cellData -> Bindings.select(cellData.getValue(), "username"));
Is there any way to make cell set NewValue to bound field automatically?
Could you explain me why eclipse want getMissionFromMissionController() to be static if i haven't annotated listView with the static word ?
Whenever i want to create this function i've got an error:
"Cannot make a static reference to the non-static method getMissionFromMissionController() from the type MainController"
LogPanelController:
public void printLog()
{
textLog.appendText(MainController.getMissionFromMissionController());
}
MainController:
public String getMissionFromMissionController() {
return MissionController.listView.getSelectionModel().getSelectedItem();
}
And the Missionontroller fields:
#FXML private MainController mainController;
#FXML private Label missionsLabel;
#FXML public ListView<String> listView;
#FXML private TextArea textArea;
Here is one of the problems:
return MissionController.listView.getSelectionModel().getSelectedItem();
You access the listView field as though it's static, and it's not - mind the upper case letter in the word MissionController, in this case you access a class, not the missionController field, which I guess you implied.
And the second one: you call non static method as though it's static:
textLog.appendText(MainController.getMissionFromMissionController());
Mind the upper case letter of the MainController in this line. It should be a small one, if you probably try to access the field.
I am creating tabs dinamically and i am using the same fxml file for all of them. So, all type of controls that I've included has de same "fx:id". I use this: "#FXML TextField textField". How could I use the TextField of the second tab, the TextField of the first tab, etc?
#Controller
public class MascotaTabControllerImpl implements MascotaTabController
{
private AnchorPane anchorPane;
private Tab tab;
private Mascota mascota;
#FXML
private ComboBox<String> comboMascota;
#FXML
private ComboBox<String> comboTamano;
#FXML
private TextField fieldNombreMascota;
#FXML
private RadioButton radioAlergiaSi;
#FXML
private RadioButton radioAlergiaNo;
#FXML
private TextField fieldRaza;
#FXML
private TextField fieldPeso;
#FXML
private ComboBox<String> comboSexo;
#FXML
private ComboBox<String> comboAgresividad;
#FXML
private TextArea areaObservaciones;
#FXML
private Button buttonEditar;
#FXML
private Button buttonCancelar;
#Override
public void inicializacionFxmlFile(TabPane tabPane, Collection<Mascota> mascotas)
{
try
{
for(Mascota mascota : mascotas)
{
anchorPane = new AnchorPane();
FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/mascotaTab.fxml"));
loader.setController(this);
anchorPane.getChildren().setAll(loader.load());
tab = new Tab();
tab.setContent(anchorPane);
tabPane.getTabs().add(tab);
tab.setText(mascota.getNombre());
fieldNombreMascota.setText(mascota.getNombre());
fieldRaza.setText(mascota.getRaza());
comboSexo.setValue(mascota.getSexo());
fieldPeso.setText(String.valueOf(mascota.getPeso()));
comboTamano.setValue(mascota.getTamano());
comboAgresividad.setValue(mascota.getAgresividad());
areaObservaciones.setText(mascota.getObservaciones());
mascota.setNombre(fieldNombreMascota.getText());
}
tabSelected(tabPane, mascotas);
buttonEditar.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent event)
{
mascota.setNombre(fieldNombreMascota.getText());
mascota.setRaza(fieldRaza.getText());
mascota.setSexo(comboSexo.getValue());
mascota.setPeso(Float.parseFloat(fieldPeso.getText()));
mascota.setTamano(comboTamano.getValue());
mascota.setAgresividad(comboAgresividad.getValue());
mascota.setObservaciones(areaObservaciones.getText());
clienteService.actualizarMascota(mascota);;
}
});
buttonCancelar.setOnAction(new EventHandler<ActionEvent>()
{
#Override
public void handle(ActionEvent event)
{
return;
}
});
}
catch(IOException ioe)
{
System.out.println(ioe.getMessage());
}
}
Edit: imagine "for" statement runs twice. How could call controls (TextFields, ComboBoxes, etc.) of the first tab?
For example, when "for" statement runs the first time "fieldNombreMascota" reference is "id=110" but when "for" statement runs the second time, the same TextField reference is "id=113". So how can I call them with annotation #FXML?
Because you call
loader.setController(this);
inside your loop, every time you call loader.load() you will reuse the same object as the controller for the FXML you are loading. This means each time you go through that for loop, the #FXML-injected fields will get redefined. So by doing this, you essentially make it impossible to access any FXML fields except the ones from the last iteration of the loop.
You must not call loader.setController(this). The "standard" way to combine FXML files and controllers is to use an fx:controller attribute in the root element of the FXML file, specifying a class name. This will cause the FXMLLoader to create a new instance of that class every time you call loader.load(). If there is some other problem you are trying to solve by attempting the non-standard way you are doing things, then the solution you are attempting is not the right approach.
If you organize your code well, the FXML creates a controller instance, and there should be no need whatsoever to access that controller externally. You may need some of your controllers to access a shared data model of some kind, but this is certainly not the right approach to achieve that.
I'm making a program to manage and show data about airports, their flights and so on.
The fact is that I have a tableView (in javafx) with several tableColumns, and I want to show some information (destiny, origin, company, etc) on each column so I typed this:
#FXML
private TableColumn<Flight, String> destinoCol;
#FXML
private TableColumn<Flight, String> numCol;
#FXML
private MenuButton aeropuerto;
#FXML
private MenuButton tipo;
#FXML
private Button filtrar;
#FXML
private TableColumn<Flight, LocalTime> horaCol;
#FXML
private Button este;
#FXML
private DatePicker fecha;
#FXML
private TableColumn<Flight, String> origenCol;
#FXML
private Label retrasoLabel;
#FXML
private ImageView companiaImg;
#FXML
private VBox detalles;
#FXML
private Button todos;
#FXML
private ImageView avionImg;
#FXML
private Label tipoLabel;
private mainVuelos m;
private List<Airport> aeropuertos;
private Data data;
#FXML
void initialize() {
data = Data.getInstance();
aeropuertos = data.getAirportList();
List<MenuItem> ItemAeropuertos = new LinkedList<MenuItem>();
for (int i = 0; i < aeropuertos.size(); i++) {
MenuItem item = new MenuItem(aeropuertos.get(i).getName());
item.setOnAction((event) -> cambiarAer(event));
ItemAeropuertos.add(item);
}
aeropuerto.getItems().setAll(ItemAeropuertos);
destinoCol.setCellValueFactory(cellData -> cellData.getValue().getDestiny());
}
The method getDestiny(), as it says returns the destiny of a specific flight as a String so obviously I cannot use the last instruction, it says
cannot convert from String to ObservableValue<String>
but I don't really know how to solve it in order to be able to show the destinies on that column.
According to the Javadocs, setCellValueFactory(...) expects a Callback<CellDataFeatures<Flight, String>, ObservableValue<String>>, i.e a function that takes a CellDataFeatures<Flight, String> as its parameter, and results in an ObservableValue<String>.
As the error message says, your function evaluates to a String (cellData.getValue().getDestiny()), which is not the correct type.
You have two choices, depending on your actual requirements.
Either you can create something on the fly that is of the correct type: the easiest thing to use is a ReadOnlyStringWrapper:
destinoCol.setCellValueFactory(cellData -> new ReadOnlyStringWrapper(cellData.getValue().getDestiny()));
This will display the correct value, but won't be nicely "wired" to the property of the flight object. If your table is editable, edits won't automatically propagate back to the underlying object, and changes to the underlying object from elsewhere won't automatically update in the table.
If you need this functionality (and this is probably a better approach anyway), you should implement your model class Flight to use JavaFX properties:
public class Flight {
private final StringProperty destiny = new SimpleStringProperty();
public StringProperty destinyProperty() {
return destiny ;
}
public final String getDestiny() {
return destinyProperty().get();
}
public final void setDestiny(String destiny) {
destinyProperty().set(destiny);
}
// similarly for other properties...
}
and then you can do
destinoCol.setCellValueFactory(cellData -> cellData.getValue().destinyProperty());
I am a bit late I think, but this might help others.
You can have cade as below
destinoCol.setCellValueFactory(cellData -> cellData.getValue().destinyProperty().asObject());
This code will work for property other than string, as I had problem with "LongProperty".
This question already has an answer here:
javafx 8 compatibility issues - FXML static fields
(1 answer)
Closed 8 years ago.
I'm introducing in javafx and i found it very interesting. But I have got a problem that i can't solve.
In the simplest case of a Hello World I can't put a #FXML public static var like this:
public class FXMLDocumentController implements Initializable
{
#FXML
public static Label label;
#FXML
private void handleButtonAction(ActionEvent event)
{
System.out.println("You clicked me!");
label.setText("Hello World!");
}
}
If I change it to private it works.
The cause of that I want to made this vars publics is because I'm using diferents controllers for diferents views (in my real app) and i want to communicate between theirs.
PS: Sorry for my bad english
You should not use a static field here. Your controller belongs to one view and every time the view is created by the FXML Loader new instances of all nodes in the view will be created. SO by calling the FXML Loader 2 times you will receive 2 instances of the view. In addition a new instance of your controller class will be created whenever you load the view by using the FXML viewer. By using a static field you will override values of old controller instances and this will end in horrible bugs. Here is a short introduction to the static keyword: What does the 'static' keyword do in a class?
If you just remove "static" it will work.
Instead of using public fields you should add getter and setter methods to your controller class and mark the field as private: Why use getters and setters?
I want to do something similar like this:
I'm working with tabs and i have this two controllers:
FXML_Tab1Controller.java:
public class FXML_Tab1Controller implements Initializable {
FXML_Tab2Controller tab2controller;
#FXML public Label Label1;
#FXML public TextField TextField1;
#FXML public Button Button1;
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
#FXML private void actionButton1(ActionEvent event)
{
Label1.setText(tab2controller.TextField2.getText());
}
}
FXML_Tab2Controller.java:
public class FXML_Tab2Controller implements Initializable {
FXML_Tab1Controller tab1controller;
#FXML public Label Label2;
#FXML public TextField TextField2;
#FXML public Button Button2;
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
#FXML private void actionButton2(ActionEvent event){
Label2.setText(tab1controller.TextField1.getText());
}
}
something similar like that video:
https://www.youtube.com/watch?v=XLVx46ycxco