I've a basic question.
I'm using Vaadin 8.
When I use :
Page.getCurrent().setTitle(IStringConstants.HOMEPAGE_LABEL);
This is working without issue.
And, when I use
getUI().getPage().setTitle(IStringConstants.HOMEPAGE_LABEL);
I receive an error :java.lang.IllegalArgumentException: Unable to create an instance of {0}. The constructor threw an exception.
Because getUI() return null.
My code is pretty simple, in my home page :
public class HomepageView extends CustomComponent implements View {
public HomepageView() {
getUI().getPage().setTitle(IStringConstants.HOMEPAGE_LABEL);
VerticalLayout layout = new VerticalLayout();
layout.setSizeFull();
setCompositionRoot(layout);
}
}
And my UI is :
#Theme("mytheme")
public class myUI extends UI {
private static final long serialVersionUID = 1L;
private Navigator navigator;
#Override
protected void init(VaadinRequest vaadinRequest) {
navigator = new Navigator(this, this);
navigator.addView("", HomepageView.class);
}
#WebServlet(urlPatterns = "/*", name = "myUIServlet", asyncSupported = true)
#VaadinServletConfiguration(ui = myUI.class, productionMode = false)
public static class myUIServlet extends VaadinServlet {
private static final long serialVersionUID = 1L;
}
}
getUI() returns null in your case, because the instance you are creating has no parent yet. The parent is set when you add your HomepageView Component to a layout or Panel for instance. getUI() traverses the parents up to the UI root or returns null no such parent is found. You can call getUI().getPage().setTitle(IStringConstants.HOMEPAGE_LABEL); in the attach listener:
public HomepageView() {
this.addAttachListener(e -> {
getUI().getPage().setTitle(IStringConstants.HOMEPAGE_LABEL);
});
VerticalLayout layout = new VerticalLayout();
layout.setSizeFull();
setCompositionRoot(layout);
// show something
layout.addComponent(new Label("Hello World!"));
}
Edit
As Morfic pointed out the more common and cleaner solution would be to use the View's enter method instead of the constructor. As you can see the listener is not necessary anymore, since the component already got attached to the UI tree:
#Override
public void enter(ViewChangeListener.ViewChangeEvent event) {
getUI().getPage().setTitle("This is a title");
VerticalLayout layout = new VerticalLayout();
layout.setSizeFull();
setCompositionRoot(layout);
layout.addComponent(new Label("Hello World!"));
}
I've tried to add a method inside MyUi.java class in vaadin, but all I get are errors. So, is it actually possible to do so? The reason why I ask is this: basically MyUi.java class has a button which when clicked opens up another window (the code for this other window sits in a different class). This button is removed (button.setVisible(false);) when clicked and I added a addCloseListener to the new window so that when that window is closed it fires an event and calls a function which will allow me to re-display the button. This function needs to sit inside the MyUi class as I can't figure out how to access the button which currently sits inside MyUi class from a different class.
Some code to make things a little clearer: MyUi.java contains the button
public class MyUI extends UI {
#Override
protected void init(VaadinRequest vaadinRequest) {
final VerticalLayout layout = new VerticalLayout();
final NewWindow newWindow = new NewWindow();
final UploaderComponent uploaderComponent = new UploaderComponent();
//final TextField name = new TextField();
// name.setCaption("Type your name here:");
final Button button = new Button("Click Me");
button.addClickListener(new Button.ClickListener()
{
#Override
public void buttonClick(ClickEvent event)
{
//button.setVisible(false);
// TODO Auto-generated method stub
getUI().addWindow(newWindow.getWindow());
newWindow.getWindow().setContent(uploaderComponent.formLayout);
}
});
layout.addComponents(button );
layout.setMargin(true);
layout.setSpacing(true);
setContent(layout);
public void testMethod(){//produces errors
}
}
}
And the window class:
public class NewWindow
{
//public static final Sizeable.Unit PIXELS;
Window window = new Window();
public NewWindow(){
window.setStyleName("wrappingWindow");
window.setWidth("620px");
window.center();
window.addCloseListener(new CloseListener()
{
#Override
public void windowClose(CloseEvent e)
{
System.out.println("window closed");
}
});
}
public Window getWindow(){
return window;
}
}
I've been trying to build a simple GUI with JavaFX (I'm completely new to JavaFX) and I've found myself stuck. In every tutorial I've found event handling is done on the level of the UI object, mostly with annonymous inner classes - what I want to accomplish is to move the event handlers to controller class, and inject references to them trough methods called on controller's (and view's) instantiation.
My small GUI is properly build and displayed, the reference is indeed passed, but for a reason the handle() method is not invoked, and I can't find the reason why.
The View:
//imports here
public class View extends Application implements ViewInterface, Runnable {
private Menu fileMenu;
private Menu storheouseMenu;
private MenuBar menuBar;
private Scene scene;
private MenuItem exitItem;
public View() {
initialize();
}
public void initialize() {
fileMenu = new Menu("Plik");
storheouseMenu = new Menu("Magazyn");
MenuItem exitItem = new MenuItem("Exit");
MenuItem displayStorehouse = new MenuItem("Display");
fileMenu.getItems().addAll(exitItem);
storheouseMenu.getItems().add(0, displayStorehouse);
}
#Override
public void start(Stage primaryStage) throws Exception {
Parent root = new VBox();
scene = new Scene(root, 400, 200);
primaryStage.setScene(scene);
primaryStage.setTitle("Szefowa test");
menuBar = new MenuBar();
menuBar.getMenus().addAll(fileMenu, storheouseMenu);
((VBox) scene.getRoot()).getChildren().addAll(menuBar);
primaryStage.setResizable(false);
primaryStage.show();
}
public void addFileMenuListeners(EventHandler<ActionEvent> eventHandler) {
exitItem = fileMenu.getItems().get(0);
exitItem.setOnAction(eventHandler);
}
public void addStorehouseMenuListeners(EventHandler<ActionEvent> eventHandler) {
MenuItem displayStorehouse = fileMenu.getItems().get(0);
displayStorehouse.setOnAction(eventHandler);
}
public void displayMessage(String message) {
Alert alert = new Alert(Alert.AlertType.INFORMATION);
alert.setTitle("Ping");
alert.setContentText(message);
}
//other methods here
}
The Controller:
package kitke.szefowa.controller;
//imports here
public class Controller implements ControllerInterface {
private Model model;
private View view;
public Controller(Model model, View view) {
this.model = model;
this.view = view;
this.view.addFileMenuListeners(new FileMenuListener());
this.view.addStorehouseMenuListeners(new StorehouseMenuListener());
}
public class FileMenuListener implements EventHandler<ActionEvent> {
#Override
public void handle(ActionEvent event) {
//do some stuff
}
}
public class StorehouseMenuListener implements EventHandler<ActionEvent> {
#Override
public void handle(ActionEvent event) {
//do some stuff
}
}
}
}
PS I've no such problem while build the GUI with Swing so the issue is connected with JavaFX.
I have tested your code by manual instantiation as:
Controller controller = new Controller( this );
in View.start() method. The event handlers are working as expected with only small problem. Both in addFileMenuListeners() and addStorehouseMenuListeners() methods you are setting the event handler to the same menuitem fileMenu.getItems().get(0). So calling of these method one after another, second invocation is overriding the setOnAction of the first one.
So change the addStorehouseMenuListeners() to:
public void addStorehouseMenuListeners( EventHandler<ActionEvent> eventHandler )
{
MenuItem displayStorehouse = storheouseMenu.getItems().get(0);
displayStorehouse.setOnAction( eventHandler );
}
I've got window class implementation with annotation #Component. Inside this class I declare object with annotation #Autowired.
On my window form I've got a button Create which should read data from TextFields, create new object and store it in the database.
#Component("newProjectWindow")
public class NewProjectWindow {
private Window createProjectWindow;
#Autowired
private ProjectService service;
public Window createWindow() {
createProjectWindow = new Window("New project");
initWindow();
fillWindow();
return createProjectWindow;
}
private void initWindow() {
createProjectWindow.setSizeUndefined();
createProjectWindow.setResizable(false);
createProjectWindow.setModal(true);
createProjectWindow.addCloseListener(new CloseListener(){
#Override
public void windowClose(CloseEvent e) {
Notification.show("Closed");
}
});
}
private void fillWindow() {
final TextField projectName = new TextField("Project name");
final TextField projectOwner = new TextField("Project owner");
Button create = new Button("Create");
create.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
Project newProject = new Project();
newProject.setProjectName(projectName.getValue());
newProject.setProjectOwner(projectOwner.getValue());
//save it somehow
}
});
Button close = new Button("Cancel");
close.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
createProjectWindow.close();
}
});
HorizontalLayout layout = new HorizontalLayout(create, close);
FormLayout formLayout = new FormLayout(projectName, projectOwner, layout);
formLayout.setMargin(true);
createProjectWindow.setContent(formLayout);
}
}
However the problem is how to store object in the database. I've got no access to instantiated ProjectService(which uses ProjectRepisitory which uses SqlSessionTemplate and etc.) because it is under control of Spring - and anonymous ClickListener is not.
But how to store object?
I tend not to use anonymous inner methods for click listeners, but instead get my own classes to implement the ClickListner. So in your example I would change the class like this:
#Component("newProjectWindow")
public class NewProjectWindow {
private Window createProjectWindow implements Button.ClickListener;
#Autowired
private ProjectService service;
private Button create = new Button("Create", this);
private Button cancel new Button("Cancel", this);;
public Window createWindow() {
createProjectWindow = new Window("New project");
initWindow();
fillWindow();
return createProjectWindow;
}
private void initWindow() {
createProjectWindow.setSizeUndefined();
createProjectWindow.setResizable(false);
createProjectWindow.setModal(true);
createProjectWindow.addCloseListener(new CloseListener(){
#Override
public void windowClose(CloseEvent e) {
Notification.show("Closed");
}
});
}
private void fillWindow() {
final TextField projectName = new TextField("Project name");
final TextField projectOwner = new TextField("Project owner");
HorizontalLayout layout = new HorizontalLayout(create, close);
FormLayout formLayout = new FormLayout(projectName, projectOwner, layout);
formLayout.setMargin(true);
createProjectWindow.setContent(formLayout);
}
#Override
public void buttonClick(ClickEvent event) {
if (event.getButton() == cancel)
{
createProjectWindow.close();
}
else
{
Project newProject = new Project();
newProject.setProjectName(projectName.getValue());
newProject.setProjectOwner(projectOwner.getValue());
//save it somehow
}
}
}
To access service from listener in your example, consider following solutions:
Anonymous inner classes can reference outer class (using OuterClassName.this syntax - in your case NewProjectWindow.this.service).
You can declare (inner) class and pass appropriate references to it.
You can use Chris M suggestion of parent class implementing listener interface itself.
In the Passive View Model View Presenter pattern, who has the responsibility for displaying the view? I have found related answers for other MVP versions, but they don't seem applicable to the passive view version.
I have a concrete example using Java Swing. It's pretty simple, but basically we have a SwingCustomersView which internally builds a JPanel with a table (list of customers) and a label displaying the currently selected customers age. When a customer is selected in the table, the presenter retrieves the selected customer age from the model. I think the example is a correct implementation of MVP Passive View, but correct me if I'm wrong.
The question is how do we bootstrap these classes? For example, if we wanted to display the SwingCustomersView in a JFrame. How would one do that? I imagine something along the lines of:
void launcher() {
CustomersModel model = new CustomersModel();
SwingCustomersView view = new SwingCustomersView();
CustomersPresenter presenter = new CustomersPresenter(view, model);
}
This is the initial wiring, but nothing is displayed yet. How do we actually display the view? Is it the responsibility of (1) launcher() , (2) SwingCustomersView or (3) CustomersPresenter to display the view? Unfortunately I don't believe any of those are very good as you can see from my thoughts below. Perhaps there's another way?
(1.a): launcher
Make SwingCustomersView extend JFrame and make it add it's internal JPanel to the content pane of itself. Then we can do this:
void launcher() {
CustomersModel model = new CustomersModel();
SwingCustomersView view = new SwingCustomersView();
CustomersPresenter presenter = new CustomersPresenter(view, model);
view.setVisible(true); // Displays the view
}
However in this case we don't use the presenter instance for anything. Isn't that strange? It's just there for wiring, we could just as well delete the variable and just do new CustomersPresenter(view, model).
(2): SwingCustomersView
Make SwingCustomersView take a Container in the constructor to which it should add it's internal JPanel:
void launcher() {
CustomersModel model = new CustomersModel();
JFrame frame = new JFrame("Some title");
SwingCustomersView view = new SwingCustomersView(frame.getContentPane());
CustomersPresenter presenter = new CustomersPresenter(view, model);
frame.pack();
frame.setVisible(true) // Displays the view
}
However, same problem as (1): the presenter instance does nothing. It seems strange. Furthermore with both (1) and (2) it is possible to display the view before the presenter is hooked up, which I imagine could cause strange results in some situations.
(3): CustomersPresenter
Make CustomersPresenter responsible for displaying the view somwhow. Then we could do this:
void launcher() {
CustomersModel model = new CustomersModel();
SwingCustomersView view = new SwingCustomersView();
CustomersPresenter presenter = new CustomersPresenter(view, model);
presenter.show() // Displays the view
}
This would solve the problem of not using it for anything after construction. But I don't see how do to this without either changing the CustomersView interface or making CustomersPresenter too dependent on the underlying GUI implementation. Furthermore, displaying a view doesn't sound like presentation logic and thus doesn't seem to belong in the presenter.
Example
public class CustomersModel {
private List<Customer> customers;
public CustomersModel() {
customers = new ArrayList<Customer>();
customers.add(new Customer("SomeCustomer", "31"));
customers.add(new Customer("SomeCustomer", "32"));
}
public List<Customer> getCustomers() {
return customers;
}
}
public class Customer {
public String name;
public String age;
public Customer(String name, String age) {
this.name = name;
this.age = age;
}
}
public interface CustomersView {
void addCustomerSelectionChangeListener(ItemListener listener);
void onNewActiveCustomer(String age);
void onNewCustomers(List<String> newCustomers);
}
public class SwingCustomersView implements CustomersView {
// Swing components here all put into a main JPanel
public void addCustomerSelectionChangeListener(ItemListener listener) {
// Add event listener to table
}
public void onNewActiveCustomer(String age) {
// Display age in label beneath table
}
public void onNewCustomers(List<String> newCustomers) {
// Display customers in table
}
}
public class CustomersPresenter {
private final CustomersView view;
private final CustomersModel model;
public CustomersPresenter(CustomersView view, CustomersModel model) {
this.view = view;
this.model = model;
initPresentationLogic();
populateView();
}
private void initPresentationLogic() {
view.addCustomerSelectionChangeListener(new ItemListener() {
#Override
public void itemStateChanged(ItemEvent e) {
String selectedName = (String)e.getItem();
List<Customer> customers = model.getCustomers();
for (Customer c : customers)
if (c.name.equals(selectedName))
view.onNewActiveCustomer(c.age);
}
});
}
private void populateView() {
List<Customer> customers = model.getCustomers();
List<String> names = new ArrayList<String>();
for (Customer c : customers)
names.add(c.name);
// View will now populate its table, which in turn will call customerSelectionChangeListener
// so the view 'automagically' updates the selected contact age too
view.onNewCustomers(names);
}
}
Option (3) all the way. It is the presenter's jobs for "controlling" the view, which includes making it visible. Yes, you'll need to add to the view's interface to allow this to happen, but that's not a big deal. Remember, you can make the view is as passive as possible. No logic whatsoever!
Working Example:
I stumbled upon this example of a simple Swing game using an MVC architecture. Since I write my Swing apps using MVP instead of MVC, I can't say with authority if this example is a true and pure example of MVC. It looks okay to me, and the author trashgod has more than proven himself here on SO using Swing, so I'll accept it as reasonable.
As an exercise, I decided to rewrite it using an MVP architecture.
The Driver:
As you can see in the code below, this is pretty simple. What should jump out at you are the separation of concerns (by inspecting the constructors):
The Model class is standalone and has no knowledge of Views or Presenters.
The View interface is implemented by a standalone GUI class, neither of which have any knowledge of Models or Presenters.
The Presenter class knows about both Models and Views.
Code:
import java.awt.*;
/**
* MVP version of https://stackoverflow.com/q/3066590/230513
*/
public class MVPGame implements Runnable
{
public static void main(String[] args)
{
EventQueue.invokeLater(new MVPGame());
}
#Override
public void run()
{
Model model = new Model();
View view = new Gui();
Presenter presenter = new Presenter(model, view);
presenter.start();
}
}
and the GamePiece that we'll be using for the game:
import java.awt.*;
public enum GamePiece
{
Red(Color.red), Green(Color.green), Blue(Color.blue);
public Color color;
private GamePiece(Color color)
{
this.color = color;
}
}
The Model: Primarily, the job of the Model is to:
Provide data for the UI (upon request)
Validation of data (upon request)
Long-term storage of data (upon request)
Code:
import java.util.*;
public class Model
{
private static final Random rnd = new Random();
private static final GamePiece[] pieces = GamePiece.values();
private GamePiece selection;
public Model()
{
reset();
}
public void reset()
{
selection = pieces[randomInt(0, pieces.length)];
}
public boolean check(GamePiece guess)
{
return selection.equals(guess);
}
public List<GamePiece> getAllPieces()
{
return Arrays.asList(GamePiece.values());
}
private static int randomInt(int min, int max)
{
return rnd.nextInt((max - min) + 1) + min;
}
}
The View: The idea here is to make it as "dumb" as possible by stripping out as much application logic as you can (the goal is to have none). Advantages:
The app now be 100% JUnit testable since no application logic is mixed in with Swing code
You can launch the GUI without launching the entire app, which makes prototyping much faster
Code:
import java.awt.*;
import java.awt.event.*;
import java.util.List;
public interface View
{
public void addPieceActionListener(GamePiece piece, ActionListener listener);
public void addResetActionListener(ActionListener listener);
public void setGamePieces(List<GamePiece> pieces);
public void setResult(Color color, String message);
}
and the GUI:
import java.awt.*;
import java.awt.event.*;
import java.util.List;
import javax.swing.*;
/**
* View is "dumb". It has no reference to Model or Presenter.
* No application code - Swing code only!
*/
public class Gui implements View
{
private JFrame frame;
private ColorIcon icon;
private JLabel resultLabel;
private JButton resetButton;
private JButton[] pieceButtons;
private List<GamePiece> pieceChoices;
public Gui()
{
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
icon = new ColorIcon(80, Color.WHITE);
}
public void setGamePieces(List<GamePiece> pieces)
{
this.pieceChoices = pieces;
frame.add(getMainPanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public void setResult(Color color, String message)
{
icon.color = color;
resultLabel.setText(message);
resultLabel.repaint();
}
private JPanel getMainPanel()
{
JPanel panel = new JPanel(new BorderLayout());
panel.add(getInstructionPanel(), BorderLayout.NORTH);
panel.add(getGamePanel(), BorderLayout.CENTER);
panel.add(getResetPanel(), BorderLayout.SOUTH);
return panel;
}
private JPanel getInstructionPanel()
{
JPanel panel = new JPanel();
panel.add(new JLabel("Guess what color!", JLabel.CENTER));
return panel;
}
private JPanel getGamePanel()
{
resultLabel = new JLabel("No selection made", icon, JLabel.CENTER);
resultLabel.setVerticalTextPosition(JLabel.BOTTOM);
resultLabel.setHorizontalTextPosition(JLabel.CENTER);
JPanel piecePanel = new JPanel();
int pieceCount = pieceChoices.size();
pieceButtons = new JButton[pieceCount];
for (int i = 0; i < pieceCount; i++)
{
pieceButtons[i] = createPiece(pieceChoices.get(i));
piecePanel.add(pieceButtons[i]);
}
JPanel panel = new JPanel(new BorderLayout());
panel.add(resultLabel, BorderLayout.CENTER);
panel.add(piecePanel, BorderLayout.SOUTH);
return panel;
}
private JPanel getResetPanel()
{
resetButton = new JButton("Reset");
JPanel panel = new JPanel();
panel.add(resetButton);
return panel;
}
private JButton createPiece(GamePiece piece)
{
JButton btn = new JButton();
btn.setIcon(new ColorIcon(16, piece.color));
btn.setActionCommand(piece.name());
return btn;
}
public void addPieceActionListener(GamePiece piece, ActionListener listener)
{
for (JButton button : pieceButtons)
{
if (button.getActionCommand().equals(piece.name()))
{
button.addActionListener(listener);
break;
}
}
}
public void addResetActionListener(ActionListener listener)
{
resetButton.addActionListener(listener);
}
private class ColorIcon implements Icon
{
private int size;
private Color color;
public ColorIcon(int size, Color color)
{
this.size = size;
this.color = color;
}
#Override
public void paintIcon(Component c, Graphics g, int x, int y)
{
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(color);
g2d.fillOval(x, y, size, size);
}
#Override
public int getIconWidth()
{
return size;
}
#Override
public int getIconHeight()
{
return size;
}
}
}
What might not be so obvious right away is how large the View interface can get. For each Swing component on the GUI, you may want to:
Add/Remove a listener to the component, of which there are many types (ActionListener, FocusListener, MouseListener, etc.)
Get/Set the data on the component
Set the "usability" state of the component (enabled, visible, editable, focusable, etc.)
This can get unwieldy really fast. As a solution (not shown in this example), a key is created for each field, and the GUI registers each component with it's key (a HashMap is used). Then, instead of the View defining methods such as:
public void addResetActionListener(ActionListener listener);
// and then repeat for every field that needs an ActionListener
you would have a single method:
public void addActionListener(SomeEnum someField, ActionListener listener);
where "SomeEnum" is an enum that defines all fields on a given UI. Then, when the GUI receives that call, it looks up the appropriate component to call that method on. All of this heavy lifting would get done in an abstract super class that implements View.
The Presenter: The responsibilities are:
Initialize the View with it's starting values
Respond to all user interactions on the View by attaching the appropriate listeners
Update the state of the View whenever necessary
Fetch all data from the View and pass to Model for saving (if necessary)
Code (note that there's no Swing in here):
import java.awt.*;
import java.awt.event.*;
public class Presenter
{
private Model model;
private View view;
public Presenter()
{
System.out.println("ctor");
}
public Presenter(Model model, View view)
{
this.model = model;
this.view = view;
}
public void start()
{
view.setGamePieces(model.getAllPieces());
reset();
view.addResetActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
reset();
}
});
for (int i = 0; i < GamePiece.values().length; i++)
{
final GamePiece aPiece = GamePiece.values()[i];
view.addPieceActionListener(aPiece, new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
pieceSelected(aPiece);
}
});
}
}
private void reset()
{
model.reset();
view.setResult(Color.GRAY, "Click a button.");
}
private void pieceSelected(GamePiece piece)
{
boolean valid = model.check(piece);
view.setResult(piece.color, valid ? "Win!" : "Keep trying.");
}
}
Keep in mind that each portion of the MVP architecture can/will be delegating to other classes (that are hidden to the other 2 portions) to perform many of its tasks. The Model, View, and Presenter classes are just the upper divisions in your code base heirarchy.