Documentation updates for fragment rendering
Closes gh-33195
This commit is contained in:
parent
0bac8d48a5
commit
e670c6b50a
|
|
@ -205,6 +205,7 @@
|
||||||
**** xref:web/webmvc-view/mvc-freemarker.adoc[]
|
**** xref:web/webmvc-view/mvc-freemarker.adoc[]
|
||||||
**** xref:web/webmvc-view/mvc-groovymarkup.adoc[]
|
**** xref:web/webmvc-view/mvc-groovymarkup.adoc[]
|
||||||
**** xref:web/webmvc-view/mvc-script.adoc[]
|
**** xref:web/webmvc-view/mvc-script.adoc[]
|
||||||
|
**** xref:web/webmvc-view/mvc-fragments.adoc[]
|
||||||
**** xref:web/webmvc-view/mvc-jsp.adoc[]
|
**** xref:web/webmvc-view/mvc-jsp.adoc[]
|
||||||
**** xref:web/webmvc-view/mvc-feeds.adoc[]
|
**** xref:web/webmvc-view/mvc-feeds.adoc[]
|
||||||
**** xref:web/webmvc-view/mvc-document.adoc[]
|
**** xref:web/webmvc-view/mvc-document.adoc[]
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
[[webflux-view]]
|
[[webflux-view]]
|
||||||
= View Rendering
|
= View Technologies
|
||||||
[.small]#xref:web/webmvc-view.adoc[See equivalent in the Servlet stack]#
|
[.small]#xref:web/webmvc-view.adoc[See equivalent in the Servlet stack]#
|
||||||
|
|
||||||
The rendering of views in Spring WebFlux is pluggable. Whether you decide to
|
The rendering of views in Spring WebFlux is pluggable. Whether you decide to
|
||||||
|
|
@ -418,6 +418,81 @@ for more configuration examples.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[[webflux-view-fragments]]
|
||||||
|
== HTML Fragment
|
||||||
|
[.small]#xref:web/webmvc-view/mvc-fragments.adoc[See equivalent in the Servlet stack]#
|
||||||
|
|
||||||
|
https://htmx.org/[HTMX] and https://turbo.hotwired.dev/[Hotwire Turbo] emphasize an
|
||||||
|
HTML-over-the-wire approach where clients receive server updates in HTML rather than in JSON.
|
||||||
|
This allows the benefits of an SPA (single page app) without having to write much or even
|
||||||
|
any JavaScript. For a good overview and to learn more, please visit their respective
|
||||||
|
websites.
|
||||||
|
|
||||||
|
In Spring WebFlux, view rendering typically involves specifying one view and one model.
|
||||||
|
However, in HTML-over-the-wire a common capability is to send multiple HTML fragments that
|
||||||
|
the browser can use to update different parts of the page. For this, controller methods
|
||||||
|
can return `Collection<Fragment>`. For example:
|
||||||
|
|
||||||
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
|
----
|
||||||
|
@GetMapping
|
||||||
|
List<Fragment> handle() {
|
||||||
|
return List.of(Fragment.create("posts"), Fragment.create("comments"));
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
----
|
||||||
|
@GetMapping
|
||||||
|
fun handle(): List<Fragment> {
|
||||||
|
return listOf(Fragment.create("posts"), Fragment.create("comments"))
|
||||||
|
}
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
|
The same can be done also by returning the dedicated type `FragmentsRendering`:
|
||||||
|
|
||||||
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
|
----
|
||||||
|
@GetMapping
|
||||||
|
FragmentsRendering handle() {
|
||||||
|
return FragmentsRendering.with("posts").fragment("comments").build();
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
----
|
||||||
|
@GetMapping
|
||||||
|
fun handle(): FragmentsRendering {
|
||||||
|
return FragmentsRendering.with("posts").fragment("comments").build()
|
||||||
|
}
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
|
Each fragment can have an independent model, and that model inherits attributes from the
|
||||||
|
shared model for the request.
|
||||||
|
|
||||||
|
HTMX and Hotwire Turbo support streaming updates over SSE (server-sent events).
|
||||||
|
A controller can create `FragmentsRendering` with a `Flux<Fragment>`, or with any other
|
||||||
|
reactive producer adaptable to a Reactive Streams `Publisher` via `ReactiveAdapterRegistry`.
|
||||||
|
It is also possible to return `Flux<Fragment>` directly without the `FragmentsRendering`
|
||||||
|
wrapper.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[[webflux-view-httpmessagewriter]]
|
[[webflux-view-httpmessagewriter]]
|
||||||
== JSON and XML
|
== JSON and XML
|
||||||
[.small]#xref:web/webmvc-view/mvc-jackson.adoc[See equivalent in the Servlet stack]#
|
[.small]#xref:web/webmvc-view/mvc-jackson.adoc[See equivalent in the Servlet stack]#
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ Controllers can then return a `Flux<List<B>>`; Reactor provides a dedicated oper
|
||||||
|
|
||||||
| `FragmentsRendering`, `Flux<Fragment>`, `Collection<Fragment>`
|
| `FragmentsRendering`, `Flux<Fragment>`, `Collection<Fragment>`
|
||||||
| For rendering one or more fragments each with its own view and model.
|
| For rendering one or more fragments each with its own view and model.
|
||||||
|
See xref:web/webflux-view.adoc#webflux-view-fragments[HTML Fragments] for more details.
|
||||||
|
|
||||||
| `void`
|
| `void`
|
||||||
| A method with a `void`, possibly asynchronous (for example, `Mono<Void>`), return type (or a `null` return
|
| A method with a `void`, possibly asynchronous (for example, `Mono<Void>`), return type (or a `null` return
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
[[mvc-view]]
|
[[mvc-view]]
|
||||||
= View Rendering
|
= View Technologies
|
||||||
:page-section-summary-toc: 1
|
:page-section-summary-toc: 1
|
||||||
[.small]#xref:web/webflux-view.adoc[See equivalent in the Reactive stack]#
|
[.small]#xref:web/webflux-view.adoc[See equivalent in the Reactive stack]#
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
[[mvc-view-fragments]]
|
||||||
|
= HTML Fragments
|
||||||
|
:page-section-summary-toc: 1
|
||||||
|
|
||||||
|
[.small]#xref:web/webflux-view.adoc#webflux-view-fragments[See equivalent in the Reactive stack]#
|
||||||
|
|
||||||
|
https://htmx.org/[HTMX] and https://turbo.hotwired.dev/[Hotwire Turbo] emphasize an
|
||||||
|
HTML-over-the-wire approach where clients receive server updates in HTML rather than in JSON.
|
||||||
|
This allows the benefits of an SPA (single page app) without having to write much or even
|
||||||
|
any JavaScript. For a good overview and to learn more, please visit their respective
|
||||||
|
websites.
|
||||||
|
|
||||||
|
In Spring MVC, view rendering typically involves specifying one view and one model.
|
||||||
|
However, in HTML-over-the-wire a common capability is to send multiple HTML fragments that
|
||||||
|
the browser can use to update different parts of the page. For this, controller methods
|
||||||
|
can return `Collection<ModelAndView>`. For example:
|
||||||
|
|
||||||
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
|
----
|
||||||
|
@GetMapping
|
||||||
|
List<ModelAndView> handle() {
|
||||||
|
return List.of(new ModelAndView("posts"), new ModelAndView("comments"));
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
----
|
||||||
|
@GetMapping
|
||||||
|
fun handle(): List<ModelAndView> {
|
||||||
|
return listOf(ModelAndView("posts"), ModelAndView("comments"))
|
||||||
|
}
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
|
The same can be done also by returning the dedicated type `FragmentsRendering`:
|
||||||
|
|
||||||
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
|
----
|
||||||
|
@GetMapping
|
||||||
|
FragmentsRendering handle() {
|
||||||
|
return FragmentsRendering.with("posts").fragment("comments").build();
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
----
|
||||||
|
@GetMapping
|
||||||
|
fun handle(): FragmentsRendering {
|
||||||
|
return FragmentsRendering.with("posts").fragment("comments").build()
|
||||||
|
}
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
|
Each fragment can have an independent model, and that model inherits attributes from the
|
||||||
|
shared model for the request.
|
||||||
|
|
||||||
|
HTMX and Hotwire Turbo support streaming updates over SSE (server-sent events).
|
||||||
|
A controller can use `SseEmitter` to send `ModelAndView` to render a fragment per event:
|
||||||
|
|
||||||
|
[tabs]
|
||||||
|
======
|
||||||
|
Java::
|
||||||
|
+
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||||
|
----
|
||||||
|
@GetMapping
|
||||||
|
SseEmitter handle() {
|
||||||
|
SseEmitter emitter = new SseEmitter();
|
||||||
|
startWorkerThread(() -> {
|
||||||
|
try {
|
||||||
|
emitter.send(SseEmitter.event().data(new ModelAndView("posts")));
|
||||||
|
emitter.send(SseEmitter.event().data(new ModelAndView("comments")));
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
// Cancel sending
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return emitter;
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
Kotlin::
|
||||||
|
+
|
||||||
|
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||||
|
----
|
||||||
|
@GetMapping
|
||||||
|
fun handle(): SseEmitter {
|
||||||
|
val emitter = SseEmitter()
|
||||||
|
startWorkerThread{
|
||||||
|
try {
|
||||||
|
emitter.send(SseEmitter.event().data(ModelAndView("posts")))
|
||||||
|
emitter.send(SseEmitter.event().data(ModelAndView("comments")))
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
catch (ex: IOException) {
|
||||||
|
// Cancel sending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return emitter
|
||||||
|
}
|
||||||
|
----
|
||||||
|
======
|
||||||
|
|
||||||
|
The same can also be done by returning `Flux<ModelAndView>`, or any other type adaptable
|
||||||
|
to a Reactive Streams `Publisher` through the `ReactiveAdapterRegistry`.
|
||||||
|
|
@ -58,6 +58,7 @@ supported for all return values.
|
||||||
|
|
||||||
| `FragmentsRendering`, `Collection<ModelAndView>`
|
| `FragmentsRendering`, `Collection<ModelAndView>`
|
||||||
| For rendering one or more fragments each with its own view and model.
|
| For rendering one or more fragments each with its own view and model.
|
||||||
|
See xref:web/webmvc-view/mvc-fragments.adoc[HTML Fragments] for more details.
|
||||||
|
|
||||||
| `void`
|
| `void`
|
||||||
| A method with a `void` return type (or `null` return value) is considered to have fully
|
| A method with a `void` return type (or `null` return value) is considered to have fully
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue