lunes, 12 de octubre de 2020

35. Migrating to Vaadin version 17 & 18. Problems in annotaitons: @Push and @Theme

see: Vaadin Forum

1.VaadinServlet/CdiVaadinServlet implementions

In version 14

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package openadmin.listeners;

import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;

import com.vaadin.cdi.CdiVaadinServlet;
import com.vaadin.flow.server.Constants;
//import com.vaadin.flow.server.VaadinServlet;


/**
 * Information about app servlet 
 * 
 * Necessary for defining i18n Provider
 * @author ximo 
 *
 */
@SuppressWarnings("serial")
@WebServlet(
	urlPatterns = "/*", 
	name = "slot", 
	asyncSupported = true, //For @Push
	
	
	initParams = {
		// I18N Provider for translation of labels	
        //Vaadin 14,15,16
	@WebInitParam(name = Constants.I18N_PROVIDER, value = "openadmin.i18n.MyI18nProvider"),	
	// Enable pmpm instead of npm	2020-05-27 for Vaadin 15 
	// @see https://dantesquevaadin.blogspot.com/2020/05/32-vaadin-15-new-version-using-pnpm.html
	@WebInitParam(name = "pnpm.enable", value = "true")
	})

// Deprecated. @see https://vaadin.com/docs/v14/flow/production/tutorial-production-mode-basic.html
//@VaadinServletConfiguration(productionMode = false)

//public class ApplicationServlet extends VaadinServlet {
public class ApplicationServlet extends CdiVaadinServlet {
}


now in version 17, there are several changes


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package openadmin.listeners;

import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;

import com.vaadin.cdi.CdiVaadinServlet;
//import com.vaadin.flow.server.VaadinServlet;
import com.vaadin.flow.server.InitParameters;

/**
 * Information about app servlet 
 * 
 * Necessary for defining i18n Provider
 * @author ximo 
 *
 */
@SuppressWarnings("serial")
@WebServlet(
	urlPatterns = "/*", 
	name = "slot", 
	asyncSupported = true, //For @Push
	
	//pushmode = "manual",
	
	initParams = {
		//Vaadin 17
		// I18N Provider for translation of labels	
     		@WebInitParam(name = InitParameters.I18N_PROVIDER, value = "openadmin.i18n.MyI18nProvider"),
        	@WebInitParam(name = InitParameters.SERVLET_PARAMETER_PUSH_MODE, value= "PushMode.MANUAL" ),
        	@WebInitParam(name = InitParameters.SERVLET_PARAMETER_ENABLE_PNPM, value= "true" ),
        	//End Vaadin17
	})

//public class ApplicationServlet extends VaadinServlet {
public class ApplicationServlet extends CdiVaadinServlet {
}


2.VaadinServiceInitListener implementions and @Push

In version 14, the @Push annotation was placed  in the main form, now in version 17 gives a runtime error: 

Found app shell configuration annotations in non `AppShellConfigurator` classes.

Please create a custom class implementing `AppShellConfigurator` and move the following annotations to it:

    - @Push from openadmin.ui.forms.MainView

So, the class implementing VaadinServiceInitListener should now implement also the AppShellConfigurator interface and will contain the @Push annotation

Now let's see the new class with the old version aspects commented


In version 18 also complains about the @Theme annotation and does not want it in the main form



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package openadmin.listeners;


import com.vaadin.flow.component.page.AppShellConfigurator;
import com.vaadin.flow.component.page.Push;
import com.vaadin.flow.server.ServiceInitEvent;
import com.vaadin.flow.server.VaadinServiceInitListener;
import com.vaadin.flow.shared.communication.PushMode;

import openadmin.ui.forms.LoginForm;
import openadmin.vaadin.http.SessionParams;
import openadmin.vaadin.http.VaadinHttpUtils;



/**
 * This class is used to listen to BeforeEnter event of all UIs in order to
 * check whether a user is signed in or not before allowing entering any page.
 * It is registered in a file named
 * com.vaadin.flow.server.VaadinServiceInitListener in META-INF/services.
 * 
 * NOTE that a SessionScopped object cannot be injected so, to know if the user is logged in
 *   we need to make use of information stored in the Session object with keys "CURRENT_USER" 
 */




