
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.tschuehlyspring-view-component-thymeleaf0.9.1src/main/java**/*.htmlsrc/main/resourcesmaven-resources-plugin3.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.tschuehlyspring-view-component-jte0.9.1gg.jtejte-maven-plugin3.1.12${project.basedir}/src/main/javaHtmlgenerate-sourcesgenerate
```
### 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
