How to define getter/setter if webcomponent property is Object

Chapter “Creating a Java API for a Web Component” in Vaadin document described:

   private static final PropertyDescriptor<Boolean, Boolean> pinProperty = PropertyDescriptors.propertyWithDefault("pin", false);

    public void setPin(boolean pin) {
        pinProperty.set(this, pin);
    }

    public boolean isPin() {
        return pinProperty.get(this);
    }

PropertyDescriptors.propertyWithDefault can support data types like Boolean, String, Integer or Double. If data type of the property changes to Object, what should be modified?

Hi,

What do you mean by Object?

You can use the Element API for “Object” (JsonValue), here you can find some documentation: https://vaadin.com/docs/v14/flow/element-api/tutorial-properties-attributes.html

And an example to create JsonObject here: https://github.com/jcgueriaud1/sortable-layout/blob/master/sortable-layout/src/main/java/org/vaadin/jchristophe/SortableConfig.java

I noticed JsonObject config.toJson() is used as a parameter invoked by javascript function initLazy().

public class SortableLayout extends Div {
...
    private void initConnector(Element layout, SortableConfig config) {
        runBeforeClientResponse(ui -> ui.getPage().executeJs(
                "window.Vaadin.Flow.sortableConnector.initLazy($0, $1, $2)", config.toJson(),
                getElement(), layout));
    }
...
}

If object captureMode in sortablejs.js works as attribute/property of SortableLayout, how to code setCaptureMode() and getCaptureMode() in SortableLayout.java?

I didn’t try it but setCaptureMode should be fine, getCaptureMode probably requires more test:

    private SortableConfig getCaptureMode() {
        SortableConfig sortableConfig = new SortableConfig();
        Object config = getElement().getPropertyRaw("captureMode");
        if (config instanceof JsonObject) {
            return (SortableConfig) sortableConfig.readJson((JsonObject) config);
        }

        return sortableConfig;
    }

    private void setCaptureMode(SortableConfig sortableConfig) {
        getElement().setPropertyJson("captureMode", sortableConfig.toJson());
    }

In my case, the configuration (captureMode) was done only to set the object from the server to the client. (in one way)

If you need to synchronize the client value (captureMode) on the server side, it’s probably better to listen an javascript event on the server side (like on update capture mode).

Test with the codes above, getPropertyRaw(“captureMode”) returns null.

Object config = getElement().getPropertyRaw("captureMode");

It looks like getOption() should be defined to handle the case.

In sortableConnector.js
        c.$connector = {
            //// functions
            setOption : function(optionName, optionValue) {
                this.sortable.options[optionName]
 = optionValue;
            },
            getOption : function(optionName) {
                return this.sortable.options[optionName]
;
            }
        };
		
In SortableLayout.java
    public int getAnimation() {
        return getOption("animation");
    }
	
    public Serializable getOption(String option) {
    	final Serializable value[] = new Serializable[]
 {1};
        this.runBeforeClientResponse(ui -> getElement()
            .callJsFunction("$connector.getOption", option, null).then(Integer.class, new SerializableConsumer<Integer>() {
				@Override
				public void accept(Integer t) {
					value[0]
 = t;					
				}

            }));
        return value[0]
;
    }	
	

and test following two lines:

        setAnimation(12);
		int animation = getAnimation();

Line 1 sets property animation to 12 sucessfully, but Line 2 returns wrongly because $connector.getOption() is executed after **return value[0]
**.

I have no idea how to handle the asynchronous issues about getter/setter, Maybe you could give me a suggestion.

The setter does not need to be asynchronous in Java.

For the getter you can do this:

        sortableLayout.getAnimation().thenAccept(value -> {
            System.out.println("animation"+value);
        });
    public CompletableFuture<JsonValue> getAnimation() {
        return getOption("animation");
    }
    public CompletableFuture<JsonValue> getOption(String option) {
        return getElement()
                .callJsFunction("$connector.getOption", option).toCompletableFuture();
    }

If you want to have a synchronous getter, it’s probably better to synchronize the state (captureMode) from the javascript side to the Java side with an event (“on Capture Mode changed”).

Jean-Christophe Gueriaud:
The setter does not need to be asynchronous in Java.

For the getter you can do this:

        sortableLayout.getAnimation().thenAccept(value -> {
            System.out.println("animation"+value);
        });
    public CompletableFuture<JsonValue> getAnimation() {
        return getOption("animation");
    }
    public CompletableFuture<JsonValue> getOption(String option) {
        return getElement()
                .callJsFunction("$connector.getOption", option).toCompletableFuture();
    }

If you want to have a synchronous getter, it’s probably better to synchronize the state (captureMode) from the javascript side to the Java side with an event (“on Capture Mode changed”).

Hi Jean Christophe, I had the similar problem, how did you manage to work around the session lock when you use the CompletableFuture.
I got this error

java.lang.IllegalStateException: Cannot access state in VaadinSession or UI without locking the session.

I tried to put it inside a thread, I got the result but it gave the same exception

new Thread() {
			@Override
			public void run() {
				try {
					boolean ss = executeJs(
							ui,
							connectorId + "getRotation($0)",
							getElement()
						).toCompletableFuture().get().asBoolean();
					System.out.println(ss);
				} catch (InterruptedException | ExecutionException e) {
					e.printStackTrace();
				}
			}
		}.start();

Any help would be appreciated.

Hi,

As completablefuture is asynchronous you don’t use a thread but need to lock the vaadin session if you are updating the view.

For example:

        sortableLayout.getAnimation().thenAccept(value -> {
            System.out.println("animation"+value); // you don't need to lock the vaadin session because it's not a ui command
			sortableLayout.getUI().ifPresent(ui -> ui.access(() -> {
				// you can update the UI
				myTextfield.setValue(value);
			}    ))
        });

I hope this helps (I didn’t run this code). Try to not do long operation in the ui.access, like save something in the database, but only things relate to the ui.