@SuppressWarnings("serial")
//Vaadin14,15,16
//public class LoggedInListener implements VaadinServiceInitListener {

//Only Vaadin 17
@Push(PushMode.MANUAL)

//Only for Vaadin 18 +
@Theme(value = Lumo.class)

public class LoggedInListener implements VaadinServiceInitListener, AppShellConfigurator {
	. . . . . . . . 

remember to remove the@Push and @Theme annotations from the main form!!!!

jueves, 1 de octubre de 2020

34. Understanding ui.access(). @Push annotation

 0. Introduction

I want to show only a few remarks about "Asynchronous Updates" from Vaadin tutorial.

When you run a thread,  the Session seems to be not accessible, so the UI. You should take it into account as when manipulating form components from the thread!!!


1. Hanging the execution

Be very careful as the execution may hang in these cases:

  • When using the annotation @Push without options and the process is executed for the first time. Maybe I am doing something wrong but I don't know.
  • When using the annotation @Push( (PushMode.MANUAL) and not using ui.push() as the final sentence inside a ui.access()(() -> {...}) block; to assure the UI is updated
  • When using ui.push(), my advice is to put it into a try-catch block!
  • In Vaadin-14 use the @Push annotation in the main form, but in Vaadin-17 use it in the class that implements AppshellConfigurator. As it is explained in the next post.

ui.access(() -> {
  Span sp=new Span("Begin command...");
  view.add(sp);
  try {ui.push()} catch (Exception e){e.printstacktrace()}; //Last sentence 
});


2. Creating components dynamically from a thread

Components should be created within the ui.access() {} block, as you cannot access the Session or the UI outside this block. 

For instance, you cannot create an Anchor component outside this block as the creation implies access to the UI for getting URIs!!

In case you can create the component, you cannot attach it to the UI outside this block!

miércoles, 9 de septiembre de 2020

33. Detecting and solving problems Vaadin 16.0.3 and 17.0.1

 1. Incompatible pnpm versions

Vaadin wants pnpm versions in a determinate range. So DO NOT install pnpm. Vaadin will install it for you.


2.  pnpm fails when downloading resources

2.1 See previous error (1)

2.2 Delete these files from the main folder of the project:

- package.json

pnpmfile.js

- pnpm-lock.yaml

- {}tsconfig.json

- types.d.ts

- webpack.config.js

- webpack.generated.js

- mode_modules (folder)


2.3 reset the project as follows: 

2.3.1 Select the parent project and Run As - Maven Clean

2.3.2 Select the parent project and Maven - Update Project

2.3.3 Select the parent project and Run As - Maven Build .. - and goals: "clean package" and profiles: "production"

2.3.4. Select the vaadin (child) project and Run As - Maven Clean

2.3.5 Select the  vaadin (child) project and Maven - Update Project (select all the project that is a dependant)


3.  Errors in some json files in the node_modules folder

These errors are causes by comments and duplicate assignments of parameters.

Modify these files and remove commends and duplicated assignments


4.  Java 14.!!!!

There can be some problems in Eclipse using Java 14+. see My Spanish blog Informatica Dantesca.

5.  @Push annotation and Vaadin 17.0.x

The @Push annotation does not work still in Vaadin 17.0.1 unless you use it in the appropriate class. It is a problem with new versions. It should be used in another class. See the previous post

sábado, 16 de mayo de 2020

32. Vaadin 15. New version. Using pnpm instead of npm

1. Introduction
Vaadin proposes to user pnpm instead of npm as it seems quicker and saves disk space.

To achieve this goal one can use several ways, see Vaadin 15 for complete details:
  1. In Spring: in the application.properties file, set this property vaadin.pnpm.enable=true
  2. In a plain JEE application with the @WebServlet annotation (is the one I use)
  3. In a plain JEE application by means of the web.xml file
  4. In the pom.xml file, in the part of the vaadin-maven-plugin

My option is using the @WebServlet annotation, as in my project, I used this class to define the management of internationalization and CDI. Here is the source code


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package openadmin.listeners;

import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;

import com.vaadin.cdi.CdiVaadinServlet;
import com.vaadin.flow.server.Constants;

/**
 * Information about app servlet 
 * 
 * Necessary for defining i18n Provider
 * @author ximo 
 *
 */
@SuppressWarnings("serial")
@WebServlet(
	urlPatterns = "/*", 
	name = "slot", 
	asyncSupported = true, 
	initParams = {
		
	// I18N Provider for translation of labels	
        @WebInitParam(name = Constants.I18N_PROVIDER, value = "openadmin.i18n.MyI18nProvider"),
        
        // Enable pmpm instead of npm	
        @WebInitParam(name = "pnpm.enable", value = "true")
 	})


public class ApplicationServlet extends CdiVaadinServlet {
}
  

martes, 21 de abril de 2020

31. Vaadin 14. Integrating external Js libraries from npm (3). Apexcharts

0. Introduction

Please read this previous post to get more details. I have tested this code with Vaadin 15 and it works!

Let's use the magnific library "apexcharts" .

Steps:
  1. Create a folder in the frontend called for instance "apex-charts".
  2. Create a file for instance "apex-charts-test.js" into the previous folder.
  3. Create a class that can contain other Html elements (like a Div) that will contain the chart.  


1. apex-charts-tests.js

Line 1: Import the required dependencies.
Line 5: A function for rendering a chart. The parameters are:
  • jsElem that is a representation of the right-hand assignment of a javascript variable that will contain the graph definition. In the apexchart web is an example that is used here. The value is defined in line 3 of the Main component that will display the component inside. and is a right-hand assignment of a variable in javascript (last source code of the post)
  • divId: the id of an Html element that will contain the char object.
Line 7: Construct a javascript sentence for evaluating the chart definition. In the next sentence is evaluated and the variable myCharDef is assigned the value of the chart.

Line 9: Retrieve the Html element whose id is the one defined in the variable divId.
Line10. Creates the chart object and in the next line is rendered into the Html element.

Line 15. The function is assigned to the window object so that it gets global presence and can be called from a Vaadin component.

Here is the code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
import ApexCharts from "apexcharts";

class ApexChartsEdu  {
 
 render(jsElem, divId) {
  var myCharDef={};
  var kk="myCharDef="+jsElem;
  eval(kk);
  var aDiv=document.querySelector("#"+divId);
  this.chart=new ApexCharts(aDiv, myCharDef);
  this.chart.render();
 }
}

window.renderApexChart = function (jsValue, anId) {
 const mychart= new ApexChartsEdu();
 mychart.render(jsValue, anId);
}

2. The ApexChart class

Line 8: Import the "apex-charts-test.js"  referenced from the frontend folder
Line 9: Import the library from the npn repository.
Line 15: Constructor that requires the id of the div component so that it will be accesible from the javascript function
Line 20: Renders the chart with the  jsString definition.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package openadmin.ui.forms.elements.customform.apexcharts;

import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.dependency.NpmPackage;
import com.vaadin.flow.component.html.Div;

@SuppressWarnings("serial")
@JsModule("./apex-charts/apex-charts-test.js")
@NpmPackage(value = "apexcharts", version = "3.18.1")

public class ApexCharts extends Div{

 private String myId="";
 
 public ApexCharts(String myId) {
  this.myId=myId;
  this.setId(myId);
    }
 
 public void render(String jsString) {
  System.out.println("myId="+ myId);
  getElement().executeJs("window.renderApexChart($0, $1)", jsString, myId);
 }
}


3. Creating a chart and inserting it into a component (Main)

Line 1: Create the Div component that will contain the chart
Line 2: add the component to the main component
Line 3: Define the right-hand assignment of the definition of the char
Line 16: rendering the component.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
ApexCharts apex= new ApexCharts("mysuperdiv");
add(apex);
String jsStuff="{" + 
  "  chart: {" + 
  "    type: 'line'" + 
  "  }," + 
  "  series: [{" + 
  "    name: 'sales'," + 
  "    data: [30,40,35,50,49,60,70,91,125]" + 
  "  }]," + 
  "  xaxis: {" + 
  "    categories: [1991,1992,1993,1994,1995,1996,1997, 1998,1999]" + 
  "  }" + 
  "}";
  
apex.render(jsStuff);




jueves, 9 de abril de 2020

30. Vaadin 14. Integrating external Js libraries from npm (2). TinyMce. NOT WORKING!!!!!

0. Introduction

Now that I have tried to use TinyMce in my Vaadin projects, but integrating with Vaadin 14 is rather difficult because the component tries to find themes and skins and cannot find them. This maybe can be due to the shadow dom, which can encapsulate most of the code so that variables and paths get local and cannot be accessed.

The main ideas of this post are from the Vaadin forum.

The steps to follow are:

1. Create a js file to get the function tinymce.init() global so that it can be accessed by the Vaadin component. this file will be put in the frontend folder. Remember that to make a function global, it should be incorporated into the "window" object.
2. Create the component class with the @NpmPackage annotation to import the tinymce module from npm
3. Execute the build process so that the npm_modules folder will be filled with the npm dependencies. If you, instead of executing the build process, execute Run As-> Run on server, then you will get an error that cannot find the "./tinymceEdu/themes/silver.index.js". To solve this error, you should comment the line 18 that tries to import this module by means of the @JsModule annotation.
4. Copy the folder npm_modules/tinymce/skins to src/main/webapp folder and we will get the folder src/main/webapp/skins.
5. Create a folder "tinymceEdu" (or any other name different from "tinymce") in the folder frontend.
6. Copy the folder npm_modules/tinymce/themes to the last folder, getting frontend/tinymceEdu/themes folder
7. Create the main form and include this component

1. The js file

I have called it tinymce-test.js . It will be placed in the "frontend" folder


1
2
3
4
5
import * as TINY from "tinymce";

window.initTinymce = function() {
 tinymce.init({selector:'textarea'});
}


2. The component class



 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.gmail.ximo;
import java.util.UUID;
import com.vaadin.flow.component.AttachEvent;
import com.vaadin.flow.component.Tag;
import com.vaadin.flow.component.customfield.CustomField;
import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.dependency.NpmPackage;

@SuppressWarnings("serial")

//Our js file used to make the library functions global
@JsModule("./tinymce-test.js") // New! [1]

//Theme
@JsModule("./tinymceEdu/themes/silver/index.js") // Modified! [2]

//Npm
@NpmPackage(value = "tinymce", version = "5.2.1") // Modified! [3]
@Tag("textarea")
public class TinyMce extends CustomField<String> {
    public TinyMce(){
        this.setId(UUID.randomUUID().toString());
    }

    @Override
    protected String generateModelValue() {
        return getElement().getText();
    }

    @Override
    protected void setPresentationValue(String s) {
        getElement().setText(s);
    }

    @Override
    protected void onAttach(AttachEvent attachEvent) {
        super.onAttach(attachEvent);
        this.getElement().executeJs("window.initTinymce()"); //Modified [4]
        this.setWidth("500px"); // Modified [5]
    }
}

Line 12: references the file" tinymce-text.js" so that we can use the "window.initTinymce()" function declared in this js file. The execution of this function is in line  38.

Line 15: references the copied folder themes from npm_modules/tinymce/themes to frontend/tinymceEdu/themes

Line 18: the reference to the tinymce package from the npm repository

Line 19: the tag used in html

Line 38: The use of the function tinymce.init() which has been "globalized" in the "tinymce-test.js" file passing it to the window object


3. The main view class

Here is the simple code


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.gmail.ximo;

import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.PWA;
import javax.annotation.PostConstruct;


/**
 * The main view contains a simple label element and a template element.
 */
@SuppressWarnings("serial")
@Route("")
@PWA(name = "Project Base for Vaadin Flow with CDI", shortName = "Project Base")
public class MainView1 extends VerticalLayout {

    @PostConstruct
    public void init() {
        TinyMce tiny= new TinyMce(); 
        add(tiny);
    }
}

Now you can run it (if you use the start-up project from Vaadin as Run As- Maven Build.. - Goals : "clean package tomee:run" or Run -Run on Server ... if you are using Tomcat server and have configured the pom.xml as in this post).



Happy coding!

29. Vaadin 14. Integrating external Js libraries from npm (1). Playing a note with Tone.js

0. Introduction

This post is based on a contribution to Vaadin form by Arthur Signell. This is a very important and useful contribution.

The steps for achieving these aims are:
  1. Create a js file (in the frontend folder) that defines the calls to the js library. 
  2. Create a java file that imports the js library from the npm and execute the functions of the previously created js file

1. JS important tips

1. Nearly all the variables and functions created are local! That means that they cannot be used in a Vaadin Java class. For instance variable "a" and function "myfunct" are local:
  
     var a = 5; 
     myfunc ( c, d) { return c + d; }
  
2. If you want a variable or function that can be recognized by a Vaadin component, (that means to make them global), you must attach it to window object. For instance  variable "b" and function "otherfunct" are global:

    window.b = 5;
    window.otherfunct= function ( c, d) { return c + d; }

3. If you want to make an "import" of the external javascript library for instance:

    import * as TONE from "tone";
 
  The reference to the external javascript library must be made by means of @NpmPackage  annotation in the Vaadin component class that uses it:

   @NpmPackage (value = "tone", version = "13.8.25")

Here we are importing the library "tone.js" version "13.8.25" from the npm repository

4. To reference the created js file from the Vaadin Component class, the @JsModule annotation can be used:
 
   @JsModule("./tone-test.js")

2. Create the js file

This simple file makes visible (global) the variables and functions we need to use and call from our Vaadin component.

This file must be in the "frontend" folder!. 

Here is a file ( tone-test.js )  that plays a note with a specific duration using tone.jshttps://tonejs.github.io/ library 


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import * as TONE from "tone";

class ToneTest {
 //Create the synthesizer
 init() {
  this.synth = new TONE.Synth().toMaster();
 }
 
 // Play notes
 play(note, duration) {
  this.synth.triggerAttackRelease(note, duration)
 }  
}  

//Make the functions global

//Create the synthesizer
window.initTone = function() {
 window.tt = new ToneTest(); //makes the variable global
 window.tt.init();
}

// Play the notes
window.play = function (note,duration) {
 window.tt.play(note,duration);
}

Now it is possible to call the functions "window.initTone" and "window.play" as they are attached to the window that is a global object (Lines 18 and 24)

Don't forget to make the imports (Line 1)

There is an intermediate class (ToneTest)  for managing direct calls to tone.js functions (Lines 3-13)


3. The Vaadin Component Class

It is important to note:
  1.  Line 21: the @NpmPackage annotation for importing the library tone.js version 13.8.25 from npm
  2. Line 20: The @JsModule that import the previous "tone-test.js" javascript file that was saved in the frontend folder. If we had saved it in an inner folder of the frontend folder, for instance, frontend/tone the annotation would have been @JsModule("./tone/tone-test.js")
  3. Line 28: In the init() method annotated as PostConstruct, we make sure to initialize the synthesizer. To execute a js code the getElement().executeJs(function,parameters...) is used.
  4. Line 32: The click event of the button is assigned to the js call to play a "C4" note for a duration of an 8th of a whole note.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.gmail.ximo;

import com.vaadin.flow.component.Key;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.dependency.NpmPackage;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.server.PWA;

import javax.annotation.PostConstruct;
import javax.inject.Inject;

/**
 * The main view contains a simple label element and a template element.
 */
@SuppressWarnings("serial")
@Route("")
@PWA(name = "Project Base for Vaadin Flow with CDI", shortName = "Project Base")
@JsModule("./tone-test.js")
@NpmPackage(value = "tone", version = "13.8.25")
public class MainView extends VerticalLayout {


    @PostConstruct
    public void init() {
        //Initialize the synthesizer        
        this.getElement().executeJs("window.initTone()");

        // Play a C4 Note with a duration of a 8th
        Button button = new Button("Say hello",
            e -> getElement().executeJs("window.play($0,$1)", "C4", "8n"));
        // You can specify keyboard shortcuts for buttons.
        // Example: Pressing enter in this view clicks the Button.
        button.addClickShortcut(Key.ENTER);

        add(button);
    }

}

I hope you have a good time!

jueves, 6 de febrero de 2020

28. Vaadin 14. How to display programmatically components to the OpenXava (C) way

1. Introduction

OpenXava is a wonderful framework based on the Model. I love it. I am a fan of its creator Mr. Javier Paniza. He has designed a nomenclature for defining the "view" part of an MVC pattern. As he states, programmers can devote to business logic instead of designing complicated HTML, CSS or javascript code on the client-side.
Now responsiveness is a must.

Vaadin has a Horizontal and a Vertical  Layout component, that seems responsive, but when you nest them it is very difficult for me to keep track of the CSS code added by Vaadin Themes.. so you cannot predict how the information is displayed

After struggling about for some days and googling some days too, I found the CSS flex property

Another problem is that you cannot know the real width and height of the components in run time, you should use some unpredictable javascript code that is not recommended.

2. Designing the layout


A simple definition of a view in OpenXava could be:
--------------------------------------------------------------------------------------------------------------------
student   [ firstName,    lastName;
            address       [streetName, number, city ],
            parents       [mother,     father]
          ];
Sports    [ firstSport;   secondSport]
--------------------------------------------------------------------------------------------------------------------

That should produce something like this:

+------------------------------------------------------------------------------+
| +--------------------------------------------------------------------------+ |
| |                                  Student                                 | |
| +--------------------------------------------------------------------------+ |
| |             +-------------+             +-------------+                  | |
| | First Name: |             Last Name: |             |                  | |
| |             +-------------+             +-------------+                  | | 
| | +---------------------------------------+ +----------------------------+ | | 
| | |            Address                    | |          Parents           | | |
| | +---------------------------------------+ +----------------------------+ | |
| | |        +------+    +---+       +----+ | |     +------+      +------+ | | |
| | | Street:|      | N. |   | City: |    | | | Mom:|      | Dad: |      | | | |
| | |        +------+    +---+       +----+ | |     +------+      +------+ | | |
| | +---------------------------------------+ +----------------------------+ | |
| |                                                                          | |
| +--------------------------------------------------------------------------+ |
| |                              Sports                                      | |
| +--------------------------------------------------------------------------+ |
| | |                +----------------------------------------+              | |
| | |  First Sport:  |                                        |              | |
| | |                +----------------------------------------+              | |
| | |                +----------------------------------------+              | |
| | |  Second Sport: |                                        |              | |
| | |                +----------------------------------------+              | |
| +--------------------------------------------------------------------------+ |
+------------------------------------------------------------------------------+



Now to simplify I will be using this pseudo Html structure:

Main form: (Horizontal Panel)
   |
   +--> Vertical Panel
        |
        +--> Horizontal Panel (Student)
        |    |
        |    +--> Vertical Panel
        |         |
        |         +--> Horizontal Panel (line of controls)
        |         |    |
        |         |    +--> Field (First Name)
        |         |    |
        |         |    +--> Field (Last Name)
        |         |     
        |         +--> Horizonal Panel  
        |              |
        |              +--> Vertical Panel
        |                   |
        |                   +--> Horizontal Panel (Address)
        |                   |    |
        |                   |    +--> Vertical Panel 
        |                   |        |   
        |                   |        +--> Horizontal Panel (line of controls)
        |                   |             |
        |                   |             +--> Field (Street)
        |                   |             |
        |                   |             +--> Field (Num)
        |                   |             | 
        |                   |             +--> Field (City)
        |                   |
        |                   +--> Horizontal Panel (Parents)
        |                        |
        |                        +--> Vertical Panel 
        |                             |   
        |                             +--> Horizontal Panel (line of controls)
        |                                  |
        |                                  +--> Field (Mom)
        |                                  |
        |                                  +--> Field (Dad)
        +--> Horizonal Panel (Sports) 
             |
             +--> Vertical Panel
                  |
                  +--> Horizontal Panel (line of controls)
                  |    |
                  |    +--> Field (First Sport)
                  |     
                  +--> Horizontal Panel (line of controls)

                       |                        


                       +--> Field (Second Sport)



Note that every group of fields (marked in OpenXava between brackets [..]) is formed by Horizontal Panel + Vertical panel 

Note that each line of controls is formed by a Horizontal Panel 

So the algorithm should identify the elements:

  • group of controls (between brackets [])
  • lines of controls (identified by comma and semicolons (,;)
  • controls 

2. Implementing Horizontal and Vertical Panels for containers and lines of controls in Vaadin

The simple Vaadin Div component and the CSS flex and @media  elements can perform the magic.

The @media is used for determining the form the components are arranged depending on the size of the screen (so we can predict if the user is managing a big screen, tablet or phone)

The flex element is used to wrap controls in a line (line of controls), so we do not need to worry about the length of the controls when they are contained in a line.

Here is  CSS code

/* For using with OX components.*/ 

/*----------------------------------------------------------*/
/* VERTICAL PANEL */
/*----------------------------------------------------------*/ 

/* For vetical layout */
 .flex-column {
    display: flex;
    flex-direction: column;
    flex: 1;
 }


/*----------------------------------------------------------*/
/* HORIZONTAL PANEL: BIG SCREENS */
/*----------------------------------------------------------*/ 
    /* For horizontal layout (line of controls) */
 .flex-row {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    width: 100%;
 }
 
 
 
 /*When sharing a horizontal with a number of group of controls*/
 .share-with-1 {
  width: 100%;
 }
 .share-with-2 {
  width: 50%;
 }
 .share-with-3 {
  width: 33%;
 }
 .share-with-4 {
  width: 25%;
 }
 .share-with-5 {
  width: 20%;
 }
 .share-with-6 {
  width: 16%;
 }
 .share-with-7 {
  width: 14%;
 }
 .share-with-8 {
  width: 12%;
 }
 .share-with-9 {
  width: 11%;
 }
 .share-with-10 {
  width: 10%;
 }
 
 
/*----------------------------------------------------------*/
/* HORIZONTAL PANEL: MEDIUM SCREENS */
/*----------------------------------------------------------*/ 
 @media all and (max-width: 1000px) {
    .flex-row  {
       /* When on medium sized screens, we center it by evenly distributing empty space around items */
       justify-content: space-around;
    }
    .share-with-1 {
       width: 100%;
    }
    .share-with-2 , .share-with-3 , .share-with-4 , .share-with-5 ,
    .share-with-6 , .share-with-7 , .share-with-8 , .share-with-8 ,
    .share-with-10 {
       width: 50%;
    }
 }

/*----------------------------------------------------------*/
/* HORIZONTAL PANEL: SMALL SCREENS */
/*----------------------------------------------------------*/ 
 @media all and (max-width: 600px) {
    .flex-row  {
     /* On small screens, we are no longer using row direction but column */
     flex-direction: column;
    }
   .share-with-1 , .share-with-2 , .share-with-3 , .share-with-4 ,
  .share-with-5 , .share-with-6 , .share-with-7 , .share-with-8 ,
  .share-with-9 , .share-with-10 {
   width: 100%;
  }
 }
 
 

The vertical panel properties are not affected by the screen size.

. However, horizontal panels must adapt to the width of the screen. (class flex-column)

For groups of controls in one line, in big displays, it is accepted up to 10 columns, in medium ones, 2 columns are accepted and in small devices only one column (class share-with-number_of_groups)

The lines of controls are managed with the flex component (class flex-row).  The behavior (number of columns) can vary depending on both the width of the controls (wrapping) and the width of the screen.