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-groovymarkup.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-feeds.adoc[]
|
||||
**** xref:web/webmvc-view/mvc-document.adoc[]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
[[webflux-view]]
|
||||
= View Rendering
|
||||
= View Technologies
|
||||
[.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
|
||||
|
|
@ -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]]
|
||||
== JSON and XML
|
||||
[.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>`
|
||||
| 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`
|
||||
| A method with a `void`, possibly asynchronous (for example, `Mono<Void>`), return type (or a `null` return
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
[[mvc-view]]
|
||||
= View Rendering
|
||||
= View Technologies
|
||||
:page-section-summary-toc: 1
|
||||
[.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>`
|
||||
| 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`
|
||||
| A method with a `void` return type (or `null` return value) is considered to have fully
|
||||
|
|
|
|||
Loading…
Reference in New Issue