How to properly bind javafx label (from fxml) to SimpleStringProperty - javafx

I've got Controller that handles actions from UI created by a Scene Builder.
Now when i'm trying to bind a label from this UI to a SimpleTextProperty inside a Controllers constructor i'm getting nullpointer exception. Apparently productInfoLabel is not instantiated yet. Where should i bind these in instead a constructor?
Here's my code
public class Controller {
#FXML
public TableView receiptTable;
#FXML
public TextField productCodeTextField;
#FXML
public Label productInfoLabel;
private StringProperty stringProperty = new SimpleStringProperty();
public Controller() {
productInfoLabel.textProperty().bind(stringProperty);
}
}
So my question is how to do it properly?

All Fields that are annotated by #FXML will be injected in the controller instance that is created by the FXMLLoader. In the constructor you can't use the Fields because the values are not injected at this moment. Use the initalize methods as shown here: http://docs.oracle.com/javafx/2/api/javafx/fxml/Initializable.html

Related

JavaFx with Google Guice gives two different instances of Controller

I introduced Google Guice to my JavaFx applicatiomn. However I have some problem when I try to #Inject my controller into another controller.
First of all I do:
loader.setControllerFactory(Main.getInjector()::getInstance);
MainController has TopMenuButtonsController included in fxml file:
<fx:include fx:id="topMenuButtons" source="TopMenuButtons.fxml" />
Then I try to load my MainController using FXMLLoader with Guice Controller Factory. TopMenuButtons are initialized automatically because it's included to MainController.
Finally I try to inject MainController into TopMenuButtonsController (because buttons control what to display in MainController:
public class TopMenuButtonsController {
private MainController mainController;
#Inject
public void setMainController(MainController mainController) {
this.mainController = mainController;
}
#FXML
public void onCreateOrder(ActionEvent event) {
mainController.setCenter(MainController.CREATE_ORDER_FXML);
}
It succeeds but the instance injected here has all field set to null (#FXML annotation didn't work). I also see that this instance injected here is some different one than I use (different object id)
I can simply make it work by doing this in MainController:
#FXML
private void initialize() {
topMenuButtonsController.setMainController(this);
}
but my intention was to get rid of such things and use DI. What I can do?

Null exception when I try to access a controller that I passed using JavaFX

I have a root layout with multiples Tabs. From my main App I open the root layout. There I included multiple FXMLs with their own controllers.
I am trying to pass the main controller to one of the Tabes controller.
The issue I am having, everything works as expected, but I get a null exception when I try to click on an action button from the new tab.
RootLayout FXML
<fx:indlue fx:id="myNewTabAnchorPane" source="NewTabFXML.fxml"/>
RootLayout Controller
#FXML NewTabController newTabController;
mainTabPane.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Tab>(){
#override
public void change(ObservableValue<? extends Tab> observable, Tab oldValue, Tab newValue){
if(newValue == myTab){
newTabController.setMyRootController(this);
}
NewTabController
public void setMyRootController(RootController rootController){
this.rootController = rootController;
System.out.println(rootController.getID); // this prints fine
}
However, if I trigger this action I get blank from the same controller
#FXML
public void createAction(ActionEvent event) throws IOException{
System.out.println(rootController.getID); // with this I get null value.
}
What am I missing?
Here is the problem:
#FXML NewTabController newTabController;
It should be myNewTabAnchorPaneController which is not a partially lowercased class name, but fx:id + Controller concatenation.

JavaFx: Static fields

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.

How to call several controls when creating tabs dinamically

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.

#FXML public static vars in javafx? [duplicate]

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

Resources