Spring(-Boot) & Vaadin - Set default-view in Vaadin navigator - java

first of all I have here a simple UI which has a addMenueItem() Method. This method gets a view-id and adds a button to the menue and tells the navigator from Vaadin to navigate to it on click:
.........
#PostConstruct
private void initPage() {
navigator.addProvider(viewProvider);
contentLayout.setSizeFull();
}
protected void addMenuItem(final String viewId) {
final String postfix;
if (viewId == null || viewId.trim().isEmpty()) {
postfix = "";
} else {
postfix = "." + viewId;
}
final String name = messageByLocaleService.getMessage(I18N_PREFIX + postfix);
menue.addItem(name, menueCommand -> getUI().getNavigator().navigateTo(viewId));
}
.........
So I'm adding some views from my MainUI like:
addMenuItem(DefaultView.VIEW_ID);
The Spring url based view resolver calls the now the id from the value of 'DefaultView.VIEW_ID'. Now I'm looking for a good solution to map a given view-id not only to ' /VIEW-ID ' but also to the application root path --> ' / '.
How can I tell Spring that this special ViewID is also the root or an synonym for /VIEW-ID ?
For sure there is a possibility to hard-code this in some kind of xml-file of Tomcat or something, but i would like to do so dynamically.
Thanks in advance
PS: I'm very new to spring and this kind of stuff, courtesy please :D

The navigator and the URI fragment go hand in hand. The root path does not have any URI fragment therefore the navigator does not know where to navigate when the URL is called.
You can solve that problem in your UI class like this:
#SpringUI
public class MainUI extends UI {
#Autowired
SpringViewProvider viewProvider;
#Override
protected void init(VaadinRequest request){
Navigator navigator = new Navigator(this,this);
navigator.addProvider(viewProvider);
this.setNavigator(navigator);
// Set default view
NavigationStateManager stateManager = new Navigator.UriFragmentManager(getPage());
stateManager.setState(DefaultView.VIEW_ID);
}
}
Explanation:
Most of the stuff should be well known already because these are basics that can be read in several tutorials, e.g.: III Views and Navigation with Vaadin Spring
In short:
You use the #SpringUI annotation, which by default maps to /, to register a new UI
You autowire the SpringViewProvider which automatically registers your views.
You initialize the navigator.
To set the default view you simply initialize the NavigationStateManager which sets the URI fragment. Once the init method completes the UI class calles the Navigator.navigateTo() method with the current state of the NavigationStateManager.
NOTE: You might find something in the net like:
navigator.navigateTo(DefaultView.VIEW_ID);
If you use this in the init method of the UI your navigator is called twice.

Related

Vaadin: How to add 3 nested layouts

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"));
}
}

Pass and receive data in Vaadin 8

