Vaadin: How to add 3 nested layouts - java

I'm having issues with my UI in vaadin at the moment. I have my views connected with RouterLayout like this:
-AppView (the main UI) | url: /
--OperationsView (a nested layout inside a container in AppView) | url: /operations
---Operation1View (a nested layout inside a container in OperationsView) | url: /operation1 <-
This isn't working
My declarations before any class are:
AppView declaration
#Route(value = AppView.ROUTE)
OperationsView declaration
#Route(value = OperationsView.ROUTE, layout = AppView.class)
Operation1View declaration
#Route(value = Operation1View.ROUTE, layout = OperationsView.class)
The problem is the third layout doesn't display correctly. It takes the whole page when accesed and mess up everything in the UI when going to another page. Shouldn't the url be: /operations/operation1 and not /operation1? However I can't get it to work correctly. Am I missing something? Or having 3 nested layouts is not possible with vaadin?
A possible solution (?): Should I dismiss the third nested layout and add methods in the second layout to remove the contents in the container and display the items I want? I really don't care about url navigation in this one. This is the last thing I can come up with.
Thanks in advance

Or having 3 nested layouts is not possible with vaadin?
It's possible. But are you implementing a RouterLayoutin both OperationsView and AppView classes?
Take a look into example here: Multiple parent layouts with #ParentLayout. It has a set-up pretty close to yours.
public class MainLayout extends Div implements RouterLayout {
}
#ParentLayout(MainLayout.class)
public class MenuBar extends Div implements RouterLayout {
public MenuBar() {
addMenuElement(TutorialView.class, "Tutorial");
addMenuElement(IconsView.class, "Icons");
}
private void addMenuElement(Class<? extends Component> navigationTarget,
String name) {
// implementation omitted
}
}
#Route(value = "tutorial", layout = MenuBar.class)
public class TutorialView extends Div {
}
#Route(value="icons", layout = MenuBar.class)
public class IconsView extends Div {
}
Shouldn't the url be: /operations/operation1 and not /operation1?
No, as in your #Router annotation you have specified that it's operation1. By specifying a layout you are defining the DOM structure, not the navigation route.From docs :
Sets the parent component for the route target component.When navigating between components that use the same layout, the same component instance is reused. Default layout target is the UI, but the layout should not be a custom UI as UI is a special class used to know where the route stack ends and no parent layouts should be involved.
All layout stacks will be appended to the UI as it represents the Body element.
BUT If you want it to be operation\operation1, you should use a #RoutePrefix instead ParentLayout Route Control
It takes the whole page when accesed and mess up everything in the UI when going to another page
Could you show a screenshot or add some details how it messes up?
Edit:
It's actually turned out to be harder to implement than I anticipated, but this seems to work:
MainView.java
#Route("")
public class MainView extends VerticalLayout implements RouterLayout {
....
OperationsView.java
//This is needed if you want "operations" to be accessible on its own
#Route(value = "operations",layout = MainView.class)
#ParentLayout(MainView.class)
public class OperationsView extends VerticalLayout implements RouterLayout {
Div content=new Div();
public OperationsView(){
System.out.println("operations view");
add(new Label("operations view"));
add(content);
}
}
Operation1View.java
#Route(value="operation1",layout = OperationsView.class)
#RoutePrefix("operations")
public class Operation1View extends VerticalLayout {
public Operation1View(){
add(new Label("Operations view"));
}
}

Related

Vaadin 14: Adding Footer application-wide to AppLayout

I have a Vaadin Application where the MainLayout is of type AppLayout. We have an application-wide Navbar defined in this MainLayout and it works as expected. However I couldn't find a way to do the same for a footer. Is there a way to add a footer to AppLayout so it shows up at the bottom of every view?
I have tried this solution and adding the footer with setContent() but it's not showing.
Would be great if there was a way because adding this manually to each view would be a bit of a pain.
Thank you!
You could always create a RouterLayout that you will chain with your AppLayout and have fixed Footer and some container for routing targets.
Like:
#Route("")
#ParentLayout(MainLayout.class) // AppLayout
public class FooterLayout extends VerticalLayout implements RouterLayout {
private Div container = new Div();
private Div footer = new Div();
public FooterLayout() {
add(container, footer);
footer.add(new Text("I'm a fixed footer!"));
}
public void showRouterLayoutContent(HasElement content) {
container.removeAll();
container.getElement().setChild(0, content.getElement());
}
}

How to add footer and header to other views using routerLayout in java using vaadin

I created a class named "Footer" having just a Label. I also have a few views like loginview, orderview). And I would like to add that footer to every view. This is my code until now.
#ParentLayout(OrderView.class)
public class footer extends VerticalLayout implements RouterLayout {
public footer() {
add(new Span("This text should be underneath the page in the views"));
}
}
Underneath you have my orederview where I want to see that text from the footerclass.
#Route("order")
public class OrderView extends VerticalLayout implements RouterLayout {
public OrderView (){
// What am I supposed to code here to get that text from Footerclass.
}
}
I would like to know what I am missing.
Thanks for the help.
Your usage of the annotations is wrong. Here is super simplified outline how you should create MainLayout and route that defines a component that is shown in the mainlayout when navigated to that route.
public class MainLayout extends VerticalLayout implements RouterLayout {
private Div childWrapper = new Div();
public void MainLayout() {
setSizeFull();
Span header = new Span("This text should be above the page in the views");
Span footer = new Span("This text should be underneath the page in the views");
add(header);
addAndExpand(childWrapper)
add(footer);
}
#Override
public void showRouterLayoutContent(HasElement content) {
childWrapper.getElement().appendChild(content.getElement());
}
}
#Route(value = "order", layout = MainLayout.class)
public class OrderView extends VerticalLayout {
public OrderView (){
}
}
There is video tutorial about Router concept on vaadin.com page that explains this in detail.
Instead of
new Label("This text should be underneath the page in the views");
try
add(new Span("This text should be underneath the page in the views"));
In Vaadin Flow the Label component is meant to be used coupled with another component, not for adding stand-alone text like in Vaadin 8 and older. Also, every component needs to be added to the layout before they can become visible, just creating them isn't enough.
Unfortunately LoginOverlay doesn't have API for adding extra components within it, but there is an open ticket about the feature that you can add thumbs up on to add more weight to it, and there's also a workaround presented in the comments: https://github.com/vaadin/web-components/issues/626

