Lenses
A Lens
is basically a way to describe the relation between an outer and inner entity in a structure. It focuses on the inner entity from the viewpoint of the outer entity, which is how it got its name. Lenses are especially useful when using immutable data-types like fritz2 does. A Lens
needs to handle the following:
- Getting the value of the inner entity from a given instance of the outer entity
- Creating a new instance of the outer entity (immutable!) as a copy of a given one with a different value only for the inner entity
In fritz2, a Lens
is defined by the following interface:
interface Lens<P,T> {
fun get(parent: P): T
fun set(parent: P, value: T): P
fun apply(parent: P, mapper: (T) -> T): P = //is already implemented
}
apply
allows you to change the current value of the inner entity within one atomic action.
You can easily use this interface by just implementing get()
and set()
. fritz2 also offers the method buildLens()
for a short-and-sweet-experience:
val nameLens = buildLens("name", { it.name }, { person, value -> person.copy(name = value) })
No magic there. The first parameter sets an id for the Lens
. When using Lens
es with SubStore
s, the id will be used to generate a valid html-id representing the path through your model. This can be used to identify your elements semantically (for automated ui-tests for example).
If you have deep nested structures or a lot of them, you may want to automate this behavior. fritz2 offers an annotation @Lenses
you can add to your data-classes in the commonMain
source-set of your multiplatform project:
@Lenses
data class Person(val name: String, val value: String)
Using an annotation-processor, fritz2 builds a L
-object per package from these annotations which contains all the Lens
es you need. They are named exactly like the entities and properties, so it’s easy to use:
val nameLens = L.Person.name
You can see it in action at our nestedmodel-example.
Keep in mind that your annotated classes have to be in your commonMain
source-set. If you need to use the generated objects in your commonMain
(e.g., to implement a Validator
you can use on client and server), you have to set up a subproject for your model.
Have a look at the validation-example to see how to set it up.
This will also help you define a multiplatform project for sharing your model and validation code between the browser and backend.