TextField returning null - javafx

Every time I type some text in the textField and click the button, to trigger the save () method the text field returns null. What could be causing this?
#FXM
private TextField nome = new TextField();
public void save() {
Model model = new Model();
model.setNome(nome.getText());
model.setStatus(status.getSelectedToggle().isSelected());
}
public void initialize(URL location, ResourceBundle resources) {
ModelController modelController = new ModelController();
btnSalvar.setOnAction(event -> modelController.save());
border.setCenter(modelController.onScreen());
}

The problem is with this line:
#FXM
private TextField nome = new TextField();
When you do that you are replacing the reference to the object displayed on screen, to a new one you created. If you want to reference an object created through FXML simply use:
#FXML
private TextField nome;
the TextField in the FXML file should have an fx:id matching the name of your variable:
<TextField fx:id="nome" />

Related

Accessing the OK button from inside the dialog controller [javaFX]

I am trying to disable the OK button in a javaFX dialog untill all of the text fields have content.
Due to the ButtonType not having FXML support it has to be added to the Dialog in the Controller class of the main Window. due to this I'm unable to (cant find a way) to link the button to a variable inside the dialog controller.
I have tried handling the process in the main Controller class as follows:
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("addDialog.fxml"));
try {
dialog.getDialogPane().setContent(loader.load());
} catch(IOException e) {
e.getStackTrace();
}
dialog.getDialogPane().getButtonTypes().add(ButtonType.OK);
dialog.getDialogPane().getButtonTypes().add(ButtonType.CANCEL);
dialog.getDialogPane().lookupButton(ButtonType.OK).setDisable(true);
AddDialogController controller = loader.getController();
// below calls on a getter from the addDialogController.java file to check if the input fields are full
if (controller.getInputsFull()) {
dialog.getDialogPane().lookupButton(ButtonType.OK).setDisable(false);
}
unfortunately this didn't work, the above code can only be run once before or after the window is called and cant run during.
so is there a way to access the OK ButtonType that comes with javaFX inside the dialog controller if it has been declared outside?
Or is there another way to disable the button based of information from the dialog controller that is being updated by the user?
thanks for any help
Edit 1:
As requested the addDialogController, this is very bare bones and incomplete, hopefully it helps:
import data.Contact;
import data.ContactData;
import javafx.fxml.FXML;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
public class AddDialogController {
#FXML
private TextField firstNameField;
#FXML
private TextField lastNameField;
#FXML
private TextField numberField;
#FXML
private TextArea notesArea;
private boolean inputsFull;
public void processResults() {
String first = firstNameField.getText().trim();
String last = lastNameField.getText().trim();
String number = numberField.getText().trim();
String notes = notesArea.getText().trim();
Contact contact = new Contact(first, last, number, notes);
// ContactData.add(contact);
}
#FXML
public void handleKeyRelease() {
boolean firstEmpty = firstNameField.getText().trim().isEmpty() && firstNameField.getText().isEmpty();
boolean lastEmpty = lastNameField.getText().trim().isEmpty() && lastNameField.getText().isEmpty();
boolean numberEmpty = numberField.getText().trim().isEmpty() && numberField.getText().isEmpty();
boolean notesEmpty = notesArea.getText().trim().isEmpty() && notesArea.getText().isEmpty();
inputsFull = !firstEmpty && !lastEmpty && !numberEmpty && !notesEmpty;
System.out.println(firstEmpty);
System.out.println(lastEmpty);
System.out.println(numberEmpty);
System.out.println(notesEmpty);
System.out.println(inputsFull);
System.out.println();
}
public boolean isInputsFull() {
return this.inputsFull;
}
First, delete your handleKeyRelease method. Never use key event handlers on text input components: for one thing they will not work if the user copies and pastes text into the text field with the mouse. Just register listeners with the textProperty() instead, if you need. Also, isn't (for example)
firstNameField.getText().trim().isEmpty() && firstNameField.getText().isEmpty()
true if and only if
firstNameField.getText().isEmpty();
is true? It's not clear what logic you are trying to implement there.
You should simply expose inputsFull as a JavaFX property:
public class AddDialogController {
#FXML
private TextField firstNameField;
#FXML
private TextField lastNameField;
#FXML
private TextField numberField;
#FXML
private TextArea notesArea;
private BooleanBinding inputsFull ;
public BooleanBinding inputsFullBinding() {
return inputsFull ;
}
public final boolean getInputsFull() {
return inputsFull.get();
}
public void initialize() {
inputsFull = new BooleanBinding() {
{
bind(firstNameField.textProperty(),
lastNameField.textProperty(),
numberField.textProperty(),
notesArea.textProperty());
}
#Override
protected boolean computeValue() {
return ! (firstNameTextField.getText().trim().isEmpty()
|| lastNameTextField.getText().trim().isEmpty()
|| numberField.getText().trim().isEmpty()
|| notesArea.getText().trim().isEmpty());
}
};
}
public void processResults() {
String first = firstNameField.getText().trim();
String last = lastNameField.getText().trim();
String number = numberField.getText().trim();
String notes = notesArea.getText().trim();
Contact contact = new Contact(first, last, number, notes);
// ContactData.add(contact);
}
}
and then all you need is
dialog.getDialogPane().getButtonTypes().add(ButtonType.OK);
dialog.getDialogPane().getButtonTypes().add(ButtonType.CANCEL);
AddDialogController controller = loader.getController();
dialog.getDialogPane().lookupButton(ButtonType.OK)
.disableProperty()
.bind(controller.inputsFullBinding().not());

How to change a part of view in JavaFx

I've got a view (SplitPane horizontaly seperated). So lets name these two parts lowerAnchorPane and upperAnchorPane. In upperUnchorPane I've got a button. I want the application after pressing that button to switch lowerAnchorPane with another AnchorPane created in FXML file. I want to achive this in function showPatients() but it seems not working, can you suggest me any solutions? I don't know any other way to load fxml file
public class MainController {
private Patient model;
#FXML
private Button newPatientButton;
#FXML
private Button newVisitButton;
#FXML
private Button showVisitsButton;
#FXML
private Button showPatientsButton;
#FXML
private AnchorPane lowerAnchorPane;
public void initData(Patient model)
{
this.model = model;
}
#FXML
void showPatients(ActionEvent event) throws IOException { // when button pressed
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/ListOfPatients.fxml"));
lowerAnchorPane = loader.load();
}
You can inject the SplitPane and replace the existing lower anchor pane with the one you load:
#FXML
private SplitPane splitPane ; // with the corresponding fx:id in the fxml file
#FXML
void showPatients(ActionEvent event) throws IOException { // when button pressed
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/ListOfPatients.fxml"));
lowerAnchorPane = loader.load();
// assuming the existing pane is the second one in the split pane:
splitPane.getItems().set(1, lowerAnchorPane);
}

How to add a actionListener to a label in JavaFx

i'm trying to add an ActionListener to a label whitch pop out whenever user types wrong password or login.
Here is my Login Controller
public class LoginController implements Initializable {
#FXML
private Label label;
#FXML
private TextField LoginField;
#FXML
private PasswordField PasswdField;
#FXML
private Button LogInButton;
#FXML
private Label IncorrectDataLabel;
//private String uri = "http://google.com";
#FXML
private void LogIn(ActionEvent event) throws IOException {
if(LoginField.getText().equals("MKARK")&&PasswdField.getText().equals("KACZOR1"))
{
Parent parent = FXMLLoader.load(getClass().getResource("/fxmlFiles/MainScreen.fxml"));
Scene MainScene = new Scene(parent);
Stage stage = (Stage) ((Node) event.getSource()).getScene().getWindow();
stage.setScene(MainScene);
stage.show();
}
else
{
IncorrectDataLabel.setVisible(true);
// <------------------- Here I want to bind hyperlink to a label with would open the google site, whenever user clicks it.
}
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
How am i able to fix that issue? I've tried many times (setOnAction, addMouseListener) but nothing worked :(.
If You dont mind i would also ask about the public void initialize function. What is it for? It pop out automatically when i created the class.
Thanks in advance
Labels do not fire action events. You could use a listener for mouse clicked events, e.g:
#FXML
private void gotoGoogle() {
// open url etc
}
and in the FXML file
<Label fx:id="IncorrectDataLabel" onMouseClicked="#gotoGoogle" ... />
However, it probably makes more sense to use a Hyperlink for this, which would give the user better visual feedback that it was something on which they were expected to click. Just replace the label with
<Hyperlink fx:id="IncorrectDataLabel" onAction="#gotoGoogle" text="..." ... />
and update the type in the controller accordingly:
#FXML
private Hyperlink IncorrectDataLabel ;
You need the appropriate import for javafx.control.Hyperlink in both the FXML file and in the controller.
Off-topic note: use proper Java naming conventions for your variable and method names.

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.

cannot convert from String to ObservableValue<String>

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".

Resources