I am learning Java and JavaFX. I have an application that has some ComboBox components that are only visible if logged in as an admin My problem is that the application populates the Combobox with system variables when it starts, If a user logs in then i get a null pointer and the application doesnt start because the ComboBox doesnt exist.. When an admin logs in the application starts correctly. Here is how i am trying to get it to work.
private void loginpressed(ActionEvent event) throws IOException
{
if (BCrypt.checkpw(userId.getText() + passwordfield.getText(), passwordhashuser))
{
Parent root = FXMLLoader.load(getClass().getResource("LaserControllerUserUI.FXML"));
Scene home_page_scene = new Scene(root);
Stage app_stage = (Stage) ((Node) event.getSource()).getScene().getWindow();
app_stage.setScene(home_page_scene);
app_stage.show();
}
else if (BCrypt.checkpw(userId.getText() + passwordfield.getText(), passwordhashadmin))
{
Parent root = FXMLLoader.load(getClass().getResource("LaserControllerUI.FXML"));
Scene home_page_scene = new Scene(root);
Stage app_stage = (Stage) ((Node) event.getSource()).getScene().getWindow();
app_stage.setScene(home_page_scene);
app_stage.show();
}
else{
errorMessage.setText("Login Incorrect!");
System.out.print("false");}
}
So i have two seperate FXML depending on who logs in. How do i handle this?
In general, you should avoid making your application in a way in which it will need administrative privileges, if it does need these privileges you will need to give this feedback to the user, or enforce this condition, you can google how to do this such as in this link for Microsoft: https://technet.microsoft.com/en-us/library/ff431742.aspx. Note that you can have access to the current user through the System properties in Java, and you can also save properties based on a user as well, if that helps.
That casting your doing completely unnecessary. I would look up some GUI tutorials on MVC for Java if I was you. Once you learn this pattern you will have a light bulb moment and everything will just make sense. There are alterations of this pattern you should learn the main ones.
Not following any GUI patterns will cause you immense pain and complications. I would estimate over 100,000 questions on SO would not exist if people knew this pattern. This includes your question. GUI and functionality should be completely separated. Your question would simply be: how do I get a list of all Users, which would be a duplicate and not need to be asked.
I would use one FXML Document. If they have pretty much the same components, I would set the locations and visibility of the components after a user logs in. What is set to visible should depend on the users account type.
Related
I am trying to make a game as my term final project. So when life level of one player is less or equal zero then I am trying to load another fxml file on the existing stage automatically. But I can't . I am not sure that if-else logic can change one scene to another. It will be helpful if anyone can give a way to change a scene automatically.
enter image description here
Thanks in advance.
if(Controller.lifeDekhao2<=0){
Parent is = FXMLLoader.load(getClass().getResource("/FXMLPack/First1.fxml"));
Scene isScene = new Scene(is);
Stage window = (Stage) ((Node) (event.getSource())).getScene().getWindow();
window.setScene(isScene);
window.show();
Controller.time_end = System.currentTimeMillis();
}
I try to build an reservation Form with vaadin 10 while building it i encounterd the problem, that the autocompletion we know from every form on te web doesn't work. I put in an name field the name press submit an the next time i want to re-enter the name i need to write it out again.
My code looks like that (shortend):
TextField name = new TextField();
Button save = new Button("submit");
save.addClickListener(event -> save());
name.setAutocomplete(Autocomplete.ON);
add(name);
add(save);
i had the hopes that Autocomplete.On does the magic for me but it seems not to work. Maybe the way the save methode works screw things up?
the methode is rather big i just simplify it
private void save() {
--save everything to db
--remove all fields
--replace the fields with text saying reservation done
}
found out that someone created issue https://github.com/vaadin/vaadin-text-field/issues/156
seems to be an Shadow DOM limitation
Related issues:
https://bugs.chromium.org/p/chromium/issues/detail?id=746593
https://bugs.webkit.org/show_bug.cgi?id=172567
Edit:
for auto completion for my loginForm i got it working with adding
class xyz extends Div implements PageConfigurator{
...
#Override
public void configurePage(InitialPageSettings settings) {
settings.addInlineWithContents(
InitialPageSettings.Position.PREPEND,
"window.customElements=window.customElements||{};
window.customElements.forcePolyfill=true;
window.ShadyDOM={force:true};",
InitialPageSettings.WrapMode.JAVASCRIPT);
}
I came across this issue recently with Vaadin 14 and a custom login form.
Chrome only offers auto-filling fields (and also save login details) if it can see the input tags with name attributes in the Light DOM, but Vaadin creates TextFields with all of its elements inside of a Shadow DOM hidden.
Solution is to create a reference with <input slot="input"> inside the parent <vaadin-text-field> as part of Light DOM. All the styles and everything will still be in the Shadow DOM but Chrome now can see the input fields for auto-completion.
Kotlin code:
val username = TextField().apply {
element.setAttribute("name", "username")
element.appendChild(Element("input").setAttribute("slot", "input"))
}
val password = PasswordField().apply {
element.setAttribute("name", "password")
element.appendChild(Element("input").setAttribute("slot", "input"))
}
add(username, password)
I'm facing quite simple, but general problem.
Problem setup
I have JavaFX application with main window (main stage). This stage works with some (keep it general) data. The data may be somehow loaded at the startup (for instance from server, database, XML file). The source of data (server adress, database name, XML file path) are to be prompted from user. (Keep in mind, that user can cancel the input and the data won't be loaded.)
Since I would like to show the stage up for the user (to get acquainted with the app and possibly browse the help) and then, by menu File -> Load data, let him to load the data.
But since the stage depends mostly on the data, I need to disable most of the controls until the user properly loads the data and makes the stage useable.
Question
Here arises dilema I cannot solve. AFAIK, I have two options, how to make most of stage disabled until the data are properly ready:
Bind disabled with "or data not loaded"
The more JavaFX-binding-friendly solution is to have boolean property like dataNotReadyProperty and then, when the controller is beeing initialized use things like:
private void init() { // call from initialize method
// ....
doFooButton.disableProperty().bind(
Bindings.or(
dataProperty().get().cannotBeFooDoneProperty(),
dataNotReadyProperty()
)
);
// ....
}
As you can see, this code is quite ugly and long (especialy when some controls' disability is given by additional or or and).
Disable and bind when data ready
The second option is to simply disable everything at startup (or even within the FXML). And also set up change listener for the dataProperty and when the data is loaded, do the proper initialization of bindings. Written in code:
private void init() { // call from initialize method
// ....
doFooButton.setDisabled(true);
// ....
dataProperty().addListener((observable, oldValue, newValue) -> {
if (oldValue == null && newValue != null) {
runInitialization();
}
});
}
private void runInitialization() {
// ....
doFooButton.disableProperty().bind(
dataProperty().get().cannotBeFooDoneProperty());
// ....
}
This solution splits initialization into two, but the "real" initialization here is kept simple and understandable. Also, I am forced to modify each control twice.
Conclusions
What would you prefer? Do you have another suggestions, how to solve this situation?
Additional info
the stage does need any more initialization than the bindings of disabled properties
once the data is loaded, it cannot be "unloaded" (it can be only reloaded by other one)
there is approx. 20 controls to be disabled on startup and 4 to be accessible.
Finally, i decided to use solution base on the #ItachiUchiha's answer.
I've created two different Panes, one for proper work with data, another one with just label like: "Load data to start work" and load button.
The remaining (just about 5 of them) controls i disabled/enabled by bindings (option 1). So the amount of "ugly" code decreased rapidly.
I would like to know what is the lifecycle of stages and controllers created on the fly(not the main one that extends Application), or in other words, any child window spawned from another window. I am concerned about any memory leaks and I just want to make sure I am building memory/resource efficient GUI application. Here is a sample code:
private void addNewAsset(ActionEvent event) {
AssetController assetController = new AssetController();
FXMLLoader loader = new FXMLLoader(getClass().getResource("assetchooser.fxml"));
loader.setController(assetController);
Stage assetChooserStage = new Stage();
assetChooserStage.initModality(Modality.WINDOW_MODAL);
assetChooserStage.initOwner(listviewAssets.getScene().getWindow());
assetChooserStage.setTitle("Choose Asset(s)");
try {
assetChooserStage.setScene(new Scene((Parent)loader.load()));
} catch (IOException e) {
e.printStackTrace();
}
assetChooserStage.show();
assetChooserStage.setOnCloseRequest((windowEvent)->{
//DO STUFF: Get any data
List<AssetChosenData> listAssets = assetController.getListAssetChosen();
System.out.println("CLOSING");}
);
}
As you can see, the method addNewAsset() is executed when the user clicks at a certain button. The method spawns a new Stage using FXMLLoader and using a controller. When this stage gets closed(setOnCloseRequest), I capture the data I want. What happens afterwards? Will the controller(AssetController) and Stage(assetChooserStage), as well as all the Controls and data used by them, be set to null and the GC come by later and dispose of them?
If you don't know specifics, maybe you can point me to an official Java/Oracle documentation that deals with this subject.
Thanks.
This is just the default Java behavior:
You are creating objects and if you no longer keep (strong) references to them, they are being garbage collected.
In your case you are creating objects solely in a local scope (method) and do not store any object otherwise.
When and if the Stage is garbage collected is an implementation detail out of our reach. If you think there is a memory leak, you should probably file an issue in the JavaFX tracker.
I can see how to instantiate a second MVC group, e.g.
def (loginPaneModel, loginPaneView, loginPaneController) =
createMVCGroup('LoginPane', 'LoginPane', [:]);
view.loginPanel = loginPaneView.loginPanel
But I don't want to show as part of my main window. I want it to pop up over it. What do I call to do that? Thanks!
The easiest way would be to use the view panel as the root of a dialog in the parent MVC group. In the view for the group that yor code snippet is the controller of you could do something like this...
application(title:'your app', ....) {
// your existing code...
loginDialog = dialog(title:'Login Panel', visible:false) {
panel(loginPanel)
}
}
And then when you need to show the dialog (in the same controller)
view.loginDialog.visible = true
Nesting a dialog inside of another window has the side effect of setting the dialog's owner to the frame or dialog of the parent. Having a dialog owned by another dialog/window is what causes the dialog to be linked with the parent and always float on top of that parent. It will also raise/lower with the parent as well.
Well, it seems that only the first line is needed. That was enough to pop up a window. I believe the key, though, was to make the view a frame.
def frame = frame(title:'Login', pack:true, locationByPlatform:true) {
...
}
frame.pack()
frame.show()