JavaFX bind a hyperlink to a label - java

I am using MVP in my JavaFX application.
Resources:
public class InfoStageResources{
StringProperty lblBlogText;
Hyperlink linkBlog;
InfoStageResources() {
this.lblBlogText = new SimpleStringProperty("link");
this.linkBlog = new Hyperlink("link");
}
}
Controller:
public class InfoStageController{
private InfoStageView view;
private InfoStageResources res;
public void initView(){
this.res = new InfoStageResources();
this.view = new InfoStageView(this.res);
this.initViewBindings();
}
private void initViewBindings(){
this.view.lblBlog.textProperty().bind(this.res.lblBlogText);
//this will not work
this.view.lblBlog.textProperty().bind(this.res.linkBlog);
}
}
View
In my InfoStageView in just init my labels and style my view.
How can bind my Hyperlink to my label. I tried some things but without success.
My StringProperty lblBlogText isn't clickable but easy to bind.
My goal: I want to open the browser with the link.

I think you are looking for
this.view.lblBlog.textProperty().bind(this.res.linkBlog.textProperty());

Related

How to handle controller/views creation in a custom MVC

I'm on an application which should open a GUI specific commands.
I tried to implement a sort of MVC like that (simplified version) :
class View1 {
// Delegate gui framework
private Gui delegate;
private Consumer<String> nextButtonCallback;
private Model model;
public void open() {
// open the view here and populate it with model values
// onButtonClick invoke nextButtonCallback.accept("data");
}
}
class EditController1 {
private Model model;
public void start() {
}
private void openView1() {
View1 view = new View1(model);
view.nextButtonCallback= string -> {
// open a second view
view.close();
openView2();
};
view.open();
}
private void openView1() {
View2 view = new View2(model);
view.backButtonCallback = string -> {
// go back
view.close();
openView1();
};
view.open();
}
}
class Command {
private ModelService modelService;
public void onCommand1(String modelId) {
Model model = modelService.getModel(modelId);
EditController1 controller = new EditController1 (model);
controller.start();
}
}
Right now the controller is controlling several views and handling internal navigation (is that a good idea ?)
Now, lets say I have another controller (InfoController) which is opened by another command (infoCommand). I would like to open edition when clicking "edit" button. But since we are in the InfoController I can not open edit views.
Same with "back" button, how to know if InfoController should open info view when EditController views are closed ?
How should I handle navigation between internal and external views with this kind of controllers ?
Or how should I rework to make it better ? :)
Thanks

How to update view via model in JavaFX MVC?

I have FXML application with model, view and controller. My view is in .fxml file and I have Text there like this
<Text fx:id="position" text="None" GridPane.columnIndex="1" GridPane.rowIndex="3">
<font>
<Font name="System Bold" size="18.0" />
</font>
</Text>
My Controller look like this
public class Controller{
#FXML
private Text position;
public void updatePosition(String text){
position.setText(text);
}
}
In my Model, there I have String variable, which is changing throughout all my project. Model look like this
public class Model {
public String position = "None";
public String getPosition() {
return tactic;
}
public void setPosition(String position) {
this.position = position;
}
}
There are another classes in my project, which call setPositon method and update variable position. Is there a way how to change Text in my view, when someone change position variable in Model class?
You need to observe the model. The easiest way to do this in JavaFX is to use JavaFX properties to represent the data.
public class Model {
private final StringProperty position = new SimpleStringProperty("None");
public StringProperty positionProperty() {
return position ;
}
public final String getPosition() {
return positionProperty().get();
}
public final void setPosition(String position) {
positionProperty().set(position);
}
}
Now you can simply bind the text's textProperty() to the model's positionProperty(), and if the model's position is changed, the text will automatically update:
public class Controller{
#FXML
private Text position;
private Model model ;
public void initialize() {
position.textProperty().bind(model.positionProperty());
}
// ...
}
The only tricky part now is to make sure the controller has a reference to the correct model instance. The best way to do this is to provide a constructor for the controller taking a model:
public class Controller {
#FXML
private Text position;
private final Model model ;
public Controller(Model model) {
this.model = model ;
}
public void initialize() {
position.textProperty().bind(model.positionProperty());
}
// ...
}
The problem now is that the default mechanism for creating controllers via the FXMLLoader relies on a zero-argument constructor, so it won't work. So you need to remove the fx:controller attribute from the FXML file and set the controller "by hand":
Model model = new Model(); // or just reference to existing model...
FXMLLoader loader = new FXMLLoader(getClass().getResource("View.fxml"));
loader.setController(new Controller(model));
Parent root = loader.load();
// ...
model.setPosition("Left"); // will update text in view
See Passing Parameters JavaFX FXML for more ways to pass the model (or other data) to the controller.
Also see the related question Applying MVC With JavaFx