JavaFx how to avoid creating one huge controller

I have an app in JavaFX, which has main scene with menu and toolbar, and smaller scenes, which are injected into this main scene, after one of menu buttons are being pressed.
Now, HomeCntroller is responsible for either scene components: Home Scene (with toolbar and menu), and injected scene. This leads me to create massive, huge and very unprofessional controller if number of injected scenes is more than one.
How to split controller responsibility?
Now my Controller looks like this:
changeDashboardPane method injects smaller Pane into my main HomePane.
#Component
#RequiredArgsConstructor(onConstructor = #__(#Autowired) )
public class HomeController extends AbstractController {
private static final Logger LOG = Logger.getLogger(HomeController.class);
private final BudgetProfileService budgetProfileService;
#FXML
private Label usernameLabel;
#FXML
private ComboBox<String> budgetProfilesComboBox;
#FXML
private AnchorPane dashBoardPane;
#FXML
public void initialize() {
refreshUsernameLabel();
getAllBudgetProfiles();
changeDashboardPane(PaneFactoryKeys.FINANCES_PANE);
}
private void refreshUsernameLabel() {
String username = UserAccountProvider.getLoggedUser().getUsername();
usernameLabel.setText(username);
}
private void getAllBudgetProfiles() {
List<String> budgetProfileNames = budgetProfileService.getAllBudgetProfileNames();
if (!budgetProfileNames.isEmpty()) {
budgetProfilesComboBox.getItems().clear();
budgetProfilesComboBox.getItems().addAll(budgetProfileNames);
}
}
#FXML
public void handleFinancesButtonAction() {
changeDashboardPane(PaneFactoryKeys.FINANCES_PANE);
}
#FXML
public void handlePeriodButtonAction() {
changeDashboardPane(PaneFactoryKeys.PERIOD_PANE);
}
#FXML
public void handleStatisticsButtonAction() {
changeDashboardPane(PaneFactoryKeys.STATISTICS_PANE);
}
#FXML
public void handleSettingsButtonAction() {
changeDashboardPane(PaneFactoryKeys.SETTINGS_PANE);
}
private final void changeDashboardPane(String paneFactoryKey) {
double injectedPanePosition = 0.0;
Pane paneToChange = getPaneFromFactory(paneFactoryKey);
dashBoardPane.getChildren().clear();
AnchorPane.setTopAnchor(paneToChange, injectedPanePosition);
dashBoardPane.getChildren().add(paneToChange);
}
}
To get this more clear, screens:
without injected second pane
with injected second pane
Any ideas guys?
I would recommend you to divide your main scene in smaller ones, for example you can have a tools scene, a header scene, a content scene and so on. Then you should have one controller for every scene.
After that I would use a publisher-subscriber pattern to deal with behaviors, like when you press a button on settings scene, it triggers an event that other scenes listen to and then they handle it changing their state accordingly.
I hope it was clear and can help!
Create multiple controllers , multiple FXML files - to continue on my answer that i provided you before, JavaFX how to inject new FXML content to current Scene each of those views that have separate fxml file also has
fx:controller="appplication.ExampleViewController"
attached to it.So what you do is create main controller as was mentioned , that is basically the FRAME CONTAINER that encapsulates controls to change your dynamic container.If your application is really ui rich and have a lot of functionality in one controller , you can break down your view even further:
For instance take out menu and put it into separated controller , and insert it into your main view with main controller
/same way as in method setView()/
, what you are doing is just taking it away to keep controller code smaller, YOU DONT DECREASE/INCREASE SCENE GRAPH THIS WAY, doesnt have a drawback its just a personal preference.
You gonna end up with more fxml files and controllers in the end.Its all the same thing as from your previous question there is no additional code needed you can actually reuse what was already provided.
Data between controllers are passed thru MODEL. - look more into MVC dont work with application data in controllers only care about view or passing them from/into model
To avoid a huge contoller class, as I am using multiple tabs, I split the tabs to single java files.
My solution was to create a cascade of classes:
Base: Containing all defs for FX types
Tab1 extends Base: Tab one implementation
Tab2 extends Tab1: Tab two implementation
Controller extends Tab2 implements Initializable: Implements initialize(URL url, ResourceBundle resourceBundle)
Important:
Any accessed object must be definded in the current tab or before.
Any Objects in Base are available in Controller whereas no object of Controller is accessable in Base, Tab1 or Tab2.
Feel free to add your opinion as comment or submit a improvement.

