Loading...
Important Notice - Forums is archived

To simplify things and help our users to be more productive, we have archived the current forum and focus our efforts on helping developers on Stack Overflow. You can post new questions on Stack Overflow or join our Discord channel.

Product icon
TUTORIAL

Vaadin lets you build secure, UX-first PWAs entirely in Java.
Free ebook & tutorial.

Hybrid Flow/Fusion App - How to access elements?

Daniel Stock
11 months ago Apr 14, 2021 3:15pm

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

Jean-Christophe Gueriaud
11 months ago Apr 14, 2021 4:09pm

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

Daniel Stock
11 months ago Apr 14, 2021 4:47pm
Daniel Stock
11 months ago Apr 14, 2021 8:59pm
Jean-Christophe Gueriaud
11 months ago Apr 15, 2021 5:54am

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.

Jean-Christophe Gueriaud
11 months ago Apr 15, 2021 5:55am

I forgot the screenshot of my consolde :).

Daniel Stock
10 months ago Apr 15, 2021 8:53am