Building a Vaadin web app with dynamic content

I'm trying to create a web app via Vaadin (Vaadin Framework 8).
I read several pages of documentation but still, I have big problems concerning the structure of a Vaadin app. My problem is that I lack something about the theory behind it, and I'll try to expose my problems with a first attempt of code. I'll really appreciate anything that can help me understand how Vaadin works.
I want to create a website where a user can register, log in and log out.
The structure of the GUI of the website is an Homepage where there is something like a button or something like that to do the login if the user is not logged, and if the user is logged, instead of the login button, it should appear a logout button.
First thing, I have a class that extends UI; in that class, I set the servlet and the init() method.
In the init() method, I start creating a VerticalLayout(), then a MenuBar and a Panel (called contentPanel). Then, I create a Navigator. The first problem I encounter is that I understand the Navigator as a possibility of browsing between different pages; the constructor of a Navigator wants a SingleComponentContainer, so for now, I don't know how to navigate between different web pages. For my example, in the constructor I use the Panel: new Navigator(this, contentPanel); Then I add different View that will then appear inside the panel. Finally, I navigate to the Welcome page.
MyUI class:
public class MyUI extends UI {
/**
* Class that checks if the user is in the database
* and the psw inserted is correct
*/
public static Authentication AUTH;
public static User user = null;
#WebServlet(value = "/*", asyncSupported= true)
#VaadinServletConfiguration(productionMode = false, ui = MyUI.class)
public static class MyUIServlet extends VaadinServlet {
}
#Override
protected void init(VaadinRequest request) {
AUTH = new Authentication();
VaadinSession.getCurrent().setAttribute("user", user);
final VerticalLayout layout = new VerticalLayout();
layout.setMargin(true);
setContent(layout);
Panel contentPanel = new Panel("Main Panel");
contentPanel.setSizeFull();
new Navigator(this, contentPanel);
getNavigator().addView(LoginPage.NAME, LoginPage.class);
getNavigator().setErrorView(LoginPage.class);
getNavigator().addView(LogoutPage.NAME, LogoutPage.class);
getNavigator().addView(WelcomePage.NAME, WelcomePage.class);
MenuBar.Command welcome = new Command() {
#Override
public void menuSelected(MenuItem selectedItem) {
getNavigator().navigateTo(WelcomePage.NAME);
}
};
MenuBar.Command login = new Command() {
#Override
public void menuSelected(MenuItem selectedItem) {
getNavigator().navigateTo(LoginPage.NAME);
}
};
MenuBar.Command logout = new Command() {
#Override
public void menuSelected(MenuItem selectedItem) {
getNavigator().navigateTo(LogoutPage.NAME);
}
};
MenuBar mainMenu = new MenuBar();
mainMenu.addItem("Welcome", VaadinIcons.ARROW_CIRCLE_LEFT, welcome);
mainMenu.addItem("Login", VaadinIcons.ENTER, login);
mainMenu.addItem("Logout", VaadinIcons.EXIT, logout);
layout.addComponent(mainMenu);
layout.addComponent(contentPanel);
getNavigator().navigateTo(WelcomePage.NAME);
}
}
LoginPage class:
public class LoginPage extends VerticalLayout implements View {
private static final long serialVersionUID = 1L;
public static final String NAME = "loginpage";
public LoginPage(){
Panel panel = new Panel("Login");
panel.setSizeUndefined();
addComponent(panel);
FormLayout content = new FormLayout();
TextField username = new TextField("Username");
content.addComponent(username);
PasswordField password = new PasswordField("Password");
content.addComponent(password);
Button send = new Button("Enter");
send.addClickListener(new Button.ClickListener() {
private static final long serialVersionUID = 1L;
public void buttonClick(ClickEvent event) {
//The authenticate method will returns
//true if the credentials are correct
//false otherwise
if(MyUI.AUTH.authenticate(username.getValue(), password.getValue())){
//In AUTH there is a User field called "user"
//User is a class that represents an user (so it has mail, psw, name etc)
VaadinSession.getCurrent().setAttribute("user", MyUI.AUTH.getUser());
}else{
Notification.show("Invalid credentials", Notification.Type.ERROR_MESSAGE);
}
}
});
content.addComponent(send);
content.setSizeUndefined();
content.setMargin(true);
panel.setContent(content);
setComponentAlignment(panel, Alignment.MIDDLE_CENTER);
}
}
Logout class has the same structure of Login class; there is a logout method:
private void doLogout() {
MyUI.AUTH.setUser(null);
VaadinSession.getCurrent().setAttribute("user", MyUI.AUTH.getUser());
getSession().close();
}
Another problem is: how can I dynamically add components in my layout, basing on the user status (logged or not?)
Next problem is: I didn't understand how to effectively do a logout.
I'll add complete code to facilitate any tests.
public class LogoutPage extends VerticalLayout implements View {
private static final long serialVersionUID = 1L;
public static final String NAME = "logoutpage";
public LogoutPage(){
Panel panel = new Panel("Logout");
panel.setSizeUndefined();
addComponent(panel);
Button logout = new Button("Logout");
logout.addClickListener(e -> doLogout());
addComponent(logout);
}
private void doLogout() {
MyUI.AUTH.setUser(null);
VaadinSession.getCurrent().setAttribute("user", MyUI.AUTH.getUser());
getSession().close();
}
}
______________________________________________________________________________
public class WelcomePage extends VerticalLayout implements View {
private static final long serialVersionUID = 1L;
public static final String NAME = "welcomepage";
public WelcomePage() {
setMargin(true);
setSpacing(true);
Label welcome = new Label("Welcome");
welcome.addStyleName("h1");
addComponent(welcome);
}
#Override
public void enter(ViewChangeEvent event) {
}
}
______________________________________________________________________________
public class Authentication {
private static User user = null;
//those fields should be in user; this is just a test
private String userID = "ID";
private String psw = "psw";
public Authentication() {
}
public void setUser(User user) {
Authentication.user = user;
}
public User getUser(){
return Authentication.user;
}
public Boolean authenticate(String userID, String psw){
if(userID == this.userID && psw == this.psw) {
user = new User();
return true;
}
return false;
}
}
I have seen many users struggling with the vaadin concept.
In short it is not a page-based framework, where you switch to a new page with each mouse click.
Instead it is more like a Swing application, where you have a single gui instance, where you add forms/poups/buttons, modify them and remove them based on user interaction. This is known as a Single-Page Application.
The Navigator is mainly used to allow the user to navigate with the backward/forward buttons of the webbrowser to the "previous" page, or bookmark specific pages.
This link provides some more detailed informations about this concept.
To answer you question:
- Use a single page/nvigation
- When not logged in, show a modal login popup
- When the user correctly enters authentification, remove the popup and show the main content in the vertical layout
- When the user logs out, remove the content from the vertical layout
For your simple use case, there is no need to work with the Navigator or Views
I initially ran into the same issues, which got me interested in Spring and ultimately Vaadin4Spring. Check out https://github.com/peholmst/vaadin4spring/tree/master/samples/security-sample-managed even if you have no interest in using Spring. It will provide you some insight on your View Navigation and with the addition of the Vaadin4Spring Sidebar it can make it easy to control access to views and menus. I am sure permissions will be your focus soon which can be complex.

