Vaadin Dev Day Spring 2021 - Live streaming

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
all right all right i think we are live now hello everyone everyone welcome to lighting dev day uh yes we are still virtual online but the important thing is that everybody everybody is uh safe and healthy and happy to join uh the talks today we have three amazing speakers uh first we'll start with simon martinelli and a talk on data providers that's going to be very interesting central to to pretty much every violin application then we'll continue with leif olsen and he's going to show some magic with javascript that's going to be very interesting and then we'll have matti tafbonen showing us what the flow value flow team is working on uh so don't don't miss out on that um some housekeeping before we start um let me see well first of all this is being recorded so you will get uh the links to the to the recordings later they're gonna be i think available anyway on youtube immediately uh second of all remember to ask questions in the chat and uh we'll try our best to to solve them all right what else do i have to say today um we'll try to have uh very short breaks between the talks so you get the chance to relax a little bit and and then um [Music] get the energy to to pay close attention to the next talk and then the next stock right uh so yeah that's that's that's kind of the plan for today i hope i didn't uh forget anything but before actually before we start with uh with the cool stuff i'm gonna talk a little bit about dividing community so uh uc dividing community online in many fronts the first one is uh the discord the uh kind of a real-time chat that we have uh if you are not there you should definitely join this uh if you are starting with vadin or if you have experience so uh there's always people hanging out there uh for example this screenshot shows that there were more than a hundred people online plus volunteers so people working uh advising as well they're they are uh uh there frequently very frequently so this is the best way to to get quick answers so join join join the discord server it's a pretty cool place to be uh of course we have twitter twitter it's a great place for kind of keeping up with what's going on in the industry in general and in particular so we announced things like this event or new releases new features blog posts everything dividing youtube channel uh with more than 17 000 subscribers so there must be some good content there you have a small short rather short videos with tips for webinars and events like this one so check it out as well getting closer to the code we have the value directory with hundreds of add-ons that you can use in your wagon applications but also you can publish those add-ons right there so it's a way to contribute contribute to the the community and of course speaking of code and open source we have github with uh all the projects uh most of the projects at least everything that's open source there you'll find it there you have a list of issues for example where you can see all the conversations that are going on there vote for the issues that you think are more important etc and of course report issues or even requests so start the projects that you are interested in right there so all these are ways to uh kind of interact with dividing community and so we thought um we get a good idea to show what people are doing and to and to recognize the work that many of you are doing and so i'm glad to introduce the uh value community award yay so this is uh for you people working out there in all these networks presenting at conferences and we want to recognize the work that you have been doing uh so how does it work first of all the winners are going to be nominated by other uh dividing community award winners but they are selected by the global community every year of course we need to start with something so so divine team is going to select the first five or so winners so we get a team uh ready right each person who wins this uh uh award is gonna receive a certificate and a physical recognition talking that i'm sure you're gonna like they're gonna be uh listed in a page on vadim.com uh you'll get the moderator role on this chord so you can also help even more the community if you wish uh and you get a prescription uh for free as well so one year uh you'll be able to use this um all the products that vaden has to offer right um so uh everybody is welcome to join you need to be an expert like eric here in this uh photo uh if you are just starting with uh with with vadim uh your contributions are as valuable so everybody is welcome to join doesn't matter what's your experience with the framework if you want to find more about this i just published a blog post and in fact i think i have the link over here i can show you quickly uh on the screen uh but if you go to van.com blog you'll find it there all right um i guess that's what i wanted to announce so uh hope to see your reactions on this and your thoughts and and and stay tuned we because we are going to announce uh the the winners uh of the first five winners of or or so of the vatine community all right but let's get into the actual cool stuff today um so i guess we can start with simon uh if you want to share your screen please go ahead yeah let's check that the screen sharing works properly because there was actually some artifacts on alejandro's presentation oh i hope it was at least a bit readable yeah i think at least i was able to follow like three thirds of the screen but one was progressed for some reason all right but we heard what you said and that's the most important thing that's the most important thing yeah okay i can also share the slides later all right so let's see if this works least at least it works yeah for me okay great all right simon this stage is yours okay thank you hi i'm simon um i'm from switzerland first maybe some words about me um sorry i started my id card here already a long time ago in 1995. as you can see in the timeline i'm in java development for more than 20 years now and i'm an active java community member i'm helping to organize travis group switzerland for example or working to export groups of the travel community process and i'm also a teacher at the university in bern have my own company since 12 years and as you can see on the right hand side i started with what in 2019 so that's not entirely true because i used what in the past as well but not as intensive as i do right now because currently i'm working on a customer project for around two and a half years and i'm using what in every day and that's probably also the reason why i'm invited here and i'm very happy uh to be part of the developer day for flooding today uh if you want to reach out you can find me on twitter or also send me an email at the address here i have some code to share you will get the link to the code as well at the end i hope to show you a lot of code and not too much slides and yeah let's go to the topic as you can see that's my current project i'm working on so this is an erp product that from this company and product is sold to small medium businesses in switzerland for example companies that are selling office supplies are using this application and currently the application this one is the new one the old one was written in oracle forms back in the days the product is more than 30 years old already and but to be relevant these days it's no longer enough to have like a desktop client to need some mobile client as well and so they decided to move away from these oracle forms and now we are working on the migration of oracle forms application to a modern web application made with with wedding and the important thing to show here on this screen is we have a lot of data so this erp system has around 2000 tables the all the business logic is in also stored in the database like functions procedures and packages and for sure also some java business code we are building but at the end uh the ui more or less looks similar does this screen here for example is from the price list module where you can set price list create special prices for some products for the customers and what we see here we see tables so if you're looking at the data centric application you usually have components that are displaying more than just one record and if you look at what in there are several of them i brought some of the three components here we have common boxes list box selects and we have what we've seen before the grid and the three grid and now the question is how does the data come into this multi record components and this is what i like to show you and to show that i created a small example we have a simple entity model so we have a customer the customer has a list of orders an order has an order date and a list of order items and the order item then has the quantity and the product and at the end the product has a name and the price so that's a very simple model you can imagine a real system um is the model much bigger but this is enough to just show uh this example so what i did i created an application with vadin and i started with an entity model so we have the customer here as you can see with the list of orders we have the order with a lot have it the list of items and the order item with the product and the product with the price and the requirement from my customers i need to show the customer's revenue in a table so they want to see a table with all the customers with their revenue in total so as i'm a java developer i could go ahead and do that in java so what i did i created the method here that method is called get renew and it gets the orders uses a stream then maps the total amount of the orbit to a double and makes a sum at the end the total amount here does more or less the same it iterates over the items and gets the price of the product and does the sum as well so that's pretty easy so i thought i created table so we can create a wedding grid i add some columns the id of the customer the first name the last name and the revenue and then i need to get the items by the way if you're using modern 14 and that's what i currently do by the way we are using the lts version not the newest one then this would look a bit different but here we have set items a bit in 17 the whole data provided stuff changed and now we have this nice set item method where i can pass just find all method and to find all methods by the way comes from a custom repository the custom repository is jp repository that's why we have to find all method and now i like to see that in action and start my application so let's see if this works application started let's move over the browser and now we are here so i have several versions of the same grid by the way just to show you but i start with the customer revenue list that's what i've shown you before and then i oh i have a problem now what's the problem let's have a look uh what does it says it says it cannot lazy initialize collection so that's a typical problem of hibernate so hibernate has this concept of lazy loading that means that's a performance optimization because you don't always need like the collection of the orders if you're dealing with the customer so hibernate decides oh it's better to have this one-to-many relationship lazy so it gets reloaded if someone tries to access this list but the problem is that we are no longer in the hibernate session or in the jp entity match if you like to if the or when the the data gets rendered in the ui so we have a problem there if you're a spring user or spring boot user you know since spring boot 2.3 and if i'm not wrong is an open session view pattern activated by default that overcomes this problem but that's not a real solution that's more kind of an anti-pattern because this means that probably the lazy loading will take place in a different transaction and you may get some inconsistent data so i know that i can change this behavior here and let's try i can define here another fetch type eager and this i have also to do here because this is the other lazy loaded collection if i miss that it will also fail again and so let's try again with with the eager and so i hope that hibernate will load all the data if i'm executing find all and let's see if this really works so now let's try again and now it works but oh what's happened now here you can see my console there were a lot of sql statements and i can tell you it were about i don't know let's see how if i can find out how many we have that again and i show you a trick how you can get that there is a property that you can set in springboard it's called spring jpa properties and it's from hibernate it's hibernate generates statistics and i can set that to true and so i rerun the application again so it may print out how many statements get executed for for that so here's the application back again and let's see what happens now still runs forever but i hope i will see the statistics at the end and then we can go ahead and see that we had 3107 sql statements so i tell you a secret we have 400 customers in the database um here by the way i'm using flyweight to create the tables and to insert also some data and if i open this one intel j doesn't like it if i open this file because this file is a bit large it has 17 527 sql statements so i insert some test data so the problem that we have now with this eagle loading that first of all it creates a lot of sql statements this is so-called n plus one select problem because what happens now is that hibernate doesn't do a join by the way if i use eagle hibernate will load first all customers then it will go to the order and select for every customer it sql statement to get the order so we have one statement for the customer 400 statements for the orders then it navigates to the order and then we have i don't know how many orders i have but for every order it will execute sql statement to get the order items and that's probably not what we want so that's first of all it's a bad idea to have this this can be changed by the way we could use something called entitygraphs that's a hibernate specific thing or a jpa standard by the way to define how to get the data so that it will not be fetched eager with separate statements but with the join or we could could use join fetch if we create or we write this statement by our own now that's maybe one thing that we could do but the other problem that we have here is it loads all the data so if my user comes and just clicks on custom revenue list he just sees a handful of customers so he don't need to we don't need to load all the data so let's create a better version of that and the better version means that we try to lazy load the data also with what infor the grid this we can see here so set items is no longer find all but it's a lambda expression here we get the query inside and the find all method that we see here is now a different one because this one has a pageable that is passed we have a page request here and this page request can be passed to find all here that's the pageable interface and this one comes from paging and sorting repository that's because the jpa repository inherits from a list or from a hierarchy key of interfaces and there we have a find all method that allows us to pass paging information and now this query object is quite interesting because this is a tree object that gets passed to the set item method the set item method here is no longer next list so maybe we go back here this setup method takes a collection and this item set item method in the second version takes um sorry takes a so-called fetch callback so this fetch callback then gets this query and this query object now has several properties that we can use to improve um the way the data is loaded first of all if you come from what in 14 there are not the same properties for example so if you have a look here again we see that we get a page on the page size that's something that's perfect fit for spring data that was improved in running 17 we also have as you've seen before an offset and the limit that could be used if you're doing pure sql statements then it's perfect because you can use offset and limit also if you're doing pure jpa you could use uh offset and limit what you also get and what we didn't talk about yet is you get a sort order so in my case here i added to id first name and last name the information that the column will be sortable and i pass the sort property that's uh just mark you can pass whatever you like to to find out on what column the sort happened and here is a mod in spring data helper that's by the way if you create the project this one is not the button standard but this comes from this avadin helper dependency and there we have for example this two-spring data sorter because this is a very common use case that we have here that we want to transform the query sort order to a spring sort order and now with this implementation here that's the second one customer revenue paging you've seen the learning indicator still there but it's much better so compared to that one and if you have a look here this runs forever and if we hit here it's a bit better it just creates 387 statements but it's still a bit slow the difference is that now we have just we have a paging um there's default page size i didn't configure anything and so um vadin is just calling or getting some of the data not all but there is still a problem so that's great now because if you come from what in 14 you see that i just have a fetch callback if you know what in 14 you know that with that inversions ordinand prior versions you have also to pass account callback so that's no longer necessary and that's also a good thing because in my case here i could easily provide account and but if you're getting the data for example from a rest api then you usually don't have account so you don't know how many elements you get but maybe your rest api allows paging so you're good with with this one where you just have a fetch callback but the problem with only having a fetch callback is that so you now have please have a look at the scroll bar on the right side what happens with the scroll bar if i scroll down you see it jumped up now it's smaller than before and if i go there it jumps up and so on so the problem here is that vadim doesn't know how many elements that we really have and if you want to improve that you have two options the first option could be you can't provide any count so you don't know how to count the elements and there is something new like you can set an estimate of the count so first of all something is also new with what in 17 you see that the set items return a data view in that case create lazy data view to create lazy data view or the data view as some interesting methods on it for example you can refresh an item or you can refresh all items you don't need the data provided that you were used to use in what in 14 and older but now you have a data view and on this data view you can set the item count estimate so let's try this one that's the one with the estimate and now we still have the same loading behavior so still 300 i d7 sql statements but you see the scroll bar is smaller and has the right size because i know now it's 400 but that's just because i know it usually you don't know how many elements you have in the database but it improves the handling on that if you want to make it perfect you can post you can add the callbacks bill so here we have uh discount callback so uh we have an asset method that allows us to pass a fetch callback and the callback and here in my case i'm passing the count of the customers to be true to be honest this is not correct you know because the find all here may work now currently because i have to see loading so find all will return all customers but if i would do that with a join with an inner join then i probably wouldn't get all customers maybe we have customers that never bought anything in our store so they don't have any orders so the count is not correct in that case but for my case currently that's good enough so let's get back to the slides and check this data provider thing we've seen already two of them we've seen the different types here on the left and on the right side we have a in memory data provided at the one that we used first so there's less data provider can pass a collection to set items we've seen that so this will create this list data provider this list data provider or the in-memory data provider shouldn't only be used if you have a handful of items so you shouldn't use that maybe 400 will be not too bad if you load it better than i did then it wouldn't load a lot of data in memory but if you have thousands of data records then maybe it's not a good idea to have that in memory because of that you have this so-called back-end data provider on the right-hand side on the left-hand side and this will have this ability to to the fetch and call call at account callbacks to get a lazy loading mechanism here in the center of the diagram it's the hierarchical hierarchical data provider because we also have components that's the three grid where data in a tree is displayed and this hierarchical data provider comes also in two flavors you can have it with with an in-memory data provider or vita with fetch or a lazy loaded data provider by the way that's only a simplified model you have a lot of more intermediate components here or classes and interfaces so to summarize first step we saw that in-memory is maybe okay for small amount of data but not in our case we maybe want to improve that and that's why we have this lazy callback method where we can pass this uh fetch call back uh simon yep yeah i think matty has a question or a comment yeah yeah or just a comment actually i noticed that there was some questions on the on the chat about the a button helper that that was mentioned because you use this one handy helper that kind of transforms the bottom qre method into spring data query method uh there is actually a similar method in in in dividing spring these days so if you use the latest versions of volume major it is there so you don't need this helper lab library just for this case anymore so just use the greatest version and then you have almost the same kind of method which is doing exactly the same thing i know that because i actually wrote that piece of code my team is super scared usually when i'm writing some code but this this is my contribution during this i'm pleased right i didn't know that so that's great i will change the example after the talk for sure so let's get back to the degree object i already mentioned it get offset and limit this clear page size and page and page size as well what i like to mention is the query sort order you get a list of sort orders the sort order also have uh the information which column is sorted plus in which direction so you get ascending or descending and it's a list so the grid for example in vadin allows multi sort so you can sort at the same time by multiple columns that's why there is a is a list there's another method on this query object it's called get filter and it returns an optional filter i will show you an example with this filter but this is deprecated now since what in 17 people who use vadim 14 notice filter stuff so you can filter on a data provider directly today this is done a bit different but i will show you a later stuff here we have the sword order also here still these two spring data sources example this will be changed because that's what in asmati said this is now integrated now again this lazy data view that was improved with the count callback some words about performance the problem or the good thing about the count is the better behavior of the scroll bar as we have seen but there also comes some disadvantages with that because depending on the query you are doing you have to create the same query to count and if this query to get the data is complex and slow for some reason maybe it's not optimized then you should optimize before you you remove the count but if it's not possible to optimize the query you maybe should not use count because then you will always get the count query at the beginning that is slow as the data query so you may have double the time for the first request because it counts first and then it it fetchs the data so it depends a bit on what you do but if you do easy stuff so displaying a list of employees for example or something like that so there's nothing against this count because it may improve the behavior so the count estimate we had so now let's uh before we go to filtering let's see how we can improve that because we still have a lot of queries here we have 404 jdbc statements that's probably not the way to go and there is a great thing or there are various great things with with jpa and with spring data one thing that i could do and that's what i did is i could use the so-called constructor expression so instead letting spring data generate the query i do decrypt myself and the only thing that i have to do i have to write the query by the way uh if you're not using java 16 that this text blocks or that's called text blocks here you can have multi-line strings with which are 16. and this is very handy for exactly this case like uh if you're embedding uh sql statements or jpeg work uh statements and what i did here i created two queries so i have account query here on the lower side and here on the upper part of the method out of the class i have a method that is pageable and has a name so i will show you filtering as well so what i do here is i do uh select from the customer join orders and items and because i join here now i will probably not get all customers because i don't know if all costs have orders so that's why i have a account query on the other side that does the similar thing as as the fetch query at the end and then i do some filtering by first named last name and i group this by the customer and then the interesting part is this year on top because i do not return a entity in my opinion jpi entities shouldn't shouldn't use if you're searching data so usually if you're looking for data in the database you will get back kind of a projection so you will not need the whole entity if you're getting a list of of data because usually you want to display it in a grid like we do or you want to send it to our http in a rest response for example so there is no need to create entities because entities have for example relationships that are lazy loaded you have to walk around a workaround this problem that we have seen before so it's probably better just to create a projection one way to do that is with constructor x construct expression from jpa it's called constructor expression because of the new keyword here so with the new keyboard and the full qualifying name by the way you have to fully qualify the name because this is not an entity if you've entered this you don't need to full qualify it we can create an instance or instances of customer info and it's also called construct expression because all the attributes that i'm passing here are sent to the constructor of customer info that's the way it works it works with ttos it works with with normal classes if you're using spring data you may also have this so-called interface projection that does similar but that's done that's maybe not as handy as this one because let's have a look what this is this is also something new from java 16 that's a record so a record is an immutable data structure like a value object where you simply have a constructor like this and then you have methods to get the properties so you don't have a get id or get last name method you will get an id and the lastname method i omitted to get from the java beans convention i don't know why but maybe it's it's a better style today and then you can use this this customer infrared directly and now let's see how we can use that we have here another version and in this version here the grid now is of type customer info no longer customer and here we see that i use id first name last name and revenue and here you see that maybe an advantage of of the records without the char beans conventional without the character i have customary id first name last name revenue that's better readable than if it's cat id get first name get last time revenue and that's more or less the only change what i also do i do filtering so i added the text field where i can filter and i have a value change listener and here i call a method called load data and pass the value of the text field that's taken from the event i'd load data creates these two factor account callbacks and passes first of all the name that is in the text field and then it uses for sure page and page size and also the the [Music] sort information to sort the data and now let's look uh how this behaves that's the one here if i click here you see that it executed and it executed exactly one jbc statement so that's not entirely true that's the fetch that just executed one if i go here up i see another one that's the count that's here we see the count and here we see the fetch callback and i think that's kind of a rule of thumb or a best practice however in my opinion if you have a list of data in a grid then you should only see one or two statements you shouldn't see a lot of statements because if you're seeing a lot of statements you have the same plus one problem i also work for customers that have problems with performance mostly with applications using hibernate and they're the first thing that i do and that's something you should do as well you should first display all the sql statements that are getting executed you can do that by a locker there's uh this logger here that's the archive and it's equal logger you set it to debug and you see all sql statements and the other thing that you should do is generate statistics during development then you see if you have this ample sum bombs that maybe should could so could generate the problem the worst thing that i ever seen in my career was an application for the swiss government there you could search for people and if you entered some data and it did search then you get around six or seven thousand sql statements executed but what you see on the screen you you saw 20 records in the grid because they did paging but first of all they did paging only on the client side and secondly they had this n plus one problem multiple times in their data model so take care if you're using hibernate for selecting data so please have a look and make sure that there are as few statements as possible in the output so that's filtering filtering has also another flavor but this is now deprecated but just for completion uh for the what in 14 user if you want to do that in the past you had to create a filter let's get here you have to create a data provider that allows filtering so we created a data provider here with callback so that's not different from what we do now we have the same queries so defined and the count but we have a method with configure filter and this will create a configurable filter data provider and this one has a type more so this string here is the type of the filter so now you can see that right there is a filter on the query object because now i can use set filter on the data provider and then if i do the query here on top i can get this information from the field so that's the old way to do by the way i asked mod typical why this is there and why there is no filter anymore and why query has still this filter property the point is it doesn't matter so it doesn't matter in my opinion this looks nice here with the filter but it doesn't matter if you create with every filter this thing from new so you create you can set the items past these two callbacks it doesn't matter if you do it like this or if you lose the legacy filtering that's the point now there is another method if you're using this callback data provider or another attribute that or a parameter sorry that you can pass it's the so-called identifier get and i like to show you uh this i think i have it no i don't have it here um sometimes or let's start different what in use is equals so you have equals and hash code and if you want to identify an object an object in a list for example equals is used button is not different from that but what if you can't implement equals so if you have a class that's not your class and this class doesn't has an equals method how do you know um which one is the identity of of this object or which attribute or which attributes and therefore you can pass a third parameter to the callback data provider we have the fetch callback we have the con callback and we have so called identifier get that's a method that you can pass that will be called if rodin needs to get an id and here for example we have the employee and the basket id so we use get id for that purpose you could also have multiple attributes if you like to that depends on on your use case at the end and here is are some methods uh that you can get on the dataview class you have refresh all so you can reload the data or you can refresh a specific item that is very handy if you want to reload the date after you have edited for example one record in a dialog or in another in a form and you want just to refresh this there are other methods on the data view that are interesting you can get the next element for example from the selected element that's also possible and by the way just uh as an addition i created another view that i didn't talk about i use stroke here so if you don't like hibernate and like sql for example you may have the possibility today to have this text blocks in java 16 and no longer use string continuation in any way but it's not type safe and that's why i brought in joke i don't want to talk about choke but just a sneak preview chook has a class so called dsl context that's the domain specific language class and with that you can formulate a sql statement and what you see here in between the constants these are generated this is generate metamodel from chooks or chalk for example takes my um my sql ddl statement for example with all the elements analyzes this and then at the end it generates some data so we have some sources so we have for example for every table and we get we get the class and then we have records to read the data or write the data and that's a perfect fit again for what in because now we can use the fetch provider with jooq and what we get back is for example the same customer info so here we have fetch in two so that dust mapping more or less the same as the constructor expression does or we can can use directly a custom rack that is also generated the nice thing about that is if you want to create the condition you can write the condition because these are kind of predicates that you can join then with and or whatever so you get the nice syntax to form to to define your conditions for for the sql statement for example but that's just an idea i like choke i use it a lot in my current project because we have so many tables and we have a lot of stored procedures and functions and it's very easy to access that with chuck but coming back to the example i made the last example with an employee so this employee has a supervisor and some directs and the boston top has no supervisor for sure and this is now a tree and we want to display the data like in a tree so we have here the employee three that's the yellow lane is the boss and then we have several uh or two uh managers with both five employees at the end and now the question is how do we display this and this can be easily done with the hierarchical data provider and here we also have this example in the employee tree view until we see we create a hierarchical data provider with the type and then we have a form or three methods to implement we have to implement a child count so that's pretty easy i count uh the elements with that supervisor's null in my case it's just one and then i count by supervisor to find to get the childs here with it has children method i count by supervisor again if it's bigger than zero then there are children and the fetch callback at the end gets the date dive the parent is null then it's it's top level we get the surprises by now and otherwise we get the supervisor by its parent so it's super easy to create a tree based data structure and to display data like this that's the same and even here we have also the query so i don't use paging by the way here because i know that i don't have a lot of data but if i had a lot of data i can use the query because it's the same it also extends query so we have offset limit page and page size that we can use directly in the hierarchy hierarchical data provider as well and to summarize maybe take care if you use jpi and hyphenate i see so many performance issues with that not because hibernate is bad so i don't want to make the impression that hibernate is a bad thing but hibernate comes with some features i would call it like lazy loading uh that you have to be aware how it works to get the best out of hibernate because hybnet is great if you want to load and write three based data structures or graph based data structures then it's a good thing but you should really consider using projection and java records with the java 16 that's a great thing that's a perfect fit for hibernate to read read only data that you want to display for example in a grid that works very well and to go back to what invading is really a great framework and is especially great framework if you have data centric applications so if you build something like the erp or a list of customer list of employees whatever vadim works very well and not to talk about uh um all the same with uh with forms and binding just uh the existing of this lazy data providers that reduces memory uh enhances the performance because it doesn't load all data that's really really great thing and again uh to finish my talk check the sql statements so you should always have uh the hibernate locker turned on if you're developing to see if you produce the n plus one select problem anywhere in your in your card yes that's all from my site are there any questions thank you simon that was a very good presentation but what you were the first ever uh external guest to avoiding deaf day event as a speaker that is so thank you very much for for joining us and on this event i really enjoyed the presentation um i hope we can get you in future editions of the of the event uh on the questions side well here's one that's not very technical but more of an opinion juke versus query dsl which is better why oh that's very simple it's true the the reason why it's as a first thing that we have to to consider is chook is maintained actively maintained and greedy has not created asl as a problem because queried they had a problem with with the committers because they are all volunteers so nobody is earning any money from query dsl and you as what in employees you know you have to find a way to monetize open source and chook find that way because chook is not entirely free if you're using an open source database like i do postgres for example then it's free but if you're using oracle database a microsoft sql server or any of the other commercial database then you have to pay a subscription and that's why joke also works on the other hand the better but much better with with chook is the support of multiple databases because if we look at this query here do we have it say here this query is free from database specific stuff but i could use database specific stuff here as well and chalk would emulate the function if it's not available in the target database so that way you could use the same joke code on multiple databases like you do when you use hibernate that's one thing and the other thing is uh watin has a truck has a lot of support now for example for json or xml data structures so to load hierarchical data in a sql statement that's all integrated in in juke as well chuck has a lot of other stuff like you can have a you have multiple callbacks when you execute the query you can do things we for example we use chuck and we have kind of a ui builder integrated where the customers can change the behavior or block of of the ui and we captured the sql statement that joke is generating store them in the database and then we can re-execute them if you migrate to database for example and chook can generate the code from multiple sources so here in my case i use flyway i could also use leaky base as or any other tool that has sql statements um and then it generates data from from the sql statements but i could also use any database that is supported to generate the meta model from so i really like like joke and the best thing about joke is chuck has a great blog you should if you don't know yet you should go to chuck.org on the blog and you will learn a lot not about joke but a lot about sequel so luca said the main contributor of of chook is very active in that space and is a sql export you can ask him anything about sql he knows more or less anything that that is possible and uh to finish my answer uh chuck will support reactive programming model in the next version so you can use r2 dbc as drivers and then you also have this non-blocking sql execution you don't get that with green dsl great yeah uh i i think you could say a very good uh it's a good match for for valid flow applications uh kind of share the filezilla feels right of writing java um there's some questions about um the source code i guess it's going to be available sure i didn't have it included but if you go to github 72 services it project is called what in performance i put it in the chat here you can share it with the audience everything is here by the way if you if you're using that the first start will be more than half a minute because it will execute the 17 000 sql statements in the first run and therefore you should use postgrestart database you could also use it with h2 but every time you will start it and you have an in-memory database you have to wait half a minute so maybe it's better to create a database that's very simple uh the best is to use docker to run it that's very easy and then you can create a database lot in with the user button and grant all privileges we don't bother if this is a good idea or not here it's just the demo and then you can run it from from from the command line or from any tool by the way i borrow it or wrote that from startup.com uh the readme just addressed it for for my exam yeah all right uh i think uh there were maybe uh some other questions that we missed but uh we are getting uh um yeah there was at least a question about three crit and how that that does with lazy loading by phillip let's how about this let's let's take it at the end of the event if you wish uh so so we can actually have a one minute now uh break uh before leaves uh talk and then then we can get back to these uh questions sounds sounds like a good idea okay perfect all right so uh thank you uh simon again and let's continue in around a minute or so yeah and we just leave just wait i guess it's by a break um all right all right i guess we can start with life let me add the presentation to the stream all right let's take it away all right thank you so hi my name is lame slave i don't have any slides for you i will just show a bunch of things live i work as kind of do it everything on vaadin i mostly do architecture related stuff but i also do all the other things that nobody else does and yeah what what we're going to talk about today is this thing in in flow that is named execute javascript and it's it's a really powerful thing that lets you embed or integrate any kind of javascript thing in your flow application because if you think about it your application runs in the browser which means that anything sooner or later will happen as javascript that happens in the browser so by speaking directly javascript you can kind of do all the things that you could do in any other kind of application so you can kind of break the limitations that you have from from just server side java two different things that these are useful for one is to integrate different kinds of components like for instance uh all the volume web components that you use like the grid then the button and so on they are implemented in this way but also if you go to for instance dividing directory and find lots of different components there like google maps and and all lots of other things they also use the same mechanisms and the other thing that you can do is to when you have an existing component or or something and you just want to tweak something that isn't supported with the java api then if you happen to know that okay this is still possible to do somehow with javascript maybe because it's integrating something with more features or or something then you can also just kind of hack yourself into it the example i will be using to show these concepts for you is a grid implementation so i tried to come up with what's the kind of suitable difficult thing but still manageable to do and since voting is like we saw in the previous presentation all about lots of data then the grid is perfect for that so you can actually if you want to follow along here in the github repository and these steps here are basically what we'll be looking at so to get started i have cheated a little bit this is the first version of this application or this grid integration and it doesn't look like much but you can see that there's this border here and that's actually where the grid ends but it's it's completely empty and to do this this is all the code i have so or actually starting here we have a view that just creates our new custom grid class component and adds exit review and what this does it has a bunch of annotations it has npm package saying that we should load uh javascript package from from the npm registry and we also define a version of it then i have at the js module telling flow that whenever this component is used somewhere in the application then this script file or the the the script of this module should be also included in the in the bundle that is loaded and then finally i'd have at tag which again says that when an instance of this component is created and added to the application then in the browser the counterpart there should be use varian grid as the html element name and that's all we need to get the grid there but of course that's not all we want to do so we actually want to get some data in here and always when you want to do any kind of javascript integration you should start with thinking how would i do this without volume or or flow basically at all how would i do it if i just would would build this in a client-side application or or basically what's the kind of native way of doing things so that's also what i will be looking at here for the grid so looking at the documentation i see well here we have a grid with some columns and some data so let's see how that is done and looking at the source we see that well we have avoiding grid element it has an items property which i see that well okay i don't see it here well but i know that it's it's just a list of items and then for each column there's avoiding grid column as a child which just has a path attribute defining which part of the of the data it should use and then also the header caption is directly based on that so let's build that for our grid so i want to get those those columns first i want to have an id column and a value column and these are the paths i want to use so then for each of those i create a new bar in grid column element like this and then i set the path attribute and then finally i need to add this to the grid and to do that i get the element that is kind of backing this grid component and then i append the columnless chart and the next thing is to get some kind of data there to see that we're doing so i want to get a range of numbers and just create one object per per per number there from 1 300 and i want to have these as generic javascript objects so json basically this is an item and then for each item i put in id property which is the index and then i put in a value property which is value and then just the index and then i need to return the item then i want to collect these into a json array there is in flow an internal helper json util as array which does exactly this you should maybe not use it because we don't promise to maintain it so you might have to do this manually but i'm cheating a little bit here and then we get the element and set a property items as json so this is all we would need to do here so what we're doing here is we're kind of creating child elements we're setting attributes on those and then we're also creating a more kind of complex data structure and setting that as a property and in html you have these kind of almost similar but quite different things which is attributes and properties an attribute that's what you set kind of through html so for instance if you look at this example then path equals and then something that's an attribute properties on the other hand they are set as javascript so you cannot just from native html set properties but for instance this example uses lit html where you can use dot something and then you actually can use a javascript expression so in that way you can assign properties in most cases these are kind of one to one so the same element supports for instance path could also be set as a property but there are some special cases where that's not working but anyways now with these a couple of lines of java code i now have this grid with 100 items here the next thing i want to do is to make so that i can select items in this grid because now if i try to interact with it i can select text but i cannot really kind of select items and again looking at how would i do this manually so here we have a single selection example in the documentation if i click arrow then it's highlighted if i click another one that one is highlighted if i click that one again it's d highlighted and so on the way this works is that it listens to this active item changed event and then it gets an event and from that event it gets details and the value there and that's the item that was was activated or deactivated and then the way it's done here is that if we have an item then we create an array containing only that one item otherwise you use an empty array and then we set that as selected items which is then also bound to the selected items property on the actual grid so that's a couple of steps to untangle i would recommend especially with these kind of integrations to always do it one step at a time because you have lots of opportunities for typos because because we have lots of hard-coded strings and so on that we need to use so always take it slowly so that you kind of if you make a mistake then you don't have five steps to to see through that did i did i do this right did i do that right but anyway what we want to do is to listen to this uh item active item changed event from java and there are two ways of doing that one is kind of a low level way using get element dot add event listener it's a little bit more flexible but it it's again it's a more low level thing and just the code isn't as readable i will instead show the more high level way which is we actually define um an event class for this so active item change event and that extends a component event which is part of flow and then we define that the source of this kind of event will also always be a custom grid instance and then we need to give it a constructor these are the things that are there by default we want to add one additional thing there which is the id of the selected item and let's put that to a field and with this we could already now listen to these events um so we use component util which just basically contains stuff that should be in a component class but we don't want to put them there because we want to keep the api cleaner so things that are not so often used to instead put in the util class and and you can use them from there instead so we want to add a listener to this component class and the type is this active item change event class and then what we want to do here again as i said take things one step at a time so we just if event id is null then we show a notification so saying deselect and otherwise we show a notification so show saying select and then also id so far this i mean it compiles but it doesn't do what we want we need to also add a little bit of metadata to this event class so that flow knows that it should do something also on the client side when we add listeners for this so i can add a dom event annotation here and put the name of the dom event there and the second thing i do is that for this parameter i need to tell flo how to actually find this value in the browser as javascript when the event happens and then flow takes care of actually sending that back to the server and actually calling this constructor with this value so i need to put an event data annotation just a new line and and here i need to write a javascript expression that will be run to actually actually get things working so it's something along these lines except that it will be event is the name of the event object here and then i want to get the id of the of the item that was selected which is kind of the same id that i sent sent here in the json but then also in case nothing is selected value will be null so then i use this newish javascript feature to if if value is now then this expression returns null otherwise it uses value dot id one word or warning here is that these javascript snippets will be used directly as is and this is a so new feature that it's not supported in all the browsers supported by varden so this won't work in ie 11 but i won't care about that so now if you go to this demo and now when i click something we get a notification they're saying select or deselect if i click the same row again so the next step then is to also set the selected items and to do that i will need here in this event handler i will need to set that property but actually actually see in the documentation i cannot use just the id i need to find the item itself but in this case it would be quite waste of bytes to take the whole item and send it to the server just so we can send it back again so instead i will just pass the id back and forth and then instead use a small javascript snippet to find the item from from the items property that we have already populated so to do that i do get element and execute javascript this is kind of where all the magic happens and what i want to do then is this dot selected items this refers now to kind of this instance and that's an array that contains one item and that's this which is again the grid dot items and then one of the items from there here i use a placeholder variable that i define the value of as event dot id and this this thing here that's it's kind of like a prepared statement in in sql but the kind of javascript corresponding thing in flow so i could in theory instead do spring concatenation but then if i'm not careful we might get cross site scripting vulnerabilities and so on so always instead pass things as as value or as parameters in this way and then for the deselection thing i'm just passing an empty array there and i don't need to put any any parameter to that so in this way we should now see that that items can be or roads can be selected we can deselect them we can select something else so yeah awesome we're almost done here there's always a but if i now emulate a slow network we will notice that when i click to select i don't get any feedback at all as a user until after the round trip and that's not really that nice so what i want to do instead is to make it so that immediately when i click on the client side without involving the server at all we will update the kind of visible selection here but at the same time we will also send the values back to the server so we can run our kind of business logic which here just shows the notification so to do that instead of setting these uh in the listener i want to actually yeah i want to run another javascript in the constructor instead and there i do add event listener and the name of the event is the same active item changed so this this is just now a javascript snippet that will be run whenever this grid constructor is run what i want to do there is say these those selected items is an array containing and here i can just use event detail value so if nothing is selected then there will be an array containing only null value which grid also will use in the same way as kind of not selecting anything and now with this done waiting for refresh there we go so now if i make the network slow again we can see that immediately when i click the highlight is updated but then only with the delay we get the notification so that's a great improvement for this particular use case but there's always about now as you can see we are getting kind of a little bit too much javascript to have inside a java file so i want to extract this into a separate javascript file instead or actual typescript file so i want to just trigger that once here in the constructor so kind of init grid and pass this so the grid instance to it and then i want to add a new annotation saying that we had another script file that should also be used and i will name that grid.ps and because the typescript editor in vs in eclipse is not so good so i will state use vs code for my typescript editing so here i am in that i will paste the old code just so i have it handy but what i want to do now is to create this init grid function and we run and it gets a grid and it's a function but the thing is well actually there's multiple things here if you look at quite much documentation especially all the documentation for how to integrate this and that with javascript like if you look at how to use google analytics okay well not google analytics but but many of these things say that this is what you should do to kind of define your global callback or something but now because voiding is using a newish feature called javascript modules it means that undefined variables that we do here they won't end up in the global scope as they did in with regular javas or old-fashioned javascript so to say so instead i need to explicitly say that this should be assigned to window which is the global scope but this is not enough because here we are using typescript which tries to protect me from mistakes and typestrip doesn't know about something called init grid on window so here i need to tell typescript that it shouldn't try to do type checking on this particular thing and the other thing i can do is to say to typestrike that this thing that will get this method that's actually a grid element that i'm importing here from the js module so now with this defined we have really nice uh syntax highlighting and and code completion and everything so for instance typestrip knows that there's a selected items thing here and so on so we can basically take this old snippet that i had and slightly updated so this is now the grid and let's split this up to a multi-line thing just to make it a little more readable and this is the grid but then we have again one more problem which is that typescript doesn't know that grid fires active item change events that contain a detail and that that detail contains a value i'm using volume 14 here just because i suspect that's what most of you are using uh but in newer versions this would work but here i again kind of cheat saying to to type street that it shouldn't be concerned about the types for this particular event variable so now with this in place here and on the java side i am calling the init grid method and passing the grid to it so now hopefully we have the same functionality as before now we just have more manageable code but there's a problem uh we actually introduced a bug when we moved this to the constructor instead of doing it in the event listener and to see that bug we need to to actually do something that finds it so what i will do is i will add a button here that that will toggle kind of remove and add the grid back again so if grid get parent is present then we remove the grid and else oops else we add the grid back as the first component because now what we will see is that no selection works yes and notifications go away thank you so now when i toggle we remove the grid and now we add it back and now we see that i get the selection of notifications but i don't get the highlighting anymore and the reason for this is that uh for instance this listener that we're adding that state that can kind of be reapplied because as you see we're reusing the same grid java instance all the time but on the client side a new instance will be created because when we completely remove the component then it will be become garbage collected on the client side and completely removed so then when we add it back a new instance will be created and state like whatever listeners do we have that gets reapplied by flow children get reapplied by flow property values get reapplied by flow but it wouldn't make sense to reapply javascript executions because those those are kind of actions they are not state so actually what i need to do here is to wrap this into um attach listener so that whenever this grid instance get attached again then we run this in initialization again now there are a couple of special cases where you might get multiple of these attached events even though the client-side instance is not recreated so to be on the safe side we can also here at a small check to avoid re-initializing so we can define weak set that will contain all the new weak sets of course that will contain all the grids that have already been initialized and then we can do if emitted has this grid that we're about to in it then we return immediately and then we also need to add the grid there so this way we have everything set up so that we can reselect things or toggle visibility and still have everything working so now here if i well now selection works yes i remove the grid add a new one and then we still have the selection highlighting there so yeah we have seen a bunch of things but there's a couple of things more i want to show you the first thing that simon also talked about was laser loading because here now i'm just setting 100 items and that's quite fine to send as one big batch but what if we have for instance 10 000 items again then it wouldn't make sense to send all of them to the browser immediately when we open this view so instead i want to add lazy loading here now this documentation page is still being worked on apparently it actually says work in progress here but if i go to the api documentation i will find how to do later loading with the grid web component and the way it works is that i need to set a data provider property on the grid and that data provider is a callback that receives one one parameters thing which kind of page and page size and a bunch of other things and then we receive a call back and to this callback we should once we have the requested items we should call the callback pass the items there and then also pass the total size so the number of items or an approximation so let's unpack this let's start on the client side so we want to do grid dot data provider equals something which get params and callback and this is an arrow function and here i want to make a request to the server saying that hey could i please get these and these items so how do i do that well the thing is if i go to the component i can add a method here request rows that takes page and page size since that's what i will get from from the clients i grid and now if i add client callable as an annotation then this will get well as it sets by the name it will be callable from the client side and again let's just show a notification first to see that we do everything correctly so request and we have a page and page size no page size i said so how do i call this thing let's first actually try that out in the browser so if i select the grid instance um actually the grid itself not the content then if i go to the console and in this context dollar zero is the grid and here i can see that it has this thing called dollar server which is actually injected thereby flow through that component instance and it contains a request rows function so if i call this with some values then this notification is shown in this case so that's what i can use and now let's go to the typescript code to actually start using this so i want to do grid dot but then the problem is that typescript doesn't know that that flow will inject this thing into into the grid instance i could again do the same kind of as any trick as i have done with with some other problems that typestrip doesn't know about but i can also define a custom type for this this thing so i will define an interface let's call it custom grid and it extends the basic grid element but it also contains this thing called dollar server and this thing contains thing called request rows the type of best it's a function that takes page which is a number and page size which is also a number and for now let's say that it returns void and now actually i also need to say that this thing that we get to init grid it's not a regular grid element it's a custom grid that flow has injected the magic property into so now hopefully typescript will know that yes there is a dollar server it contains a request rose with which we should pass a page and a page size so let's take those out of the params object params.page size and now if i save this and open my grid again and we see that we get the notification show that okay page number zero and 50 pages long that's what has been requested so now we just need to return the values and actually make grid use them so what we want to do here is kind of the same thing that we did here with generating items but we want to move it here into the callback or the client callable thing and this is based on in in interviewing indexes now so the start is page times page size and then we can see that we should start at start and we should go go on until we are at start plus one page size so then we will get the right items for this request and then we can just return those items we need to update the return type of this client callable method and that's all we need to do on the server side now on the client side um no actual client side i said here we have the client side code we need to get this value back so this thing it doesn't actually return void it returns something asynchronously because we're doing a server sound kind of request and then get a response back later so it's a promise that's the javascript way of saying it will happen later and what we have here we have basically an array of something that that we don't need to know anything about and now with this we can directly use promis is kind of like completable future in in java so you can do dot then and then you pass a callback that gets the value but javascript also has this very convenient thing i can mark this callback or this function as asynchronous and then i can just await the value so these are my items that i await and then once i get them i will call the call back and i will pass the items and if i would do this properly i would also have fetched how many items we have from the server but now i will just put a hard-coded number here just to to keep things simple in this demo so now with this we have items in the grid and we have a lots of items or more exactly we have a hundred ten thousand of them and we also see that kind of as i'm scrolling it's making new requests all the time and always fetching kind of 50 items at a time so now we have the perfect grid there's still a couple of small features they also want to show you but they kind of they don't really make sense in this grid anymore so i will just show them kind of a standalone things here the first thing is there's a shorthand because quite often what you want to do is just to call up function on the on the custom element that you're integrating so let's create a scroll button here and what i want to do is to get the grid instance get the element and call j as function and scroll to index is the name of that thing i remember from the documentation so this actually shows two things it shows this called js function it's just a shorthand instead of doing this dot scrolling scroll to index and pass dollar zero into it the other thing we see is that get element is public here so any voting component you have you can do get element on it to mess with its internal details with that you can do lots of interesting hacks and tweaks and stuff you can of course also make the thing not work at all you can break it but in many cases it's useful if you have some really small thing that that you know can be done with with a javascript integration so now we have this scroll button and when i click it i scroll to row with index 42 which is exactly what i wanted the next thing that in sometimes useful is to get values back from javascript in into java and to do that we again do grid get not get children get element and we want to execute some javascript stuff i don't want to pass the parameters to it so what i want to do just for the sake of the demo today get the width of this grid so this dot offset width where offset width come on like this is the javascript way of seeing how how wide it pixels an element is and then this thing is also then able in a way so uh i can get it as either just consumer that gets a json value or then i can directly convert it to some basic type so in this case i want to have a double pass and a callback getting a value and again just showing a notification the width is oh it's value value like so so now when we run this we'll get a new button and this this gets a little bit tricky because what happens here is that when i click this button the browser sends a request to the server the server runs the click handler the click handler says that hey run this javascript then that runs in the browser it sends back in the response that javascript runs and then it sees that hey we should return a value back to the server again so then the client side of flow sends another request to the client to the server to actually deliver the value so that's also what we need to use this asynchronous kind of callback waiting for the result to come later instead of just getting it immediately but still it works so that it doesn't work i just get no and this is a really typical mistake i do it on purpose but you will make it by mistake i forgot to actually return the value here so whenever you try to get a value back and you just get now sometimes you actually do something that produces null but many cases you just forgot the return statement here because this can actually be it can be a long javascript snippet you can have like multiliner with semicolons in there and so on but now when i fix this bug we actually get that now this is 508 pixels wide and if i click again we see that it's 8 31. so that's getting values back or another way of getting values back because we also get values back with uh the as events and in a way this client callable that i show you can also be used for getting values back but in different use cases different things are are valuable to use the last thing i want to show is to synchronize also properties so for this we could use an input element so input is just an html kind of text field with minimal features uh and then i want to get the element backing this div that is this whole view and append the input element there so one thing we also see here is that with this div it's a volume component yes i can add a component i can add an element i can add another component and volume doesn't care because beneath the surface this grid also has an element and that's what actually gets added and so on so in this way you can even mix and match between the high level component api and the low level element api but what i want to actually do with this input is that i want to add a property change listener to it and there's two variants one that takes just a string and a callback the it takes two strings i use the two string variant so the first thing it takes is the property name and that's value and then the interesting part it takes a dom event so the name of a dom event which should trigger actually synchronizing so reading the value property from the client side element and send that to the server so that it's available there also and in this case the event name i want to use it is input and then finally we have the listener that will be run whenever this property changes so it this would also be run uh if i do servers i just input set property uh notification oh show notification typing is difficult sometimes there we go now oh there we have it um so i just want to show that the value now is i get the value in in the event so i haven't get value but to also show you it's also actually synchronized into the input element itself so if i get a value from here then we will get the value that was the client side value of the element which is also then synchronized to the server so now if i type anything into or every time i press a key here we will get the latest value property of the input synchronized to the server and then we show a notification that was all the javascript features i wanted to to highlight and kind of these are all just building blocks you can combine them in different ways and use them to integrate basically anything as long as you know how you would use it with just plain javascript and you know about these flow building blocks then you can create your own grid component if you think that the voting grid component isn't good enough or anything else basically so the concepts we have we have starting from where i started adding child elements to web components setting attributes on any components we can set properties also though i removed that code already we can listen to dom events from the server we can use this high level component util add listener thing or we can also use getelement.advent listener which has a couple of additional low-level features that might be useful when listening to events we can instruct flow to take specific values from the event or also from the element that fired the event and send those back to the server so we can use them for doing something sensible then we need to remember that initialization needs to be done every time we attach the component because otherwise it the client-side instance might be removed but the server-side instance is still reused then we saw how we can extract our script into a standalone file so that we don't have long javascript or typescript snippets inside java code then we saw about client callable how we can use that to from the ser from the client do our pc to the server and also pass return values back then we have seen shorthand for calling functions we have seen how to get yet another way of getting values back if you kind of explicitly ask for something because here's the different thing with execute javascript we are asking hey client could you send this thing to us whereas this thing with client callable it's the client that kind of takes the first step and says that hey could you please send me this then finally we also saw the the property change listener to synchronize property values whenever there's a dom event that kind of tells that hey now this value has proper probably changed and then volume will take off actually passing that back to the server so with those building blocks you can do anything with flow do you have any questions thank you leaf uh that was a great presentation uh very powerful uh let's say feature of of inflow uh there are some questions so uh let me find them here in the command so that's why i didn't ensure that the function example init grid is removed from window when the component that caused the caused it to be added is destroyed yeah no it doesn't because what what this annotation says is that this script should be included in the application bundle so for performance uh every all the different client side dependencies like all the web components that are part of vardin itself and any custom things that you add all those are bundled to one big file and when you when you load the page then all that script is is loaded and run so what that means is that this is run just as any other javascript it's kind of permanent until the page is reloaded so this function will be here forever on the other hand this is just it's just code that is loaded so it doesn't really waste any resources or or well it wastes really really minimal resources so it's not a problem that it's not unloaded in any way but then if you do things on the element instance like the way i do in the beginning assign items property to grid those are kind of well the element instance itself is removed so then also all the values that you put there those are those are removed right uh next question we have here maybe i've missed something but what is the advantage of doing it with javascript typescript and not only in pure java and servers right there was already some conversation in the chat about it so um some comments might matty as well so please feel free to to jump in matthias if you want yeah yeah it's basically implementing components so so you saw some very like tricky javascript tricks hit by late but never write that kind of code into your own application write those into your component implementations and hide it there and create a nice looking java api on top of that code yeah so basically these are these are the things that all the voiding components are using internally if you open for instance grid.java it's well it isn't actually full of these things because grid.java has so much bookkeeping for for managing data providers and so on but it still has a whole bunch of those kinds of things using exactly these mechanisms and these are low-level things allowing you to integrate anything but then definitely you as a kind of component developer it's your responsibility to create a nice java api so that you can kind of get all the abstractions that used to using you get you you implement support for something once and then you can use it anytime you want so that's the kind of point because what this do is it allows you to do things that are just impossible otherwise and and that's the whole point of it yeah and this is this is kind of what the actual flow framework provides so flow flow framework is actually providing these low level things and then what you as a flow developer actually use is usually the component implementations that are built on top of this api so quite quite quite that often if you are using vodinflow then you are actually using bottom components which are using body flow to implement this java api for you yeah but as an example just today in body directory there was a new interesting add-on released it lets you it basically it adds a new feature to the grid component because the grid component apparently didn't have a way of scrolling to a specific pixel position or to get back to the server what is the currently scrolled to pixel position so then someone it was about an employee but anyone could have done that they created an add-on basically a small java class that uses these kinds of mechanisms so that it can just do i think it was kind of grid scroll something dot scroll to and then you just give the coordinates as a java java call there and then behind the scenes that internal uses these kinds of mechanisms to to actually make it happen because the wording grid class java class itself didn't provide a way of doing it this but it's possible using javascript yes so it was basically kind of an extension to the body butting components yeah very good yeah um very kind of um low level let's say part of of of adding that allows you to uh extend even like life mentioning with these add-on standard features of existing uh components i would say it's the assembler of audio it's the assembly robot yeah i like how that sounds yeah uh so be responsible when you use it and this is a good api for for the um for the actual application layer or presentation layer of your applications so i have another this is kind of a command but let's address this uh seems quite fragile having to use screens that the compiler cannot check yes that's absolutely true and that's one of the reasons why i also moved out this no actually yeah exactly this i moved out a bunch of code to a standalone typescript file that can be checked of course like we have uh here and there there are some some script files that oh screw hard-coded strings that still cannot be checked but that's kind of that's the price you pay for using assembler so there is unfortunately not any way around it but again kind of try to limit the exposure try to do as little as possible as magic strings and then give a nice safer java api around it and move the as much of the client side logic as possible into into a separate file that can be checked by typescript right um okay here's another one let's see so if i have multiple users of this method i'm not sure what's this method i need to ensure that there are no collisions similar to name spacing yeah i guess that refers to this init grid function that i published in the global namespace that is absolutely true we are thinking of a way of dealing with that also but we haven't implemented it yet so i i would recommend that kind of do a little bit of name spacing in anything you you publish as global things like this so kind of instead of just init grid it could be on acme create a grid or something just to reduce the risk of collisions kind of putting place your own namespace uh where in the code 3 do i put those oh yeah sorry yes files i forgot to show that uh it's inside the front end directory in the in the project itself so anything that should be handled by the by the client-side build so typescript files and and so on those go into the the front-end directory and then an additional question where do i put it when i'm building a reusable add-on component src main resources something i don't remember you can google it yeah but it's a different place if you want to do it it's a different creation the easiest way to find it is that you go to vardin.com start and there you have you can download a kind of add-on skeleton project and that has a file in the right location for that purpose i guess it's also documented in the uh it is surely documented somewhere well documented yeah we can try to share a link to that page later uh let me see i think there are more coming here okay here's document would you find a way that would be awesome especially when using multiple component libraries it would be helpful and this is an actual question uh easier to integrate extend their party components than it was with with uh it depends i mean it's it's easier in the way that that you can use all the features that the component you integrate provides directly whereas with grid you always have to kind of how do i use this stuff from from java code and it's also easier in the sense that you can usually just copy paste examples and so on from the documentation of that thing but on the other hand it's not as easy because you actually need to know javascript and types or and or typescript instead of just being able to use java all the way through so it's it depends a little bit on on your preferences and so on and here i actually found the answer to the previous question because i looked how grid is implemented so inside the grid flow jar it's meta resources front-end and that's the location of the of the grid connector file which which contains all the it's actually quite scary file because it's there's lots of things to integrate here as you can see but that's the location and then of course to get this into into the right place inside your jar it's src main resources meta in resources front-end in a maybe project yeah which i assume 80 percent of all users use and the others use gradle which use the same conventions yeah just want to share this uh answer uh somebody uh saying that i think it's easier i think they're referring to uh yep since oh yeah i think we can use existing js examples and not have to translate them to so there you go uh um that's a valid answer as well and felipe actually uh identified the the the folder already just after that you put uh front end and there's that's where you put the files thank you philip uh what else do we have what's the time so we have a round of four minutes how do we quickly uh go through the questions that that we um had for simon [Music] now it's tricky for me to to find them here but i have taken note of that so some ideas about optimistic loading i think it was uh lockheed probably it's a typo optimistic locking support in a similar project if simon has any idea or experience in this or any of you for that matter i at least use optimistic locking in all my all my hobby applications that use jpa so that's quite quite the same way as with whatever java applications you have so you just have the version if you're using jba then you can just have the version version field in your entities and then just wrap the save action in a try catch clause and then if you get the optimistic locking exception then you shall somehow deal that with the end user you can either show the differences or then just say that sorry your changes were lost or whatever you want to do there but naturally you want to do you want to use the latest body in addition the collaboration engine and then you never have this optimistic locking access again so then you actually see when somebody else is using the or editing the same entity yeah the only problem with optimistic locking is if you're not the only one who is dealing with this database so this is the case in my project currently so we don't use a version field we use all the fields of the database table to do optimistic locking and this is supported by jooq out of the box so with choke you can define the optimistic locking mechanism either use a version field or a timestamp or you use all the fields and that's what we currently do because we have background processes that manipulate data that we that don't work because we built something like you mentioned we have a kind of we don't have a collaboration engine but we have a mechanism to to lock data on the application layer so if a user opens a certain record he gets a notification that is currently edited by someone else with the username and he can he cannot chat with him maybe should integrate the collaboration engine a nice uh use case for that it sounds like so um okay so this is i think the the question that that matti was talking about before since simon just mentioned data intensive applications could you shed some light on how to efficient efficiently collapse all nodes in a three grid my three grids have around 100k nodes yeah yeah yeah this is the one that i was i was wanting to comment before so yeah yeah we when we did the data provider lazy data provider changes uh we didn't have time to actually look into three click at all so it's it's quite from data pointing data pointing point of view it's quite a different piece than than showing just like tabular data in a grid so so we still need to look into that at some point to make the lazy data pointing in three grid work well it doesn't currently it's it's it's quite tricky still i i don't know if if that is possible even all right thanks thanks matty um i think uh we can have another one minute break before uh the next talk uh so let's continue in around that in around a minute uh all right all right i hope everybody got a cup of coffee tea water coca-cola maybe not if you are ronaldo watching this or wherever [Laughter] good good um yeah my team you can i guess start your presentation if you want to share your your screen oh yes yes i do i have some slides of course and that i have a couple of demos so all right yes so my name is matthew daven i work as product owner at vaudin my responsibilities are the actual flow framework and then all the integrations that we have for that and test bench and also designer my today's presentation is mostly like bragging about the flow tip uh i'm listed there as actually a part of this team but i'm hardly a part i'm just the one who actually orders some stuff from this team so so so if i if i'm doing some changes the this team actually treats my codes like external contributions floating is actually the second largest r d t mint body there is only one team which is larger and that's the so-called design system team which is working on the component apis so we work on the core core things and then then the components team actually they they do the apis that that you mostly use there are some like framework little things of course that you use at what in uh from voting flow and those will be handled handled by this this theme so also the build tooling that belongs to the floating integrations to spring cdi osti whatever and then we have actually some uh other tooling for for those who are coming from older versions of audience so multi-platform runtimes that allows you to run one in seven or eight applications inside your flow application and we are maintaining that as well and then then there is test vents and uh i mentioned designer there as well i'm the product on our designer these days but but that's not handled by the flow team so we have a small small beam for designer who is taking care of the designer maintenance and development that's actually quite a lot of things that we have on the floor teams table uh a lot of context switches that that we have disk these guys need to do so it's not an easy job so so all the respect in my team they are doing wonderful job uh this is basically how how we work on the daily basis so roughly 70 of what we are doing is on the maintenance side so body flow and the java parts of volume they are very well established framework and uh and there is never any like revolutions coming up for for the flow it's just evolutions and smaller smaller things and new integrations and improving mostly improving existing features but of course a lot of new new development as well sometimes it's actually quite hard to say that what is like new development and what is maintenance certain renewables or or updates to existing aps they they it's it's it's almost very very hard to say that if it's a maintenance or not but yeah security pitches then there are priority fixes that are customers that they have this ability to prioritize certain certain bucks that they find important then my team takes actually those to the top of the priority list we try to solve those as soon as possible if there is something very urgent going on then then it might be that people have to wait for a week or two but but usually within a couple of weeks they they have been being being dealt and then yeah maintaining dependencies that's that's a big thing so we need to keep we we need to make sure that vaadin for example works with the next version of java and it needs to work with the next version of spring and then if there is a security issue in one of the dependencies that we are doing we need to maintain those then performance optimizations and and all that falls on the maintenance side and then yeah new features i categorize usually if i can write a blog post about a new feature then it's a new feature development uh so today uh we will go through about what we have done in the flow team during the past half a year a bit more than half year and what we are planning to do during the summer and during the autumn uh i will also show a couple of demos and and and and in the end we can actually discuss that what you would like to see so we can have a lot of time for the questions as well so live reload is one of the coolest things that i think we did recently i i have at least enjoyed it by myself in my hobby applications that i have built with barton when i'm developing those i feel much more productive these days in life's demo you actually saw quite a lot of that already but i could actually still go to intellij where i have one project this was generated just before the start and i used uh spring initializer actually to create a very very simple project but i didn't choose the spring tool dev tools because i'm i'm more into using j-rebel or whatsapp agents for for for the live updates so so the system that we have to reload the browser it actually supports different back-end technologies but i prefer to use j-rebel or hotspot because they are slightly faster so basically when i created the project i just enabled shareable here which i have installed in in the ide and then when i'm running the main method of this spring boot application i will use rebel run so this now use this the jbm agent by by j rebel and when i have the application up and running let's go here and go to localhost 8080 let's put this screen here on the right and you can see this small gizmo here which is only shown in the development mode so you can see that library load is active and uh it's using t repo as a kind of a library load back end there and now if i go to ide and change something here uh hello that day and now if i hit command b which is which basically builds the class it i don't need to go to the browser at all but you can see that there is a small notification that the browser was reloaded automatically and uh this way i don't need to leave the ide this is kind of a weird situation because i have everything here on one screen but if you have the typical development setup where or developer setup where we have another screen so if you have a dual monitor setup then you probably most probably have the application in another screen and the code in another screen and then then you can keep keeping the id hit the save or compile or whatever and then see the changes changes in the browser everything pretty much everything works now in the beginning it didn't support for example cs exchanges and and template changes but in volume 14.4 we added support for those as well and in the master versions everything was released in in 1 in 17. then we added a new lit template support so this was introduced in body 14.5 in lds series and in body 17 and 1 in 18 in volume 17 there was a kind of a re-mental support and then for body 18 we improved the documentation and also added uh some small enhancements for template support in general so nowadays you can actually read certain values from the template itself so if you put a text of some spam to something you can then read the value on the server side and you don't just get empty values there polymer templates will be still there so if you are using those there is no rust yet to migrate those delete i suggest to create all the new templates that you are using either either using body designer or manually i suggest you to create those inlet but leave the polymer templates there they they will be still supporting in the next lds release as well but after that we will drop the support so for the next lts we will most likely also create some kind of a conversion tool that can do the conversion for 3dl template changes or trivial templates that you have but if you have some javascript magic or something wired into the template the tooling probably cannot do the conversion so if you want to convert those in the future for lit then there is some manual task that you need to be doing another big change we did lately was for theming so this this was a major renewable for the theming mechanism invading so it's easier to use in many ways there is a nice blog post about that by rolf smith in in in modern.com and most essentially you can now really really easily share the feed that you have built so if you have for example multiple applications in your company and you all you want all those applications to look the same you can create one project kind of a design system project where you maybe have some test classes but the main thing is that you have the css images and all those resources and then you package that as one jar file and then you can add a dependency to all your different body applications and then i think it's like one annotation and then then they all look basically the same improved lazy data pointing i was actually planning to show a demo about this but actually simon kind of stole my show here so i think i will skip that demo to not get you bored so even even though the bottom floor team actually usually works only on the kind of back-end things and and and the lower level details and so on we we do help our components team as well so we removed all the all the data binding apis that are used by by combobox and crit to lazily load data from the back end and as you saw it's it's it's really quite easy actually these days to create this memory efficient data binding it's very essential if you have uh really large data sets or a lot of people but if you have a simple applications with a fairly low amount of end users and you don't have like millions of rows in your database i don't actually such a still to use the lazy data binding but just paste the arraylist directly into the grid and then profile your applications if you have issues with the server-side memory then use this improved lazy data binding to optimize your application usually you don't have the problem but if you have then then do the change uh osti yeah uh there is actually fairly large amount of ostia users involving community we it suits well for for typical orc applications they they are usually heavy java developers who are building these kind of uh dynamic applications so what it has gained quite quite nice foothold on that that area uh the support in in latest versions in in lds in current lds is a bit limited though so for example if you were using this npm mode which became the default in volume 14. oscil was not supported now in body 19 we have like basic support for osti and we also have some examples how to get started uh i will saw an example that is using karate and that's the most that's the more complex one of our ost examples let me escape the presentation i think i have there so if you go to body.com example you will see this you can check it out and you can build it i actually already did it so i don't need to load all the dependencies here so it actually contains couple of sub projects because ost applications typically are very modular so in this we have uh the main ui this is this is the main bottom application or main body bundle in this this example then we have a creed service which is declared in this api module and then we have an actual implementation for that this is likely really dummy service service layer so it's just there for for for demonstration then then we have uh uh actually the spot in designer view is from a different brands that i forgot to delete but we have a one other like additional ui model which is helpful so this kind of promotes an additional view to the main ui without the main ui knowing nothing about this additional ui so let's start the car off so you need to use java 11 and now i'm starting the actual curve server and then i will also cheat because i don't remember the comments from the bottom of my heart so here is actually a nice script that that kind of shows how to run this demo through fully so first off we will need to install the http whiteboard specification so korov runs the web server usually in localhost 81.81 but if i try to load that now there is nothing that responds so only you get this offline page which is visible currently because there is no server running so let's start or let's install the basic basic html http white for whiteboard server and then we will need to i'm getting mixed with all these tabs here and then we will install a color feature for the for the main ui so in body 19 we actually added support for so-called cora features for body as well so if you want to add the volume dependencies uh you that there is a button graph declaration file this this xml file that declares that what are the volume dependencies and those are automatically like loaded by graph and now this this one is a feature file for the main ui and i can install that and it took that now from the local maven repository and then if i install the main ui now we should have the basic basic volume application skeleton running so now we have this kind of application which is which has this main menu structure the same same as we have in the startup volume.com created projects and then we have a simple ui there and if i click the button it actually says that there is no create service available so so the so the ui has kind of a or the click listener checks that okay we don't have the service and then it falls back to this and uh let me for some reason the main menu is not available there okay now it's there well okay there it is so let's add this create service then the actual functionality of the application changes so now i installed this bare-bones ost bundle for the create service and then i will start it and now you can actually see that there is an optional view that appeared here and if i click the button which says say hello now there is actually a different message that is shown here and if i go to the optional view this this was already available in the main ui bundle but it was not activated because there was no discrete service available but now that the grid service became active also the actual ui changed and then let's still try to start this help view same here so this is like a bare bones ostia bundle that contains body views and now if i start it it registers itself into the body main ui that we have here and now we have the help view here as well and naturally i can also like remove this so i can actually go bundle stop and help you and now if i open the application it's it's disappeared here and all this happens without stopping and starting the actual jbm so pretty pretty kind of natural things for osd developers but but those who haven't been for working with this kind of java applications it might seem quite cool that you can actually modify or configure the application runtime and and upgrade some views so you so if you want to have your application constantly on ostia is one one way to achieve that that you can update your application view by view and then the users can continue and work working on the other views while those are updated there is also another osti example which is based on enroute that's kind of a hello world style application and probably the one that you want to use if you want to just like start using body in in some existing osti project then cradle cradle cradle uh there is actually quite a large amount of well maven is the vast majority of java users who use maven for their applications but there is a big part of our customers who use cradle for reasons and we want to support of course those customers as well let me escape the presentation again and go to github here so we have actually two example projects these days so we have a one with uh plain charlotte built and then we have uh this is the spring one and then then there is one for uh servlet one and it seems that we have a security vulnerability there yeah yeah this is actually bought in seven but important brands so we haven't updated that lately so cradle has been supported before as well and uh and but it has been only like a community built project so now with latest versions we we actually whenever we cut a new body platform build we actually execute tests against the the cradle plug-in as well and also the plug-in is actually using exactly the same source code as maven does so there was a really nice community contribution that split all these shareable parts of the maven plugin in this different module and now we have two build plugins maven and cradle which use the same code base and this way it will be much more harder for us to break the compilability with with the cradle builds in in the future so uh yeah let's sew some cradle from the command line so i think i have there i have a cradle project so let me show it from the finder so so there is a cradle build file and uh all the typical front-end files here and if i run yeah let's use java 11 here as well and then cradle and use app run so this is the non-spring boot version so this is using create the server now so now it's doing the front and build compiles java sources and so on and oh and the default effect do i have something running in 8080 apparently yeah well let's do kill all java and try again all right now it looks more promising so now it started the front end compilation so we should be able to go to localhost 8080 now and yeah we actually have to buy an application here so this is this is now built by cradle and there is a jetty server that has been started up by cradle and the volume application is deployed there pretty much all the same features or exactly the same features are supported by both cradle and maven so if if you have been using vaudin on made on people you can see pretty much the same goals on the on the cradle side right then [Applause] what are we working on currently so all these have actually been released already so these features but these are not backboarded yet to buy in 14 series and uh i think the lazy data binding is the only one that we are currently considering on backboarding but also platforming that takes a lot of effort so i'm not 100 sure if we ever have time to do the backboarding the next lds is rather near in the future so so it might be that it will only be important to mp3 when when you get that feature for lps and all ci support and cradle those we have kind of agreed before that they will not be backboarded but about cradle plug-in still we also know publish the body-important version of the cradle plug-in but we don't have the integrated bests so in case you are using bottom 14 and cradle and there is an issue just let us know as soon as possible and let's fix that so we are not planning to break the build with the cradle build but that might happen accidentally currently so we are kind of reactive on the body and forth inside on cradle support at this point but yeah what are we working on now so these are three projects that are somewhat like work in progress uh spring security enhancements those some of this has actually been pushed out in body 20 already but for the flow side we are making some major enhancements now still for for spring security so in bonnie 21 you will get a way to kind of annotate the roles that you want to be supportive by or or roles that you want to allow access or well you can declare that which roles are allowed to come to that specific view in your applications and uh there is already inviting 20 there is some very very nice helpers for spring security in a way that you don't know need to kind of know all the internal details about wanting that which actual urls needs to be secured by screen security and which are kind of public urls for example which are containing the client-side engine part of audi and those needs to be allowed to be downloaded by all all users not just those who are already authenticated so that gets much more easier and safer for you because we are taking care of that and i'm not you and the less security related code you have in your applications the better uh yeah the spring boot killer by red hat there are also customers who are using quarkus already like there are people a lot of you are probably using spring security already today but with quarkus we are doing basically the same thing as we did with cradle so we will be creating integration tests and uh and then we will be creating an add-on or extension in in quarkus lingo that allows you to run body easier in quercus you can do that manually today already or you can use the excellent community add-on uh you can find that from github using urasporo account and uh i i think that's a swiss swiss customer of ours who has developed that but uh basically we will be doing kind of an official solution for for that kind of integration and we will be supporting cdi as well at least at some point we are in a phase that we might actually get something still quizzed into body 21 as well but if we miss the deadline then it will be invading 22 in the autumn then on portal side there there is also kind of the same kind of situation as with osgi so we we had in the past very strong foothold on the liferay community but now now with bottom 14 we don't have anything official yet for life ray there is a proof of concept that we did in the autumn or actually it was one of our or one of our friends in turku another company who we outsourced this proof of concept and it turned out to be fairly easy to build it on top of the existing portlet support that we did earlier for another customer for different portal so so that is possible today to use it and in in little like during the summer or or in the autumn i think that we will be making making progress with the ostia support of library as well we are currently negotiating with one of our customers for sponsoring that feature so so i i hope that we also get kind of a decent library support for body before the next lds is out there are lots of like buying customers who currently have their body eight portlets running and they are happy with those but now when the kind of free support is ending they they attempted to convert into the latest versions of live and then what else so what else these are things that i would like floating to look into as soon as possible but we haven't yet started this uh front-end build optimization is kind of part of the maintenance thing but but it's such a big thing that i i raised it here as one one topic so currently if you use part in 19 or 1 120 which is the latest version it the front end build is slightly slower than in volume 14 or or not not just slightly but i i think it takes like four seconds longer than than than it did with vaudin 14 or does with 114. so we want to cut that down back back to the level that it was inviting 14 or even better we don't know yet exactly what to do there but but that's that's part of the that's the r part of the r d then during my whole stay as a product owner in body i have actually had this data entry improvements on the task list so so in the same way as we improved the lazy data binding features where you bind a lot of rows to create for example another important area of volume is the binding data from the entity level to your forms where you can edit those and that has been rather still that apis in spotting eight but there are a lot of things that we would like to work there so better cross-field validation and and then also providing some kind of a base class for forms because currently you will need to do quite a lot of boilerplate code when you want to create the kind of the best possible ux so so our ui designers who are working for the project they for example often want to save button to actually change when you actually modify the form then it becomes active and and the same for the cancel button and so on if you want to do this all this in your body and application you will need to write this all manually but hopefully in the future we have a like a base class for for forms that you can use if you just want a basic form and then all these kind of small little details and enhancements would be coming right from the abstract base class for you other than that the autumn will be probably quite full of maintenance because we are closing to the next lds so the current plan is now that volume 23 is going to be the next lds version and we have a like a stabilization period there so we will have like a partial feature freeze involving 21. uh what invited you to and uh or is it yeah yeah like after morning 21 so that that then we will people basically just like i ring out the last box that we know and and improving the existing features and documentation but actually for for the next lds there will be lots of new features as well so in the same way as we have done now with the volume 14 series there will be minor releases and uh and new visitors will be introduced in those and uh how much do we have time we are a bit uh actually over the time so we can start to wrap up that'll be great yeah then i'm basically done with the presentation uh if we have any questions then let's start to tackle those here's one at least um will the starter apps generate from your website support gradle soon or should i just grab that get started project map to show it yeah go go to the github so the starter startupbody.com that that is the kind of the primary way that we have in body.com that's kind of an opinionated way to start one application it it has a springboard which is not required by vaadin but it's the one that we suggest for you and it has maven which is not required to build one in application but it's the one that we suggest for you and i think it has also relational database which is not required with volume but we suggest for you so so it's it's like a default stack if you don't know how to how to compile your own framework around voting then you will take that and then if you know that you use cradle or if you know that you use osti and if you know that you are not using relational database but uh you will use neo4j on the backend layer then go to spring initializer configure your project there download it you can you can pick cradle there actually by default then then you can go to the github repository where we have the cradle build example and then copy the relevant volume parts to your application and then you have exactly the stack that you want to have in your application but if you are unsure the stack that we keep from startupboarding.com that's a very good pages for your next application here's another one if we can tackle this real real quick uh what are the plans for that infusion i was very surprised and worried about the question in the survey asking if i'd be sad if it was to go away will it become a paid feature no it's not coming a paid feature it is it is there and we have two frameworks now so we have flow which is the majority which the majority of our community is using today and then we have the new flow the new fusion framework and uh it's there and it will be there and uh it will be in the next lds as well as and then it will get kind of a similar support guarantee that we currently have for the bonding flow for example you can use buckfix priority in the future or you can do that already yeah yeah you can actually do that already yeah yeah but but then then you have the five years long term support in in that so so currently if you are using a fusion you will typically need to do some minor changes in your application every every quarter when we push out any new new major version of body yeah a really really quick update on fusion roadmap right now the team is working on spring security integration and also stateless authentication using jwt probably but not certain the next thing on the roadmap would be server push support all right just to to wrap it up we have we had another question i think maybe this uh came during life so i can make sure if i want to add a javascript file that implements a monitoring agent for app dynamics it should be loaded early and still loaded in the application you is your shown away applicable for this too yeah we can tackle that from there um the shown way that i showed basically using at js module means that the script is included and run in the main bundle which means that it will actually happen only after after downloading the main bundle which is around 300 kilobytes big if you want to get it before that then you need to add an bootstrap listener or actually you can just put a custom index html file nowadays that actually has a old-fashioned script tagged to load that specific thing first before anything else all right perfect i think we are a bit over time more than more than 10 minutes hour but i think it was worth uh having a conversation like this to spend as much time as possible on your talks i want to thank you um you simon leif mati for for your presentations um they were very informative and uh i'm pretty sure uh the audience also liked the presentation at least i saw a lot of comments in the uh chat uh that's great um so yeah once again thanks for for joining us in this uh event once more online and we'll see uh probably during the autumn um when we are going to have another van dev day uh i guess it's gonna be focused in fusion so if you're interested in that uh framework as mati mentioned we now have two frameworks that's going to be a very interesting event for you so thank you everybody and i hope to see you in the next event bye bye bye thank you bye-bye
Info
Channel: vaadinofficial
Views: 3,311
Rating: 5 out of 5
Keywords:
Id: h0rGWGJ-dQI
Channel Id: undefined
Length: 162min 25sec (9745 seconds)
Published: Thu Jun 17 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.