I have a pretty simple class that basically is just an AppLayout with some Tab.
Now my issue. I am not able to find a smart way to display different contents for the Tabs-class. Is there any interface or something that can be called to differ the content for the Tab?
class MainAppView extends AppLayout {
public MainAppView()
{
createDrawerAndAddToAppView();
}
void createDrawerAndAddToAppView()
{
Tabs tabs = createTabsForDrawer();
tabs.setOrientation(Tabs.Orientation.VERTICAL);
addToDrawer(tabs);
H1 a = new H1("Test"); // Is displayed as content for every Tab
tabs.addSelectedChangeListener(selectedChangeEvent ->
/**
* How to get the specific content of a Tab here?
*/
//selectedChangeEvent.getSelectedTab(). //getContent() and put in super.setContent()?
super.setContent(a)); // Displays 'Test' as content for every Tab
// The Listener shall display the specific content of the getSelectedTab()
}
private Tabs createTabsForDrawer()
{
return new Tabs(
new Tab("Home"),
new Tab("Dummy"),
new Tab("Test"));
}
}
Here is one example, using a map to keep track of which content belongs to which tab. In reality your tab content would be more complicated, and maybe be created in it's own method.
#Route
public class TabTest extends VerticalLayout {
private Map<Tab, Component> tabComponentMap = new LinkedHashMap<>();
public TabTest() {
Tabs tabs = createTabs();
Div contentContainer = new Div();
add(tabs, contentContainer);
tabs.addSelectedChangeListener(e -> {
contentContainer.removeAll();
contentContainer.add(tabComponentMap.get(e.getSelectedTab()));
});
// Set initial content
contentContainer.add(tabComponentMap.get(tabs.getSelectedTab()));
}
private Tabs createTabs() {
tabComponentMap.put(new Tab("Show some text"), new H1("This is the text tab"));
tabComponentMap.put(new Tab("Show a Combo Box"), new ComboBox<String>());
tabComponentMap.put(new Tab("Show a button"), new Button("Click me and nothing happens"));
return new Tabs(tabComponentMap.keySet().toArray(new Tab[]{}));
}
}
You can do something similar with routes also, but then you would probably want your containing component to be a RouterLayout. Also this requires a bit more logic if you want to automatically select the correct tab after navigating from somewhere else.
#Route
public class TabTest extends VerticalLayout implements RouterLayout {
private Map<Tab, String> tabToUrlMap = new LinkedHashMap<>();
private Div contentContainer = new Div();
public TabTest() {
Tabs tabs = createTabs();
Div contentContainer = new Div();
contentContainer.setSizeFull();
add(tabs, contentContainer);
tabs.addSelectedChangeListener(e ->
UI.getCurrent().navigate(tabToUrlMap.get(e.getSelectedTab())));
}
private Tabs createTabs() {
RouteConfiguration routeConfiguration = RouteConfiguration.forApplicationScope();
tabToUrlMap.put(new Tab("View 1"), routeConfiguration.getUrl(TestView1.class));
tabToUrlMap.put(new Tab("View 2"), routeConfiguration.getUrl(TestView2.class));
tabToUrlMap.put(new Tab("View 3"), routeConfiguration.getUrl(TestView3.class));
return new Tabs(tabToUrlMap.keySet().toArray(new Tab[]{}));
}
#Override
public void showRouterLayoutContent(HasElement content) {
getElement().appendChild(content.getElement());
}
}
And an example view
#Route(layout = TabTest.class)
public class TestView3 extends VerticalLayout {
public TestView3() {
add("View 3");
}
}
Related
I want to make tabs in tab in Vaadin. I'm using tabs with routes https://cookbook.vaadin.com/tabs-with-routes/a . It is working fine when I have only one level of tabs. I dont know how to display Main Tabs, tabs under and its content. How to achieve something like that:
MainView.java
#Route("")
public class MainView extends VerticalLayout implements RouterLayout {
private Map<Tab, String> tabToUrlMap = new LinkedHashMap<>();
public MainView(){
H1 title = new H1("CMS");
Tabs tabs = getTabs();
UI.getCurrent().navigate(OrderGUI.class);
tabs.addSelectedChangeListener(e -> UI.getCurrent().navigate(tabToUrlMap.get(e.getSelectedTab())));
add(title, tabs);
}
private Tabs getTabs() {
RouteConfiguration routeConfiguration = RouteConfiguration.forApplicationScope();
tabToUrlMap.put(new Tab("Zamówienia"), routeConfiguration.getUrl(OrderGUI.class));
//code for rest tabs
Tabs tabs = new Tabs(tabToUrlMap.keySet().toArray(new Tab[]{}));
tabs.getStyle().set("margin", "auto");
return tabs;
}
}
OrderGUI.java
#Route(value="zamowienia", layout = MainView.class)
public class OrderGUI extends VerticalLayout implements RouterLayout{
private Map<Tab, String> tabToUrlMapOrder = new LinkedHashMap<>();
public OrderGUI() {
//super();
Tabs tabs = getTabsOrder();
UI.getCurrent().navigate(OrderedOrderGUI.class);//pierwsza zakładka jako główna
tabs.addSelectedChangeListener(e -> UI.getCurrent().navigate(tabToUrlMapOrder.get(e.getSelectedTab())));
add(tabs);
}
private Tabs getTabsOrder() {
RouteConfiguration routeConfiguration = RouteConfiguration.forApplicationScope();
tabToUrlMapOrder.put(new Tab("Złożone"), routeConfiguration.getUrl(OrderedOrderGUI.class));
tabToUrlMapOrder.put(new Tab("W produkcji"), routeConfiguration.getUrl(ProductionOrderGUI.class));
tabToUrlMapOrder.put(new Tab("Gotowe"), routeConfiguration.getUrl(ReadyOrderGUI.class));
Tabs tabs = new Tabs(tabToUrlMapOrder.keySet().toArray(new Tab[]{}));
tabs.getStyle().set("margin", "auto");
return tabs;
}
}
OrderedOrderGUI.java
#Route(value="zamowienia/zlozone", layout = OrderGUI.class)
public class OrderedOrderGUI extends VerticalLayout {
public OrderedOrderGUI() {
add(new Text("some content"));
}
}
Problem solved, I had to add annotation #ParentLayout(MainView.class) in OrderGUI.java
I work on desktop application based on JDK 8 and JavaFX.
I created custom dialog class with 2 buttons(finish and cancel). My goal is to return the list of strings added in dialog (after clicking finish button, dialog returns list. Cancel makes return empty list).
I have problem, beacause function showAndWait return type of button which I clicked ('ButtonType.FINISH' or 'ButtonType.CANCEL'). My goal is to override default action on finish and close button and I want to return list instead of return button type.
It's always possible to create custom buttons, however, it would be better to use those already provided by JavaFX.
In response, you can use any of the JVM languages (Java/Kotlin/Scala).
Code:
class MyDialog : Dialog<MutableList<String>>() {
val listToReturn: MutableList<String> = mutableListOf()
init {
val dialogPane: DialogPane = this.dialogPane
dialogPane.buttonTypes.addAll(ButtonType.FINISH, ButtonType.CANCEL)
}
}
val myDialog: MyDialog = MyDialog()
// here I got ButtonType ('ButtonType.FINISH' or 'ButtonType.CANCEL'), not list of string
myDialog.showAndWait().ifPresent { list -> println(list) }
You need to use a result converter
public class MyDialog extends Dialog<List<String>> {
ArrayList<String> returnList = new ArrayList<>();
public MyDialog() {
returnList.add("test 1");
returnList.add("test 2");
this.getDialogPane().getButtonTypes().addAll(ButtonType.FINISH, ButtonType.CANCEL);
setResultConverter(dialogButton -> {
if (dialogButton == ButtonType.FINISH) {
return returnList;
}
return new ArrayList<>();
});
}
}
and for the application side
public class main extends Application {
public static void main (String [] args) {
launch();
}
#Override
public void start(Stage primaryStage) {
MyDialog myDialog = new MyDialog();
myDialog.showAndWait().ifPresent(System.out::println);
}
}
I got a strange issue. My web application works as it don't remove text/numbers from text boxes or resetting check boxes when I refresh the web page from the web browser.
But when I refresh the web page then I got the problem:
java.lang.IllegalStateException: Can't move a node from one state tree to another. If this is intentional, first remove the node from its current state tree by calling removeFromTree
java.lang.IllegalStateException: Unregistered node was not found based on its id. The tree is most likely corrupted.
And
Assertion error: No child node found with id 28
Assertion error: Node 3 is already registered
So why can't I set the content again after I have fresh the web page in Vaadin? I'm using Vaadin 14. If I don't refresh the web page, then I can change the content as much as I want. But as long I don't refresh the web page, then I will not get an error.
Notice that I have #PreserveOnRefresh enabled. Without that, I get no error. But then the text/values and all information will disappear when I refresh the page.
Here is my code.
#Route("")
#Viewport("width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes, viewport-fit=cover")
#PreserveOnRefresh
public class MainView extends AppLayout {
/**
*
*/
private static final long serialVersionUID = 1L;
public MainView() {
// Get the components
BuildPredictValidateTemplate buildPredictValidateTemplate = new BuildPredictValidateTemplate();
LoadExportTemplate loadExportTemplate = new LoadExportTemplate();
// Create logo and drawer
Image barImage = new Image("img/barImage.png", "Fisherfaces Logo");
barImage.setHeight("55px");
addToNavbar(new DrawerToggle(), barImage);
// Create tabs and add listeners to them
Tab buildPredictValidate = new Tab("Build & Predict & Validate");
buildPredictValidate.getElement().addEventListener("click", e -> {
setContent(buildPredictValidateTemplate.getBuildButtonPredictButtonValidateButtonTextArea());
});
Tab loadExport = new Tab("Load & Export");
loadExport.getElement().addEventListener("click", e -> {
setContent(loadExportTemplate.getSubjectCounterExportButtonUploaders());
});
// Add them and place them as vertical
Tabs tabs = new Tabs(buildPredictValidate, loadExport);
tabs.setOrientation(Tabs.Orientation.VERTICAL);
addToDrawer(tabs);
}
}
And
#Data
//#Component
public class BuildPredictValidateTemplate {
private VerticalLayout buildButtonPredictButtonValidateButtonTextArea;
public BuildPredictValidateTemplate() {
// Create the complete form layout
buildButtonPredictButtonValidateButtonTextArea = createBuildButtonPredictButtonValidateButtonTextArea();
}
private VerticalLayout createBuildButtonPredictButtonValidateButtonTextArea() {
// Text area that works like a terminal
TextArea textTerminal = new TextArea();
textTerminal.setPlaceholder("");
textTerminal.setWidthFull();
textTerminal.setHeightFull();
// Progressbar
ProgressBar progressBar = new ProgressBar();
progressBar.setValue(0);
// Buttons for Builing, Predicting and Validate
Button build = new Button("Build");
build.addClickListener(e -> {
System.out.println("Building");
});
Button predict = new Button("Predict");
predict.addClickListener(e -> {
System.out.println("Predicting");
});
Button validate = new Button("Validate");
validate.addClickListener(e -> {
System.out.println("Validating");
});
// Uploader for prediction
//Upload upload = new PictureUpload().getUpload();
// Add them all now
HorizontalLayout horizon = new HorizontalLayout(build, validate, predict);
return new VerticalLayout(horizon, progressBar, textTerminal);
}
}
And also
#Data
//#Component
public class LoadExportTemplate {
private VerticalLayout subjectCounterExportButtonUploaders;
public LoadExportTemplate() {
// Create layout for the uploads
VerticalLayout uploadsLayout = new VerticalLayout();
// Create subject counter for how many uploaders we should have
NumberField subjectCounter = createSubjectCounter(uploadsLayout);
// Create layout for holding subject counter, export button and uploaders
subjectCounterExportButtonUploaders = createLayoutForSubjectCounterExportButtonUploaders(subjectCounter, uploadsLayout);
}
private VerticalLayout createLayoutForSubjectCounterExportButtonUploaders(NumberField subjectCounter, VerticalLayout uploadsLayout) {
// Create SubjectCounter and ExportButton on a row
Button exportButton = new Button("Export to MATLAB code");
exportButton.addClickListener(e -> {
System.out.println("Exported to MATLAB code.");
});
HorizontalLayout layoutHorizon = new HorizontalLayout(subjectCounter, exportButton);
// Add the uploaders under the horizontal layout
return new VerticalLayout(layoutHorizon, uploadsLayout);
}
private NumberField createSubjectCounter(VerticalLayout uploadsLayout) {
NumberField subjectCounter = new NumberField();
subjectCounter.setValue(1d);
subjectCounter.setHasControls(true);
subjectCounter.setMin(1);
subjectCounter.addValueChangeListener(e-> {
// First clear, then fill with new uploaders
uploadsLayout.removeAll();
for(Double i = 0.0; i < e.getValue(); i++) {
PictureUpload pictureUpload = new PictureUpload();
uploadsLayout.add(pictureUpload.getUpload());
}
});
// Add one to begin with
//PictureUpload pictureUpload = new PictureUpload();
//uploadsLayout.add(pictureUpload.getUpload());
return subjectCounter;
}
}
I believe this is a bug in Vaadin, at least it is not what I would expect to happen.
I have created an issue for it here https://github.com/vaadin/flow/issues/8286
Edit:
As a workaround, you can toggle visibility instead. But this would require you to add all components to e.g. a Div, and set that as the content.
E.g setContent(new Div(component1, component2));
Then when clicking on a tab, you would have to hide all components except the one clicked, e.g.
getContent().getChildren().forEach(component -> {
boolean visible = component.equals(theComponentIWantToShow);
component.setVisible(visible);
});
I would like to have this functionality in my program:
I will have a user input field. When the user pressed the button, it will be added to the list, and input will be shown to the user.
The problem is, I would like to deselect/remove those input if the user wants. I could not achieve this.
Here is the code I have written so far, I have removed some functionality unnecessary for the question's scope:
public class AddUserInput extends VerticalLayout{
// The user input will be added to the this list
// later, this list will be sent to the server for some verification
private List<String> emails;
private HorizontalLayout content;
private VerticalLayout rows;
// user input field
private TextField emailField = new TextField("Enter email address");
public AddUserInput() {
content = new HorizontalLayout();
rows = new VerticalLayout();
content.setMargin(true);
Button addToListButton= new Button("Add to list");
addToListButton.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(Button.ClickEvent event) {
// When the user clicks add to list button
// The raw input will be added to the emails list
// The UI component is added to 'rows' component
rows.addComponent(addNewRow(emailField.getValue()));
}
});
content.addComponents(emailField, addToListButton, rows);
addComponent(content);
}
public Component addNewRow(String email){
HorizontalLayout newRow = new HorizontalLayout();
Button deleteRowButton = new Button("-");
deleteRowButton.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(Button.ClickEvent event) {
// I can delete from the UI by using the code below
newRow.removeAllComponents();
rows.removeComponent(newRow);
// How to remove from the email list???
}
});
emails.add(emailField.getValue());
Label lastEmail = new Label(emailField.getValue());
emailField.clear();
newRow.addComponents(lastEmail,deleteRowButton);
return newRow;
}
}
Is there any component/library that does this functionality?
I only need a text field, and adding the input to the list, and removing the list item if a user wants to.
The visualization of the code above:
You could use the NativeSelect component for managing the entered Strings.
I modified your AddUserInput-Component to use a NativeSelect and a corresponding DataProvider:
public class AddUserInput extends VerticalLayout {
private HorizontalLayout content = new HorizontalLayout();;
private NativeSelect<String> select = new NativeSelect<>("The List");
private ListDataProvider<String> dataProvider = DataProvider.ofCollection(new ArrayList<>());
private Button addToListButton= new Button("Add to list");
private Button deleteFromListButton = new Button("-");
private TextField emailField = new TextField("Enter email address");
public AddUserInput() {
select.setVisibleItemCount(5);
select.setWidth("100px");
select.setDataProvider(dataProvider);
select.setEmptySelectionAllowed(false);
deleteFromListButton.setEnabled(false);
content.setMargin(true);
addToListButton.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(Button.ClickEvent event) {
addEmailToList(emailField.getValue());
}
});
deleteFromListButton.addClickListener(new Button.ClickListener() {
#Override
public void buttonClick(Button.ClickEvent clickEvent) {
select.getSelectedItem().ifPresent(selectedItem -> removeSelectedEmailFromList());
}
});
select.addValueChangeListener(new HasValue.ValueChangeListener<String>() {
#Override
public void valueChange(HasValue.ValueChangeEvent<String> valueChangeEvent) {
deleteFromListButton.setEnabled(select.getSelectedItem().isPresent());
}
});
content.addComponents(emailField, addToListButton, select, deleteFromListButton);
addComponent(content);
}
private void addEmailToList(String email){
dataProvider.getItems().add(email);
select.getDataProvider().refreshAll();
emailField.clear();
}
private void removeSelectedEmailFromList(){
select.getSelectedItem().ifPresent(selectedItem -> dataProvider.getItems().remove(selectedItem));
select.setSelectedItem(dataProvider.getItems().isEmpty() ? null : dataProvider.getItems().iterator().next());
select.getDataProvider().refreshAll();
}
}
It looks like the following:
Would that be a possible option for you?
Im facing overlaps problem with my project when trying to resize browser.
I was trying so many different variations to make it work, but still result is not acceptable.
Before resizing:
A, B and C are contained in VerticalLayout - I will call it root.
Root is inside HorizontalLayout - content of UI.
A is simple component.
B is extending VerticalLayout that contains 2 HorizontalLayouts inside.
C is only one component - Grid.
Now, when Im trying to resize my browser (like arrow shows) C is starting to steal other components place.
After resizing:
The effect I would like to achieve is that my Grid (C) is not trying to fit my browser. It should not move, and just hide - like below (green is showing actually visible part):
/*ROOT class that extends VerticalLayout*/
private void init()
{
super.setSizeFull();
addA();
addB();
addC();
}
private void addA()
{
Label header = new Label();
super.addComponent(header);
super.setComponentAlignment(header, Alignment.MIDDLE_CENTER);
}
private void addB()
{
layoutB.setSizeFull();
layoutB.setWidth("92%");
super.addComponentsAndExpand(layoutB);
super.setExpandRatio(layoutB, 0.3f);
super.setComponentAlignment(layoutB, Alignment.MIDDLE_CENTER);
}
private void addC()
{
grid.setSizeFull();
grid.setColumnReorderingAllowed(true);
grid.setWidth("92%");
super.addComponentsAndExpand(grid);
super.setExpandRatio(grid, 0.6f);
super.setComponentAlignment(grid, Alignment.BOTTOM_CENTER);
}
As you can see C is added in the same way as B, but only C is moving. Thanks in advance for any help!
Im using Vaadin 8.
#Edit:
#SpringUI(path = "/ui")
public class MyUI extends UI {
#Override
protected void init(VaadinRequest request)
{
Workspace workspace = new Workspace();
HorizontalLayout root = new HorizontalLayout();
root.setSizeFull();
root.addComponentsAndExpand(workspace);
setContent(root);
}
public class Workspace extends Panel
{
public Workspace()
{
init();
}
private void init()
{
setSizeFull();
addStyleName(ValoTheme.PANEL_BORDERLESS);
VerticalLayout layout = new VerticalLayout();
// by default width 100% and height undefined in Vaadin 8
setContent(layout);
// component A
Label label = new Label("Test1");
layout.addComponent(label);
// component B
HorizontalLayout bar = new HorizontalLayout();
bar.addComponents(new Label("Label 1"), new Label("Label 2"));
layout.addComponent(bar);
// component C
Grid<MyBean> grid = new Grid<>(MyBean.class);
grid.setCaption("My Grid:");
grid.setHeight("1000px");
//grid.setHeightByRows(50); // same as fixed height
List<MyBean> items = new LinkedList<>();
IntStream.range(1, 100).forEach(i -> items.add(new MyBean("Item " + i)));
grid.setItems(items);
layout.addComponent(grid);
}
}
public static class MyBean {
private String name;
public MyBean(String name) { this.name = name; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
}
Have a look at this working example:
#SpringUI(path = "/ui")
public class MyUI extends UI {
#Override
protected void init(VaadinRequest request) {
Panel panel = new Panel();
panel.addStyleName(ValoTheme.PANEL_BORDERLESS);
panel.setSizeFull();
VerticalLayout layout = new VerticalLayout();
// by default width 100% and height undefined in Vaadin 8
panel.setContent(layout);
// component A
Label label = new Label("Test1");
layout.addComponent(label);
// component B
HorizontalLayout bar = new HorizontalLayout();
bar.addComponents(new Label("Label 1"), new Label("Label 2"));
layout.addComponent(bar);
// component C
Grid<MyBean> grid = new Grid<>(MyBean.class);
grid.setCaption("My Grid:");
grid.setHeight("1000px");
//grid.setHeightByRows(50); // same as fixed height
List<MyBean> items = new LinkedList<>();
IntStream.range(1, 100).forEach(i -> items.add(new MyBean("Item " + i)));
grid.setItems(items);
layout.addComponent(grid);
setContent(panel);
}
public static class MyBean {
private String name;
public MyBean(String name) { this.name = name; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
}
The Grid has a fixed height (either by pixels or by number of rows). No expand ratios are necessary for the VerticalLayout. The layout within the Panel will grow as needed by its child components. If the height is greater than the space available for the Panel then scroll bars are shown.