Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions articles/flow/advanced/downloads.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,21 @@
* Generating dynamic content
* Streaming large files

==== Overriding the File Name [since:com.vaadin:vaadin@V25.2]

Check failure on line 69 in articles/flow/advanced/downloads.adoc

View workflow job for this annotation

GitHub Actions / lint

[vale] reported by reviewdog 🐶 [Vale.Terms] Use '(?-i)Vaadin' instead of 'vaadin'. Raw Output: {"message": "[Vale.Terms] Use '(?-i)Vaadin' instead of 'vaadin'.", "location": {"path": "articles/flow/advanced/downloads.adoc", "range": {"start": {"line": 69, "column": 42}}}, "severity": "ERROR"}

By default, the file name of the download comes from the [classname]`DownloadResponse` returned by the callback, and the download URL is a random-looking string. The [methodname]`fromInputStream` overload that takes a `fileNameOverride` parameter lets you define the file name already when the handler is created:

[source,java]
----
Anchor downloadLink = new Anchor(DownloadHandler.fromInputStream(event -> {
byte[] report = reportService.generateReport();
return new DownloadResponse(new ByteArrayInputStream(report),
event.getFileName(), "application/pdf", report.length);
}, "monthly-report.pdf"), "Download Monthly Report");
----

The given file name is appended to the download URL as a postfix (see <<#url-postfix,URL Postfix>>), and [methodname]`getFileName()` of the [classname]`DownloadEvent` returns it inside the callback, as shown above. This is useful when serving generated content under a stable, user-friendly file name, without writing a custom download handler. An overload that additionally accepts a [classname]`TransferProgressListener` is also available.

=== Render Or Download Static Resource

The [methodname]`forClassResource` and [methodname]`forServletResource` methods allows you to serve resources from the classpath or servlet context.
Expand Down Expand Up @@ -394,6 +409,7 @@

[classname]`DownloadHandler` allows to override this mode by overriding the [methodname]`getDisabledUpdateMode()` method.

[#url-postfix]
=== URL Postfix

The [methodname]`getUrlPostfix()` method allows you to specify an optional URL postfix that appends application-controlled string, e.g. the logical name of the target file, to the end of the otherwise random-looking download URL.
Expand Down
66 changes: 66 additions & 0 deletions articles/flow/binding-data/data-provider.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,72 @@
----


== Hierarchical Data [since:com.vaadin:vaadin@V25.2]

Check failure on line 566 in articles/flow/binding-data/data-provider.adoc

View workflow job for this annotation

GitHub Actions / lint

[vale] reported by reviewdog 🐶 [Vale.Terms] Use '(?-i)Vaadin' instead of 'vaadin'. Raw Output: {"message": "[Vale.Terms] Use '(?-i)Vaadin' instead of 'vaadin'.", "location": {"path": "articles/flow/binding-data/data-provider.adoc", "range": {"start": {"line": 566, "column": 40}}}, "severity": "ERROR"}

Check failure on line 566 in articles/flow/binding-data/data-provider.adoc

View workflow job for this annotation

GitHub Actions / lint

[vale] reported by reviewdog 🐶 [Vale.Terms] Use '(?-i)Vaadin' instead of 'vaadin'. Raw Output: {"message": "[Vale.Terms] Use '(?-i)Vaadin' instead of 'vaadin'.", "location": {"path": "articles/flow/binding-data/data-provider.adoc", "range": {"start": {"line": 566, "column": 33}}}, "severity": "ERROR"}

Check warning on line 566 in articles/flow/binding-data/data-provider.adoc

View workflow job for this annotation

GitHub Actions / lint

[vale] reported by reviewdog 🐶 [Vaadin.HeadingCase] 'Hierarchical Data [since:com.vaadin:vaadin@V25.2]' should be in title case. Raw Output: {"message": "[Vaadin.HeadingCase] 'Hierarchical Data [since:com.vaadin:vaadin@V25.2]' should be in title case.", "location": {"path": "articles/flow/binding-data/data-provider.adoc", "range": {"start": {"line": 566, "column": 4}}}, "severity": "WARNING"}

Components such as Tree Grid display hierarchical data, where each item can have child items. The [interfacename]`HierarchicalData` interface in the `com.vaadin.flow.data.provider.hierarchy` package represents such data. It defines three methods:

* [methodname]`getChildren(T item)` returns the immediate child items of the given item, or the root items when the given item is `null`.
* [methodname]`getParent(T item)` returns the parent of the given item, or `null` for root items.
* [methodname]`contains(T item)` tells whether the given item is part of the hierarchy.

The built-in [classname]`TreeData` class implements [interfacename]`HierarchicalData` and is a good choice when you need a ready-made, modifiable in-memory tree structure. See <<{articles}/components/tree-grid/data-binding#,Binding Data to Tree Grid>> for details.

When your domain model is already hierarchical, you can implement [interfacename]`HierarchicalData` directly instead of copying the items into a [classname]`TreeData` instance. For example, consider a `Department` type whose items reference their parent department:

[source,java]
----
public class DepartmentData implements HierarchicalData<Department> {

private final List<Department> departments;

public DepartmentData(List<Department> departments) {
this.departments = departments;
}

@Override
public List<Department> getChildren(Department item) {
// A null item means the root of the hierarchy
return departments.stream()
.filter(department -> Objects
.equals(department.getParent(), item))
.toList();
}

@Override
public Department getParent(Department item) {
return item.getParent();
}

@Override
public boolean contains(Department item) {
// Must return true for null, which represents the root
return item == null || departments.contains(item);
}
}
----

The [classname]`InMemoryHierarchicalDataProvider` class turns any [interfacename]`HierarchicalData` implementation into a hierarchical data provider with support for in-memory sorting and filtering, so there is no need to implement a data provider from scratch:

[source,java]
----
TreeGrid<Department> treeGrid = new TreeGrid<>();
treeGrid.addHierarchyColumn(Department::getName).setHeader("Department");

InMemoryHierarchicalDataProvider<Department, DepartmentData> dataProvider =
new InMemoryHierarchicalDataProvider<>(
new DepartmentData(departmentService.findAll()));
treeGrid.setDataProvider(dataProvider);

// Show only active departments
dataProvider.setFilter(Department::isActive);

// Sort sibling items by name
dataProvider.setSortOrder(Department::getName, SortDirection.ASCENDING);
----

The data provider doesn't track changes in the backing data structure: call [methodname]`refreshAll()` after modifying the underlying [interfacename]`HierarchicalData` instance. The built-in [classname]`TreeDataProvider` is an [classname]`InMemoryHierarchicalDataProvider` backed by a [classname]`TreeData` instance.


== Recycling Data Binding Logic

In large applications, you typically have multiple places where you display the same data type in a listing component. You can use various approaches to share the lazy data binding logic.
Expand Down
Loading