JavaFX how to inject new FXML content to current Scene

I have an app, which has HomeScene.fxml file with headers and menu. HomeScene has also dashboardPane, which should be changed dynamically after menu button is being pressed. Dashboard pane content should be loaded from another fxml file, lets say 'FinancesPane.fxml' or 'SettingsPane.fxml'.
Im trying to replace content of dashboardPane in HomeController:
#FXML
public void handleFinancesButtonAction() {
FinancesPaneFactory paneFactory = new FinancesPaneFactory();
dashBoardPane.getChildren().clear();
dashBoardPane.getChildren().add(paneFactory.createPane());
}
My FinancesPaneFactory looks like this:
public class FinancesPaneFactory extends PaneFactory {
private static final String PANE_TEMPLATE_PATH = "/sceneTemplates/FinancesPane.fxml";
public FinancesPaneFactory() {
super(PANE_TEMPLATE_PATH );
}
#Override
protected Pane generatePane(FXMLLoader loader) {
try {
return (Pane) loader.load();
} catch (IOException e) {
throw new FatBirdRuntimeException("Unable to load FinancesPane", e);
}
}
}
To be more clear, this is how HomeScene looks like: HomeScene .
This empty space is a dashboardPane, and should be replaced with another content when user press the left menu button.
How to inject this content dynamically?
Yes, you should do this to keep scene graph low and you will benefit from better performance , what i do is create dynamic container :
#FXML
private ScrollPane dynamicNode;
Scroll pane is a good choice.
This is put to MainController.
I have main controller different from others , main controller is actually the only one i initialize, so in your main program class whatever you call it :
private static MainViewController mainViewController;
...
private static BorderPane loadMainPane() throws IOException {
FXMLLoader loader = new FXMLLoader();
loader.setController(mainViewController);
BorderPane mainPane = (BorderPane) loader.load(
CsgoRr.class
.getResourceAsStream(Info.Resource.FXML_FILE_MAIN));
mainPane.getStylesheets().add(CsgoRr.class.getResource("path...style.css").toString());
return mainPane;
}
Dont forget to create static accessor, other controllers that i have are usually not created this way , i use fx:controller in fxml to specify what controller should be for which fxml , its usually handy to have mainController accessable.
So to change your views create in your main controller methods that are connected to your menu with whose you change views
#FXML
private void setViewPreferences() {
setView(Info.Resource.FXML_FILE_PREFERENCES);
}
#FXML
private void setViewProductPage() {
setView(Info.Resource.FXML_FILE_PRODUCT_PAGE);
}
Currently in dynamicNode is helper to see what exactly is the current selected, its
private String currentlyInDynamicPane;//not important
Here is setView
public void setView(String fxmlPath) {
dynamicNode.setContent(getView(fxmlPath));
currentlyInDynamicPane = fxmlPath;
}
public Node getView(String fxmlPath) {
try {
return new FXMLLoader(getClass().getResource(fxmlPath)).load();
} catch (IOException ex) {
ex.printStackTrace();
return null;
}
}
So when you click left menu you swap FXML files, you can make sure that you have some default FXML shown at the start or when nothing in menu is selected as well.
This is the way i do it, roughly.
So think about YOUR DASHBOARD as DynamicPane,

