Hybrid Flow/Fusion App - How to access elements?

Hi,

I have a “legacy” application, which has been upgraded from vaadin 7 to 8.

Now I want to reimplement it using Vaadin 19.

I would like to re-use respectively rebuild some of the custom Java components with Flow, but Fusion seems to have some advantages and I would like to look into it for future developments, especially for client-side components.

I have used the start.vaadin.com tool to generate a hybrid app and mixed some of the views to get familiar with it.

Now I have a few essential questions, since I’ve only has a bit of experience with simple React apps, but kept away from more complex frontend stuff which is why Vaadin was my prefered framework for building a GUI:

I have the main-view.ts, which defines the component.
I have nested a div into the header component with the id “entityId” and a value, which should show the entity name.

Below the header I have inserted a “good old” Java based view.
This view only shows a button, which has a click listener.

Is it possible to access the div with the id “entityId” in the header from the button click event from the server side in the Java-based view?

A second question:
Alternatively I tried to use another fusion based view to add a button in the .ts and added a click listener there.
For that one I used:

 document.getElementById('entityId')!.innerHTML = "THIS VALUE HAS BEEN CHANGED BY BUTTON CLICK!";

To change the “entityId” value.
But this only works when I add the div in the view.ts, the main-view.ts’ “entityId” div is not found respectively above method will then return a null exception.

Is this also even possible? Do I need to indicate a context or pass the object somehow?

If this is possible could you point me to a tutorial which goes into this or something in the documentation?
I don’t even know what to look for.

Thanks & best regards

18570847.png

Hi,

You can’t find the element because it’s in the header which is in the shadow root of the component.
The shadow root is not accessible from the outside (document.getElementById is not working).

You could, probably for testing purposes, get the mainview get its shadow-root and find the correct element inside.
It’s not really what I can call a nice way to do but it’s working (you can check my screenshot to see the details).

If you don’t know what is the shadow root maybe this link can help: https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM

In your case, I would probably use a shared state like mobx: https://vaadin.com/docs/latest/fusion/application/state-management

Ah well, looks like the state management with mobx is what I was looking for.

I’ll look into that.

Thank & BR

Hi,

so one part of my problem is now solved, the mobx state management works great when I exchange data in the front-end part.

But now I want to call a java method from the client in a fusion component:

I tried to add a click listener to a <div id="logo" onclick="${this.$server.toggle()}"> in my main-view.ts and annotating the toggle method with @Clientcallable in the Java class.

But I get ‘$server’ does not exist on type ‘MainView’.

The Vaadin 19 API documentation also just mentions that you can call the @Clientcallable method via this.$server.method(), but is this the way to do it in the .ts template?

I can’t find anything on how to do this in fusion, all tutorials seem to be made for flow.

—> Edit: ok for this it seems like the @Endpoint API is supposed to be used, at least I was able to call the backend method.

But the other way around I don’t know how to call a function in a .ts view from my Java code.

I tried UI.getCurrent().getPage().executeJs("method()"); but that throws an error and tells me my method reference doesn’t exist.

All posts I find about this say just to add window. to declare the function globally, but that’s just the fix for plain js and doesn’t seem to work with the .ts files and fusion.

I’m not sure which approach is the correct one or if there is some new way to do it in the fusion components.

What you are looking for is basically what flow does: The communication between the Java and JS :).

To answer your question in this.$server the most important is what is “this” ?
It’s the element in the browser who has a flow counterpart (that defines the Java function “@Clientcallable”).

If you have a Java component Canvas.java then you can call from the browser. (The tag is not relevant for this example)


@Tag("canvas")
public class Canvas extends HtmlContainer implements ClickNotifier<Canvas> {
    public Canvas() {
    }

    @ClientCallable
    public void test(String test) {
        System.out.println("test " + test);
    }
}

document.getElementById('my-component-id-1').$server.test('Perfect'); is working (if the component is not in a shadow dom)

In Typescript if you have the flow counterpart then you can define a private property:
private $server?: MyComponentServer;

And declare your own interface:

declare interface MyComponentServer {
  test(arg: string);
}

In my opinion, $server should be private but it doesn’t mean anything in javascript. It should be internal for the component.
I would probably use custom Event from the client but I don’t have a good example right now.

I would also try to avoid too many communication between the Java components ↔ TS components. It already seems quite hard to follow.

Can you explain why you want to use Fusion and Flow? Maybe there is another way.

I forgot the screenshot of my consolde :).
18571213.png

Hi,

Thanks for the explanation, I’ll try it out.

Currently I don’t really know if I “want” to use Fusion, Flow or both.

That’s why I have used the starter app with both in hybrid mode with all kinds of views.

Now I’m trying to get a feeling for which one seems best in my context, so I’m probing the various approaches on how to do this.

Best means, that I also have that V7/V8 legacy app mentioned in the beginning with lots of custom components and Java code.
I try to assess if it’s easier to move as much code as possible over or if I should rewrite as much of it as possible.
I suspect that I won’t be able to get completely rid of all legacy code from the start but would try to gradually rebuilt everything in fusion if possible.

I also need to implement a new app respectively new functionality and for that “new code” I probably would prefer to use fusion components from the start. And during building that app I would use what I learn to rebuild the legacy parts if needed.