Changelog History
-
v0.8 Changes
December 03, 2020๐ฅ breaking changes
๐ This release contains changes that break code written with earlier versions. Hopefully these are the last major api-changes prior to fritz2 1.0:
Setting attributes per function
In fritz2 0.8 we decided to use functions to set attribute values instead of vars with delegation.
๐ That way you do not have to wrap constant values in aFlow
anymore. This yields better performance and theconst()
-function could be removed. For convenience reasons we also added a new functionasString
forFlow
s to
convert aFlow
to aFlow<String>
by calling thetoString()
method internally.input { type("text") // native value(myStore.data) // flow name(otherStore.data.asString()) // otherStore.data is not a Flow of String}
RenderContext
replacesHtmlElements
๐ We renamed the
HtmlElements
interface toRenderContext
, because we think this name better fits the Kotlin DSL approach.
The idea behind it is that everyrender
function creates a newRenderContext
in which
๐ newTag
s can be created. This also means that you must replace the receiver type in your custom component-functions accordingly:val errorStore = storeOf("some text")// own componentfun RenderContext.errorText(text: String): P { return p("error") { +text } } errorStore.data.render { //this: RenderContext errorText(it) }
โ Adding Text and Comments
We clarified the creation of TextNodes in
Tag
s. Now you use unary+
-operator for constantString
s
to append text at this position to yourTag
. If you have aFlow
, callasText()
instead.
To create a CommentNode, you can use the!
-operator andasComment()
analogous. This intentionally follows a different approach in contrast to the attribute functions so it can be distinguished more easily.p { +"Hello " myStore.data.asText() !"this is a comment" myStore.data.asComment() }
Evolution of
render()
andrenderEach()
๐ Using former fritz2-versions you mapped a
Flow
of data to aFlow
ofTag
s and created aMountPoint
explicitly by callingbind()
at some place in your rendering. This was error prone. Since nobody would do anything with aFlow<Tag>
other than binding it, allrender
functions now implicitly create the mount point and therefore nobind()
is necessary anymore. It has been removed completely.val myStore = storeOf(listOf("a","b","c")) render { ul { myStore.data.renderEach { li { +it } } // no .bind() here anymore } }
๐ For performance reasons the render-functions prior to version 0.8 did not allow more than one root-element. In version 0.8 the standard
render
allows you to add as many root elements to your context as you want or even none:val myStore = storeOf(42)// renders multiple root-elementsmyStore.data.render { repeat(it) { div { +"one more" } } }// does only render something if value is large enoughmyStore.data.render { if (it \> 100) { div { +"number" } } }
๐ If you you do not need this feature (because you know you will always have exactly one root-element) use
renderElement()
instead to get (slightly) improved performance.๐
render()
andrenderElement()
now reserve their place in the DOM until the content is rendered by using a temporary placeholder. Since this costs some performance you can disable it when you are sure that there are no sibling-elements on the same level in your DOM-tree by settingrenderElement(preserveOrder = false)
. Use this when you have to render lots of elements (in huge lists, tables, etc.).Instead of
someListFlow.each().render {...}.bind()
you now simply writesomeListFlow.renderEach {...}
. This is analog for all flavors ofrenderEach
onStore
s andFlow
s with and without anidProvider
.
Please note thatrenderEach()
still allows only one root-element (likerenderElement
)!Tracker offers
Flow<Boolean>
๐
Tracker
now implementsFlow<Boolean>
instead ofFlow<String?>
so it adopts better to most use-cases. Find an example here.๐ new features
๐ improvements
- โก๏ธ update all dependencies to latest version PR#166
- extend Router functionality PR#197
- โฌ๏ธ upgraded Dokka-version and moved to html for api-docs PR#194
- annotation processor visibility option PR#178
- โ use local test server PR#165
๐ fixed bugs
-
v0.7.2 Changes
October 15, 2020Small patch resolving a memory issue related to coroutine scopes.
-
v0.7.1 Changes
September 06, 2020๐ Just a small patch to be compatible with Kotlin 1.4.0.
๐ No new, features or bug fixes included.
-
v0.7 Changes
August 13, 2020๐ฅ breaking changes
๐ This release contains changes that break code written with earlier versions:
- ๐
Handler
s are now suspendable, so you can call suspend-methods directly inside yourHandler
. There is no need forApplicator
anymore. Therefore this class and it's utility-functions have been removed. (PR#124 & PR#126) ๐ฆ
FormateStore
and interfaceFormat
have been removed. Useformat
-factory-function insidelenses
package to create a formattingLens
and create a normalSubStore
(by usingsub
). (PR#139 & PR#146)val df: DateFormat = DateFormat("yyyy-MM-dd")// converts a Date into String in vice versaval dateFormat = format( parse = { df.parseDate(it) }, format = { df.format(it) } )//using the dateLensval birthday = personStore.sub(L.Person.birthday + dateFormat)// orval birthday = personStore.sub(L.Person.birthday).sub(dateFormat)
๐จ Validation has been extracted as a service and refactored to be more concise. (PR#149 & #157)
in
commonMain
data class Message(val id: String, val status: Status, val text: String) : ValidationMessage { override fun isError(): Boolean = status \> Status.Valid // renamed from failed() -\> isError()}object PersonValidator : Validator\<Person, Message, String\>() { // return your validation messages hereoverride fun validate(data: Person, metadata: String): List\<Message\> { ... } }
in
jsMain
val personStore = object : RootStore\<Person\>(Person()) { // only update when it's validval addOrUpdate = handle\<Person\> { oldPerson, newPerson -\>if (PersonValidator.isValid(newPerson, "update")) new else oldPerson } } ...// then render the validation message list in your htmlPersonValidator.msgs.render { msg -\> ... }.bind()
in
jvmMain
if (PersonValidator.isValid(newPerson , "add")) { //e.g. save your new Person to Database ... } else { // get the messages, only available after isValid() was calledval msgs = PersonValidator.msgs ... }
๐ new features
- โ added tracking-service to access process state of
Handler
s (e.g. to show process indicator). (PR#147) - โ added history-service to keep track of historical values in
Store
s and provideback()
function. (PR#152) - โ added
Repository
to offer CRUD-functionality for entities and dealing with queries. Implementations are available for REST and LocalStorage (see example). (PR#141, PR#144, PR#155 & PR#153) - โ added
storeOf()
function to create a minimalRootStore
(withoutHandler
s) (PR#144) - โ added convenience-funtion
render
onSeq
, so you can directly writeeach(...).render { ... }
(and leave outmap
) (PR#142) - โ added convenience-funtion
render
onFlow
, so you can directly writeflow.render { ... }
(and leave outmap
) (PR#154) - โ added functions to deal with errors in
Handler
s (PR#137) - snapshots are now provided on oss.jfrog.org (PR#128)
- โ added
append
function to remote (PR#127) - ๐ changed
IdProvider
to generic type (PR#123) - โ
use
Inspector
(created byinspect()
-function) to navigate through your model in validation and test and have data and corresponding ids available at any point (PR#118)
๐ fixed bugs
- ๐
-
v0.6 Changes
July 13, 2020๐ฅ breaking changes
๐ This release contains changes that break code written with earlier versions:
๐ You no longer need to inherit
WithId
in your model-classes (the interface has been removed from fritz2 entirely). Instead, you need to provide a function which returns the id of a certain instance. This function can be used when callingeach
or creating aSubStore
for a certain element (PR#94):// in commonMain@Lensesdata class Model(val id: String, val value: String)// in jsMainval store = RootStore<List<Model>>(listOf(...)) render { ul { store.each(Model::id).map { modelStore -> render { li { modelStore.sub(L.Model.value).data.bind() } } }.bind() } }.mount("target")
All of the
each
methods (PR#113) were unified:๐ use
Flow<T>.each()
to map each instance of T to yourTag
s. It uses Kotlin's equality function to determine whether or not two elements are the same, and therefore re-renders the whole content you mapped when an element is changed or moved.with
Flow<T>.each(idProvider: (T) -> String)
you can also map each instance of T to yourTag
s, but it uses the given idProvider to determine whether or not two elements are the same. In your mapping, you can get aSubStore
for an element usinglistStore.sub(id, idProvider)
, so only the parts that actually changed will be re-rendered.๐ use
Store<List<T>>.each()
to map aSubStore<T>
toTag
s. It uses the list position of the element to determine whether or not two elements are the same. This means that when inserting something into the middle of the list, the changed element AND ALL following elements will be re-rendered.with
Store<List<T>>.each(idProvider: (T) -> String)
you can also map aSubStore<T>
toTag
s, but it uses the given idProvider to determine whether or not two elements are the same`, so only the parts that actually changed will be re-rendered.๐ renamed
handleAndEmit
tohandleAndOffer
(PR#109)๐ renamed
ModelIdRoot
toRootModelId
to follow the naming of stores (PR#96)๐ new features
- โ add static text in HTML by
+"yourText"
(PR#95) - โ add HTML-comments by
comment("yourText")
or!"yourText"
(PR#108) - ๐ use the
action
function to dispatch an action at any point in your code (PR#117)
๐ fixed bugs
- ๐ fixed handling of
value
andchecked
attributes (PR#81) - ๐ fixed
MapRouter
to useMap<String,String>
(PR#82) - ๐ fixed double
kotlin
-block in gradle build-file (PR#97) - ensure order of children when mixing static tags with bound values on the same level by using
bind(preserveOrder = true)
(PR#102) - classes of HTML-tags are now open so you can inherit your own tags from them (PR#104)
SingleMountPoint
forBoolean
(leaving out the attribute if false) (PR#105)
-
v0.5 Changes
June 11, 2020๐ฅ breaking changes
๐ This release contains changes, that break code written with earlier versions:
- We moved all artifacts and packages to match our domain: dev.fritz2. You will have to adjust your inputs and dependencies accordingly.
- The default project-type for fritz2 now is multiplatform (to make it easy to share models and validation between client and server). Use the new fritz2-gradle-plugin to setup your project:
๐ build.gradle.kts
plugins { id("dev.fritz2.fritz2-gradle") version "0.5"}repositories { jcenter() }kotlin { kotlin { jvm() js().browser() sourceSets { val commonMain by getting { dependencies { implementation(kotlin("stdlib")) } } val jvmMain by getting { dependencies { } } val jsMain by getting { dependencies { } } } } }
๐ fixed bugs
- ๐ fixed dom-update problem with
checked
attribute atHTMLInputElement
-
v0.4 Changes
May 26, 2020๐ฅ breaking changes
๐ This release contains changes, that break code written with earlier versions:
๐ since it was the source of much confusion we renamed the function to build a tree of
Tag
s (formerlyhtml
) torender
:render { div("my-class") { // ... } }
the overloaded operator
<=
to bind aFlow
of actions or events to aHandler
was definitely not Kotlin-like, so we replaced it by thehandledBy
infix-function (please note the reversed order):button("btn btn-primary") { text("Add a dot") clicks handledBy store.addADot }
๐ new features
- ๐ improved remote-api
- ๐ support for building and using WebComponents
๐ bug fixes
- ๐ improved examples
- ๐ improved documentation
๐ build.gradle.kts
๐ Kotlin style
dependencies { implementation("io.fritz2:fritz2-core-js:0.4") }
๐ Groovy style
dependencies { implementation 'io.fritz2:fritz2-core-js:0.4'}
-
v0.3 Changes
April 22, 2020- ๐ several bug-fixes
- tidyed up syntax for text, attr, const
- ๐ better examples
- ๐ Improved diff-algorithm for list-handling
- ๐ better extractions on events (current value, selected item, etc.)
- reworked structure of GitHub-projects
-
v0.2 Changes
March 19, 2020๐ First automated release using github actions...
-
v0.1 Changes
February 28, 2020๐ Our first public release, just to test the build- and publish-process.