Adding to GWT RootPanel or just setting the default view?

I have the following GWT classes:
public class MyDefaultView extends Composite {
// Uses UiBinder and just contains all the widgets for this view.
}
public class MyDefaultActivity extends AbstractActivity {
#Inject
private MyDefaultView myDefView;
#Override
public void start(AcceptsOneWidget panel, EventBus eventBus) {
panel.setWidget(myDefView);
}
}
public class MyActivityMapper implements ActivityMapper {
#Override
public Activity getActivity(Place place) {
if(place instanceof MyDefaultPlace)
return new MyDefaultActivity();
else
return null;
}
}
public class MyAppModule implements EntryPoint {
#Override
public void onModuleLoad() {
// Lots of initialization and wiring...
// Why do I need this?!?
MyDefaultView myDefaultView = GWT.create(MyDefaultView.class);
RootPanel.add(myDefaultView);
Place myDefaultPlace = GWT.create(MyDefaultPlace.class);
PlaceHistoryHandler historyHandler = getHistoryHandler();
historyHandler.register(myPlaceController, myEventBus, myDefaultPlace);
historyHandler.handleCurrentHistory();
}
}
Why do I need to add MyDefaultView to RootPanel, if I'm just going to call PlaceHistoryHandler#handleCurrentHistory() and display MyDefaultView when the module loads?
If I shouldn't be adding MyDefaultView directly to RootPanel, then what should I be adding?
Thanks in advance!
1) You don't need to add MyDefaultView, but you need to a panel that implements AcceptsOneWidget and set that panel on the activity manager. This will take care of having your views made visible.
2) In most applications you have a part of the application that is always visible. For example a bar at the top showing among other things the user name. This kind panel needs to be added to the root panel. In that panel on the position where your views should be visible a widget/panel that implements AcceptsOneWidget should be used. This widget should be set as display in your activityManager, via setDisplay. That widget will be passed to the start method in your activity. Here is how the code to use with the ActivityManager and RootPanel could look like:
final ActivityManager activityManager = new ActivityManager(myActivityMapper, eventBus);
activityManager.setDisplay(rootView.getViewPanel());
Rootpanel.add(rootView);