I have a view:
#SpringUI(path="order")
#Title("order")
public class OrderGUI extends UI{
and I would pass parameter to other:
#SpringUI(path="orderNumber")
#Title("Order number")
public class GetOrderNumber extends UI {
I tried to send parameter(orderNumber) by:
getUI().getPage().setUriFragment(orderNumber);
getUI().getPage().setLocation("orderNumber");
then it goes to /orderNumber but when tried to catch it by:
String fragment = getPage().getUriFragment();
but
System.out.println("Fragment: " + fragment);
says that Fragment: null
How to send parameter from one Vaadin view to another?
#Edit
It is close to work. I have changed my View to:
#SpringView(name = "GetOrderNumber")
#Title("Order number")
public class GetOrderNumber extends VerticalLayout implements View {
and in #SpringUI made getUI().getNavigator().navigateTo("GetOrderNumber/" + orderNumber);
it actually still throws java.lang.IllegalArgumentException: Trying to navigate to an unknown state '' and an error view provider not present but it works. I mean all the time when I go to localhost:8080/order the
#SpringUI(path="order")
public class OrderGUI extends UI {
throws an error but then works as expected - go to view and pass parameter right. I have no idea why navigator = new Navigator(this, this); cause an error
#SpringUI annotation defines always just the name of the view, it is not the place where you give the URI parameter, the correct place is the navigateTo(..) method, e.g.:
getUI().getNavigator().navigateTo("myview/someparameter");
There is more information about Vaadin and Spring add-on here
https://vaadin.com/docs/v8/framework/advanced/advanced-spring.html

How can I switch perspective programmatically after starting an E4 application?

Scenario
I have a pure E4 application in which I want to select the initial perspective for the user depending on the user's roles. I therefore have a perspective to start with which contains one part only. In that part, I use the #PostConstruct-Method to check the user's roles and afterwards trigger the command for switching perspective:
Initial View
#Inject
private IEclipseContext eclipseContext;
#PostConstruct
public void initialize() {
// checking credentials and retrieving roles come here which is pretty long
// that's why switching perspective is a seperate method
// and EclipseContext is injected to instance instead of method
this.switchPerspective(_usersInitialPerspectiveId)
}
private void switchPerspective(String pTargetPerspectiveId) {
final ECommandService _commandService = this.eclipseContext.get(ECommandService.class);
final EHandlerService _handlerService = this.eclipseContext.get(EHandlerService.class);
final Map<String, Object> _commandParameter = new HashMap<>();
_commandParameter.put(PluginIdConstants.ID_OF_PARAMETER_FOR_SWITCH_PERSPEKTIVE,
pZielPerspektiveId);
final ParameterizedCommand _switchPerspectiveCommand =
_commandService.createCommand(COMMAND_ID_FOR_SWITCH_PERSPECTIVE,
_commandParameter);
_handlerService.executeHandler(_switchPerspectiveCommand);
}
For switching perspective from here, I use the exact same handler as from menu items configured in Application.e4xmi, which looks like this:
Perspective Switch Handler
#Execute
public void execute(final MWindow pWindow,
final EPartService pPartService,
final EModelService pModelService,
#Named(PluginIdConstants.ID_OF_PARAMETER_FOR_SWITCH_PERSPEKTIVE)
final String pPerspectiveId) {
final List<MPerspective> _perspectives =
pModelService.findElements(pWindow, pPerspectiveId, MPerspective.class, null);
if (!(_perspectives.isEmpty())) {
// Show perspective for looked up id
pPartService.switchPerspective(_perspectives.get(0));
}
}
The Problem
The problem is pretty simple: When using the above handler triggered by a menu item, it works as intended and switches perspective. But using the same handler by my initial view (triggering it programmatically) does not switch perspective. I debugged the code to check if the handler gets identical information in both cases and it does.
Maybe my application has not finished starting and that's why the handler has no effect, but if this is the problem, how can I check this?
Any ideas on what I maybe missed are welcome!
Based on Christoph Keimel's hint I could create a working solution (thank you very much!). Here's the code that solves the problem:
#ProcessAdditions
private void switchPerspective(final MApplication pApplication,
final IApplicationContext pApplicationContext,
final EModelService pModelService) {
final MWindow _window =
(MWindow) pModelService.find(PluginIdConstants.WINDOW_ID_FOR_MAIN, pApplication);
final String _appName = pApplicationContext.getBrandingName();
initializeWindowTitle(_window, _appName);
final MPerspectiveStack pPerspectiveStack =
(MPerspectiveStack) pModelService.find(PluginIdConstants.PERSPECTIVE_STACK_ID_FOR_MAIN,
pAnwendung);
for (final MPerspective _perspective : pPerspectiveStack.getChildren()) {
if (_perspektive.getElementId().equalsIgnoreCase(this.startingPerspectiveId)) {
pPerspectiveStack.setSelectedElement(_perspective);
break;
}
}
}
On how to register a LifeCycleHandler you can take a look at Lars Vogel's Tutorial.
My main problem finding this solution was how to access the perspective stack. As the UI is not up while the method annotated with ProcessAdditions is running, I have to access the application model via the MApplication type - which is the root element of my application model. Combining the EModelService I can access all UI elements I want and manipulate them accordingly.
Injecting any UI element like the MPerspectiveStack or the MWindow leads to a skipped method as these result in null values due to not being initalized yet.

GXT - send forms on enter, project-wide

I have an application which uses GXT and contains ±30 forms. I would like to make these forms so that when the user hits enter in a text field, the form gets submitted, like a regular browser form would.
I know I can add a key press listener to every text field, which would invoke the submit after enter is pressed, but since I want to apply this to every field in every form I am not sure if this is ideal.
Is there a simpler way to implement this in the entire application?
If not, which pattern should I use to add this functionality to every field? I can extend the TextField class, add the functionality in the child class and use the child class in the application. Or I can create a factory for the text field class which would also add the listener to the field. Or is there some other way, Decorator perhaps? I was wondering which of these approaches, if any, is generally preferred.
I would try something like this:
Event.addNativePreviewHandler(new NativePreviewHandler() {
#Override
public void onPreviewNativeEvent(NativePreviewEvent event) {
if (event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER) {
if (event.getNativeEvent().getEventTarget() != null) {
Element as = Element.as(event.getNativeEvent().getEventTarget());
if (as.getTagName().toLowerCase().equals("input") ||
as.getTagName().toLowerCase().equals("textarea")) {
// TODO submit data;
}
}
}
}
});
Every time someone hits the Enter Key and the cursor is placed on a input- or textarea-tag, you will get the control and can submit your data.
I don't think there is a way to do what you're asking directly in the GXT library. I do want to stress that extending the TextField class just to add an event handler to it is not the correct way to handle this. Event handlers are based on the composition of a class. It would be like extending a class with a List field just to add another element into the list.
A singleton factory class that created and initialises the Textfield for you would be the cleanest solution, in my opinion. It would allow you to effectively change defaults and add other handlers as required at a later time in a single place if requirements change.
You can try it with GWT JSNI also.
Steps to follow:
define a function in JavaScript that is called on Enter key press
call GWT JSNI from above JavaScript function that is exported at the time of onModuleLoad using GWT JSNI
get the Element from where this event is triggered and finally submit the form based on its tag name or Id
Sample code:
HTML/JSP:
<script>
window.onkeydown = keydown;
function keydown(event) {
if (event.which == 13) {
formSubmit(event.target);
}
}
</script>
JAVA(Entry Point):
import com.google.gwt.dom.client.Element;
public void onModuleLoad() {
exportFormSubmit();
...
}
public static void formSubmit(Element element) {
Window.alert("element tag name:" + element.getTagName() + "form ID:"
+ element.getParentElement().getId());
}
public static native void exportFormSubmit() /*-{
$wnd.formSubmit = $entry(#com.x.y.z.client.GWTTestProject::formSubmit(Lcom/google/gwt/dom/client/Element;));
}-*/;

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