javascript extension in vaadin 10 ?

Hi,

I’m starting to discovers vaadin 10.
Is there a way to communicate between js and server as we could in vaddin 7 and 8 with the javascript extension.

https://vaadin.com/docs/v8/framework/gwt/gwt-javascript.html

I didn’t find the javascript extension in the new api.
We still have the javascript annotation to import a js file, but how to make it communicate with the servers. Call java methods in js script, and call js methods in java ?

I noticed we can now access now to the DOM in pure java. But didn’t find the great features realeaded in vaadin 7 to wrote some custom js extensions.
I hope we can still do that.
Because rewrite our js code into pure java will take a long time. (And not sure about the performance)

Hi there,

Let’s split the solution in multiple parts. First: how to call a function defined on the client-side from the server-side:

If the DOM element which your component is attached to has a specific function, you can call it from the server side by using:

myComponent.getElement().callFunction("functionName", param1, param2);

If you have a function that is defined elsewhere, you can use this:

UI.getCurrent().getPage().executeJavascript("functionName($0, $1)", param1, param2);

For example:

UI.getCurrent().getPage().executeJavascript("window.alert($0)", "Hello world!");

Now, suppose you are on the client-side and want to call a function defined on the server-side:

First, on the server-side, you need to create a method annotated with @ClientDelegate. For example:

@ClientDelegate
public void println(String text) {
    System.out.println(text);
}

Then, on the client-side, you can call your function by using:

element.$server.println('Hello world!');

You can see more details about the javascript function execution in this tutorial: https://vaadin.com/docs/v10/flow/polymer-templates/tutorial-template-basic.html

Worth mentioning that with V10 you don’t have to deal with GWT connectors anymore. All the client part can be done with pure javascript (and you can import any webcomponent or js library you want, without having to create client-side wrappers).

Thank your for your answer. I will try and experiment this.

First trap i came :slight_smile:

I followed the tutorial but replaced name “hello-world” by “test”
And got errors.

I think it will be nice that the tutorial says that when writing a polymer extension we should have a dash in the name.

https://stackoverflow.com/questions/22545621/do-custom-elements-require-a-dash-in-their-name

other point :

it will be nice to have a single url import we can use everywhere instead of playing with a relative url to import polymer-element.html

i try this
<link rel="import" href="/frontend/bower_components/polymer/polymer-element.html">

this won’t work because missing the app name.

Alain, you are 100% right - I also faced the same problem with the dash in the name when I created my first webcomponent. It is part of the spec, so we can’t do much about it. But sure, I’ve created a pull request already to properly communicate that in our tutorial. Thanks for the suggestion!

The way the HTML imports are handled is a bit strange at first, but it is the way it is to properly support bundling, resource minification and transpilation for production builds. The path changes depending on the capabilities of the browser - ES5 browsers (IE11) have the resources in a different path than ES6 browsers, for instance. That’s why absolute paths are not supported.

Thank you for your anwser!

I have another question.
In my web component i want to intialize some js after the component is loaded at the first time.
As i see on polymer documentation there is a callfunction named “ready”.

Here is my .html file :

<link rel="import" href="../../../bower_components/polymer/polymer-element.html">

<dom-module id="atalante-test">
    <template>
        <div>test</div>
        <div>
            <div id="greeting">[[greeting]
]</div>
        </div>
        <slot></slot>
    </template>
    <script>
        class Test extends Polymer.Element {
            static get is() {
                return 'atalante-test'
            }
            
            afterServerUpdate(){
                console.log("The new 'greeting' value is: "+this.greeting);
            }
            
            
            ready(){
            	console.log('ready');
            }
            
            created() {
            	console.log('création');
            }
            
            test(){
            	console.log('test!');
            }
        }
        customElements.define(Test.is, Test);
    </script>
</dom-module>

In the console i see the ready it seems to work.
but when i have this function ready, the div declared in the template section disapear.

i don’t have the <div>test</div> in the dom.

But the element i added dynamicly in java server side the will still appear in the <slot> tag.

@Tag("atalante-test")
@HtmlImport("frontend://src/views/test/test.html")
public class Test extends PolymerTemplate<TemplateTest>{
	 private static final String EMPTY_NAME_GREETING = "Please enter your name";
	
	public Test() {
		setId("template");
		getModel().setGreeting(EMPTY_NAME_GREETING);
		
	
		Element div = new Element("div");
		div.setText("some text");
		
		Button b = new Button("test greeting");
		
		b.addClickListener(event -> {
			getModel().setGreeting("" + System.currentTimeMillis());
			
		});
		
		
		Button testJs = new Button("Test js");
		testJs.addClickListener((e) ->{
			getElement().callFunction("test", "a", 1);
		});
		
		this.getElement().appendChild(div).appendChild(b.getElement()).appendChild(testJs.getElement());

You can’t be just ready. You have to be super ready!

ready(){
    super.ready();
    console.log('ready');
}

That should do it :slight_smile:

yes it’s good now :slight_smile:

hum still have an issue.
i wanted to register an event on the div i have in the template

	super.ready();
	console.log('ready');

	console.log($('body').html());

	$("#greeting").click(function(){
		console.log("click");
	});

Problem in the ready method in the dom at this time i don’t have the id property of the div
here is the html of my compoment when ready is triggered

<atalante-test><div></div><vaadin-button></vaadin-button><vaadin-button></vaadin-button></atalante-test></div>

a bit strange ?

maybe there is another callback function i should use ?

The Polymer syntax to query by ID items inside the shadow-root is the following:

let div = this.$.greeting;

So you don’t need to use jQuery for that.

But the recommended way of listening to events is by using on-eventType in your template (so you don’t even need to query for the element). For example:

<div id="greeting" on-click="onGreetingClicked">[[greeting]
]</div>

… and then you define the function in your webcomponent to handle the event:

onGreetingClicked(event){
    console.log("click");
}

ok i assume to use that for newer js component i will write.

but i have already lot of js component whose not based on polymer model and written with jquery in client side and communicating with vaadin server with the vaadin javascript extension.

So i need to create a “vaadin polymer” wrapper to make them still work again in Vaadin 10.
Should work but i need to have the start point.
“Ready” callback seems not be my option, because at this moment the DOM is not entirely loaded.

You don’t have to encapsulate your jQuery components in a Polymer template to make them work with V10.

You can use them as they are.

We have a project with proof-of-concept technical demos where there’s a jQuery table: https://github.com/vaadin/flow-demo/tree/master/demo-jquery-table Take a look, it might help you integrating your own components.

thank you for the example, but i still have to make changes because my javascript component was written on previous vaadin javascript extension model.

window.com_vaadin_book_examples_client_js_MyComponent =
function() {
    // Create the component
    var mycomponent =
        new mylibrary.MyComponent(this.getElement());

    // Handle changes from the server-side
    this.onStateChange = function() {
        mycomponent.setValue(this.getState().value);
    };

    // Pass user interaction to the server-side
    var self = this;
    mycomponent.click = function() {
        self.onClick(mycomponent.getValue());
    };
};

so i need to rewrite this client/server mechanism of communication.

But i found solution in my precedent post to have the first start point after initialization of the DOM.

By declaring an init js method in client side and fired it on server side on my PolymerTemplate component.

addAttachListener(event -> getElement().callFunction("init"));

init(){
	console.log('init');
	console.log($('body').html());
}