![image](https://user-images.githubusercontent.com/33346637/235085980-eb16eaa3-ec89-4293-9609-cf651a44f60e.png) Spring ViewComponent allows you to create typesafe, reusable & encapsulated server-rendered UI components. ##### Table of Contents - [What’s a ViewComponent?](#whats-a-viewcomponent) - [Render a ViewComponent](#render-a-viewcomponent) - [Nesting ViewComponents:](#nesting-viewcomponents) - [Local Development Configuration](#local-development) - [Production Configuration](#production-configuration) - [Installation](#installation) - [Technical Implementation](#technical-implementation) ## What’s a ViewComponent? ViewComponents consolidate the logic needed for a template into a single class, resulting in a cohesive object that is easy to understand. ([Source: ViewComponent for Rails](https://viewcomponent.org/)) A Spring ViewComponent is a Spring Bean that defines the context for our Template:
Java ```java @ViewComponent public class SimpleViewComponent { public record SimpleView(String helloWorld) implements ViewContext { } public SimpleView render() { return new SimpleView("Hello World"); } } ```
We define the context by creating a record that implements the ViewContext interface Next, we add the `@ViewComponent` annotation to a class and define a method that returns the `SimpleView` record.
Kotlin ```kotlin // SimpleViewComponent.kt @ViewComponent class SimpleViewComponent { fun render() = SimpleView("Hello World") data class SimpleView(val helloWorld: String) : ViewContext } ```
A ViewComponent always needs a corresponding HTML Template. We define the Template in the SimpleViewComponent.[html/jte/kte] In the same package as our ViewComponent class. We can use [Thymeleaf](https://thymeleaf.org) ````html // SimpleViewComponent.html
```` or [JTE](https://jte.gg/#5-minutes-example) ```html // SimpleViewComponent.jte @param de.tschuehly.example.jte.web.simple.SimpleViewComponent.SimpleView simpleView
${simpleView.helloWorld()}
``` or [KTE](https://jte.gg/#5-minutes-example) ```html @param simpleView: de.tschuehly.kteviewcomponentexample.web.simple.SimpleViewComponent.SimpleView

This is the SimpleViewComponent

${simpleView.helloWorld}
``` ## Render a ViewComponent We can then call the render method in our controller to render the template.
Java ```java @Controller public class SimpleController { private final SimpleViewComponent simpleViewComponent; public TestController(SimpleViewComponent simpleViewComponent) { this.simpleViewComponent = simpleViewComponent; } @GetMapping("/") ViewContext simple() { return simpleViewComponent.render(); } } ```
Kotlin ```kotlin // Router.kt @Controller class SimpleController( private val simpleViewComponent: SimpleViewComponent, ) { @GetMapping("/") fun simpleComponent() = simpleViewComponent.render() } ```
## Examples If you want to get started right away you can find examples for all possible language combinations here: [Examples](/examples/) ## Nesting ViewComponents: We can nest components by passing a ViewContext as property of our record, if we also have it as parameter of our render method we can easily create layouts:
Java ```java @ViewComponent public class LayoutViewComponent { private record LayoutView(ViewContext nestedViewComponent) implements ViewContext { } public ViewContext render(ViewContext nestedViewComponent) { return new LayoutView(nestedViewComponent); } } ```
Kotlin ```kotlin @ViewComponent class LayoutViewComponent { data class LayoutView(val nestedViewComponent: ViewContext) : ViewContext fun render(nestedViewComponent: ViewContext) = LayoutView(nestedViewComponent) } ```
### Thymeleaf In Thymeleaf we render the passed ViewComponent with the `view:component="${viewContext}"` attribute. ```html
``` ### JTE / KTE In JTE/KTE we can just call the LayoutView record directly in an expression: ```html @param layoutView: de.tschuehly.kteviewcomponentexample.web.layout.LayoutViewComponent.LayoutView ${layoutView.nestedViewComponent} ``` ## Local Development Configuration You can enable hot-reloading of the templates in development, you need to have Spring Boot DevTools as a dependency. ```properties spring.view-component.local-development=true ``` ## Installation ### Thymeleaf: [LATEST_VERSION](https://central.sonatype.com/artifact/de.tschuehly/spring-view-component-thymeleaf) on Maven Central
Gradle ```kotlin implementation("de.tschuehly:spring-view-component-thymeleaf:0.9.1") sourceSets { main { resources { srcDir("src/main/java") exclude("**/*.java") } } } ```
Maven ```xml de.tschuehly spring-view-component-thymeleaf 0.9.1 src/main/java **/*.html src/main/resources maven-resources-plugin 3.3.0 ```
### JTE [LATEST_VERSION](https://central.sonatype.com/artifact/de.tschuehly/spring-view-component-jte) on Maven Central
Gradle ```kotlin plugins { id("gg.jte.gradle") version("3.2.1") } implementation("de.tschuehly:spring-view-component-jte:0.9.1") jte{ generate() sourceDirectory.set(kotlin.io.path.Path("src/main/java")) } ```
Maven ```xml de.tschuehly spring-view-component-jte 0.9.1 gg.jte jte-maven-plugin 3.1.12 ${project.basedir}/src/main/java Html generate-sources generate ```
### KTE [LATEST_VERSION](https://central.sonatype.com/artifact/de.tschuehly/spring-view-component-kte) on Maven Central
Gradle ```kotlin plugins { id("gg.jte.gradle") version ("3.1.12") } dependencies { implementation("de.tschuehly:spring-view-component-kte:0.9.1") } jte { generate() sourceDirectory.set(kotlin.io.path.Path("src/main/kotlin")) } ```
## Technical Implementation Spring ViewComponent wraps the Spring MVC container using an AspectJ Aspect and automatically resolves the template and puts the ViewContext in the ModelAndViewContainer ![image](https://github.com/tschuehly/spring-view-component/assets/33346637/ad2f2517-7eab-4b07-9249-59aeaae1e778)