The example consists of a lazy loading list of persons, which are also plotted on a map. Push is used to update person locations in real time.
Run the project as
mvn
A live demo is running at https://labs.vaadin.com/flow-lit/
ListAndMapView.java is a simple template-based view; it just adds the LazyList
component to the (unnamed) slot in the template, while letting the template handle everything else. Note the annotations specifying which template to use, as well as enabling server push.
@Tag("listmap-view")
@JsModule("./listmap-view.js")
@Push
public class ListAndMapView extends Component implements HasComponents {
public ListAndMapView() {
add(new LazyList());
}
}
The associated template (listmap-view.js), has a few elements around a single slot where the lazy list will end up. The elements are laid out using CSS in the template.
render() {
return html`
<github-corner><a href="https://github.com/Artur-/flow-lit"></a></github-corner>
<div class="container">
<slot></slot>
<marker-map></marker-map>
</div>
<lazy-loading-indicator></lazy-loading-indicator>
</div>
`;
}
LazyList shows a more complex use of Lit. The server-side Java class provides lazy-loading of the Person list (with a fake delay to make the laziness visible) and pushes movements of the currently visible persons. The Java class provides the data, while the Lit template does handles the rendering.
public LazyList() {
List<Person> persons = PersonService.get().get(0, 10);
loadedInBrowser = 10;
getElement().setPropertyJson("persons", toJson(persons));
getElement().addEventListener("load-persons", e -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
}
loadedInBrowser += 10;
sendEvent(PersonService.get().get(loadedInBrowser, 10));
});
}
Meanwhile, the Lit template in lazy-list.js defines the presentation of the data and handles all interactions, requesting more data when needed (using an IntersectionObserver):
render() {
return html`
${repeat(
this.persons,
person => person.id,
(person, index) => html`
<j-card class=${classMap(person.classes ? person.classes : {})} @click="${e => this.personSelected(person)}">
<h3 slot="title"><a-avataaar identifier="${person.firstName} ${person.lastName}"></a-avataaar>${person.firstName} ${person.lastName}</h3>
<div>${person.company}</div>
<div>${person.address}</div>
<span>${person.zip} ${person.city}</div>
</j-card> `
)}
This uses the Lit repeat
directive to loop over all the available persons and render a <j-card>
element (from j-elements) for each person. The repeat
directive uses three parts:
this.persons
, which is the list of persons populated by the serverpersons => person.id
defines the identity of individual items so they can be updated later(person, index) => html`...`
defines the markup to render for each person. Here the person and index variables are related to the active person being rendered.
A click listener is added to each card using @click="${e ⇒ this.personSelected(person)}
which calls the personSelected
method with the selected person as an argument. This is used to highlight the person in the list and also to emit a person-selected
event when a person is selected so the map can highlight the selected person without going through the server.
If you are interested in more details about lazy loading, intersection observers etc, see the corresponding files in frontend (client side) and/or src/main/java (server side).
MarkerMap is another component fully defined by the Lit template in marker-map.js. It is an integration of the Leaflet map that plots the loaded persons locations on the map. It has no server side logic, instead it listens to browser events about person data being available or updated and then updates the map accordingly.