How to disable a tab in a GXT 3 tabpanel

I have a GXT 3 TabPanel and would like to disable one or more tabs in response to an event.
There does not seem to be a way to do this.
PlainTabPanel panel = new PlainTabPanel();
TabItemConfig config = new TabItemConfig("Disabled");
Label disabled = new Label("This tab should be disabled");
config.setEnabled(false); // here what you need
panel.add(disabled, config);
I figured this out a while back and forgot to post the answer. Here's a way to do this that works reliably :
public class SimpleTabPanel extends com.sencha.gxt.widget.core.client.PlainTabPanel
implements TabPanel {
Tab currentActiveTab;
private Map<SimpleTab,TabItemConfig> tabConfigs = new HashMap<SimpleTab,TabItemConfig>();
// a map sorted by priority used to keep the expected tab order
SortedMap<TabData, SimpleTab> tabsConfig = new TreeMap<TabData, SimpleTab>(
new Comparator<TabData>() {
#Override
public int compare(TabData o1, TabData o2) {
return Float.compare(o1.getPriority(), o2.getPriority());
}
});
#Override
public Tab addTab(TabData tabData, String historyToken) {
SimpleTab newTab = createNewTab(tabData);
tabsConfig.put(tabData, newTab);
newTab.setTargetHistoryToken(historyToken);
return newTab;
}
private void disableTab(SimpleTab tab, TabData key){
// get and save tab's config.
TabItemConfig tic = getConfig(tab);
tabConfigs.put(tab, tic);
// put new one on tab to disable it.
tic = new TabItemConfig(key.getLabel());
tic.setEnabled(false);
update(tab, tic);
}
public void enableAllTabs(){
for(TabData key : tabsConfig.keySet()){
SimpleTab tab = tabsConfig.get(key);
TabItemConfig tic = tabConfigs.get(tab);
if(tic!=null){
tic.setEnabled(true);
update(tab, tic);
}
}
}
}
I had the same issue happening in my project.
The way I solved it is really simple and I hope that it answers your need.
public void enableTab(IsWidget item, Boolean enable) {
TabItemConfig config = tabPanel.getConfig(item.asWidget());
config.setEnabled(enable);
tabPanel.update(item.asWidget(), config);
}
I call that method every time I need to change a tab state.

Categories