GWT-Platform revealing presenters together

Hey so I am just learning the gwtp framework and I have come across a bit of a dilemma. I have a LayoutPresenter at the top level that has a main content slot and menu content slot and I am trying to find a way to bind my presenters for each slot together if possible so when the main content is revealed it will automatically show the correct side menu. Currently I have a static boolean in the Menu's Presenter that get updated onReveal and onHide. I can then check if the menu is visible when the main content is revealed and if not I reveal it.
public class MenuPresenter extends Presenter<MenuPresenter.MyView, MenuPresenter.MyProxy> {
private static boolean hidden = true;
...
#Override
protected void revealInParent() {
RevealContentEvent.fire(this, LayoutPresenter.SIDE, this);
}
#Override
protected void onReveal(){
super.onReveal();
hidden = false;
}
#Override
protected void onHide(){
super.onHide();
hidden = true;
}
public static boolean isHidden(){
return hidden;
}
}
Then in The main content Presenter:
public class ContentPresenter extends
Presenter<ContentPresenter.MyView, ContentPresenter.MyProxy> {
...
private final DispatchAsync dispather;
private final PlaceManager placeManager;
#Inject
public PhoneCallPresenter(final EventBus eventBus, final MyView view, final MyProxy proxy, final DispatchAsync dispatcher, final PlaceManager placeManager) {
super(eventBus, view, proxy);
this.dispather = dispatcher;
this.placeManager = placeManager;
}
#Override
protected void revealInParent() {
RevealContentEvent.fire(this, LayoutPresenter.CONTENT, this);
}
#Override
protected void onReveal() {
super.onReveal();
if (MenuPresenter.isHidden()){
placeManager.revealPlace(new PlaceRequest(NameTokens.menu));
}
}
}
As far as I understood the question, you want to have different side-menus for different main contents.
In this case there are two solutions:
Treat the menu as a normal Presenter (you will probably have multiple of them for each main content type). You just need to annotate the corresponding MenuPresenter with the same history token as your main content Presenter. So for the above example you would have a PhoneCallMenuPresenter that is annotated with the same history token as your PhoneCallPresenter. When you navigate to /phonecall (or whatever your history token is), both PhoneCallPresenter and PhoneCallMenuPresenter will be revealed automatically . (you don't have to do anything).
In case you want to have only one MenuPresenter and put the logic what to display in the Presenter itself, I would recommend to use a PresenterWidget instead of a normal Presenter. The MenuPresenterWidget will be injected into the LayoutPresenter and will be added to the LayoutPresenter.SIDE slot. You can define a setter for the MenuPresenterWidget to specify which main content is currently displayed (the setter will be called from the LayoutPresenter or you can override the onReset() method and check the current place request and decide what to display in the menu.
For solution 1 you have to have one MenuPresenter for each main content Presenter and potentially many code lines will be redundant (you could create a base MenuPresenter and derive from it). So in case you have a lot of business logic in the side-menu which is quite different from main content to main content, I would go with solution 1. In case you only display different links the overhead of creating a MenuPresenter per main content Presenter might be to high and I would go with solution 2 and create only one MenuPresenterWidget for all main content types and always show it.

Categories