diff --git a/core/src/scalive/LiveView.scala b/core/src/scalive/LiveView.scala index 566eb5d..b59c887 100644 --- a/core/src/scalive/LiveView.scala +++ b/core/src/scalive/LiveView.scala @@ -8,6 +8,10 @@ class LiveView[Model] private ( val static: ArraySeq[String], val dynamic: ArraySeq[LiveDyn[Model]] ): + assert( + static.size == dynamic.size + 1, + s"Static size : ${static.size}, Dynamic size : ${dynamic.size}" + ) def update(model: Model): Unit = dynamic.foreach(_.update(model)) @@ -70,6 +74,8 @@ object LiveView: val (attrs, children) = el.mods.partitionMap { case Mod.StaticAttr(attr, value) => Left(List(Some(s""" ${attr.name}="$value""""))) + case Mod.DynAttr(attr, _) => + Left(List(Some(s""" ${attr.name}=""""), None, Some('"'.toString))) case Mod.Tag(el) => Right(buildStaticFragments(el)) case Mod.Text(text) => Right(List(Some(text))) case Mod.DynText[Model](_) => Right(List(None)) @@ -85,8 +91,10 @@ object LiveView: startsUpdated: Boolean = false ): Seq[LiveDyn[Model]] = val (attrs, children) = el.mods.partitionMap { - case Mod.StaticAttr(_, _) => Left(List.empty) case Mod.Text(_) => Right(List.empty) + case Mod.StaticAttr(_, _) => Left(List.empty) + case Mod.DynAttr(_, value) => + Right(List(LiveDyn.Value(value, model, startsUpdated))) case Mod.Tag(el) => Right(buildDynamic(el, model, startsUpdated)) case Mod.DynText[Model](dynText) => diff --git a/core/src/scalive/View.scala b/core/src/scalive/View.scala index a3e567f..5b026f7 100644 --- a/core/src/scalive/View.scala +++ b/core/src/scalive/View.scala @@ -26,6 +26,7 @@ object Dyn: enum Mod[T]: case StaticAttr(attr: HtmlAttr, value: String) + case DynAttr(attr: HtmlAttr, value: Dyn[T, String]) case Tag(el: HtmlElement[T]) case Text(text: String) case DynText(dynText: Dyn[T, String]) @@ -51,5 +52,7 @@ val li = HtmlTag("li") class HtmlAttr(val name: String): def :=[T](value: String): Mod.StaticAttr[T] = Mod.StaticAttr(this, value) + def :=[T](value: Dyn[T, String]): Mod.DynAttr[T] = Mod.DynAttr(this, value) val idAttr = HtmlAttr("id") +val cls = HtmlAttr("class") diff --git a/core/src/scalive/main.scala b/core/src/scalive/main.scala index 2de2482..1a4bf43 100644 --- a/core/src/scalive/main.scala +++ b/core/src/scalive/main.scala @@ -58,17 +58,18 @@ def main = println("Remove all") lv.update( - MyModel(List.empty) + MyModel(List.empty, "text-lg") ) println(lv.diff.toJsonPretty) -final case class MyModel(elems: List[NestedModel]) +final case class MyModel(elems: List[NestedModel], cls: String = "text-xs") final case class NestedModel(name: String, age: Int) object TestView extends View[MyModel]: val root: HtmlElement[MyModel] = div( idAttr := "42", + cls := model(_.cls), ul( model.splitByIndex(_.elems)(elem => li( diff --git a/core/test/src/scalive/LiveViewSpec.scala b/core/test/src/scalive/LiveViewSpec.scala index a483101..8655d2a 100644 --- a/core/test/src/scalive/LiveViewSpec.scala +++ b/core/test/src/scalive/LiveViewSpec.scala @@ -10,6 +10,7 @@ object LiveViewSpec extends TestSuite: title: String = "title value", bool: Boolean = false, nestedTitle: String = "nested title value", + cls: String = "text-sm", items: List[NestedModel] = List.empty ) final case class NestedModel(name: String, age: Int) @@ -79,6 +80,43 @@ object LiveViewSpec extends TestSuite: } } + test("Dynamic attribute") { + val lv = + LiveView( + new View[TestModel]: + val root: HtmlElement[TestModel] = + div(cls := model(_.cls)) + , + TestModel() + ) + test("init") { + assertEqualsJson( + lv.fullDiff, + Json + .Obj( + "s" -> Json + .Arr(Json.Str("
")), + "0" -> Json.Str("text-sm") + ) + ) + } + test("diff no update") { + assertEqualsJson(lv.diff, emptyDiff) + } + test("diff with update") { + lv.update(TestModel(cls = "text-md")) + assertEqualsJson( + lv.diff, + Json.Obj("0" -> Json.Str("text-md")) + ) + } + test("diff with update and no change") { + lv.update(TestModel(cls = "text-md")) + lv.update(TestModel(cls = "text-md")) + assertEqualsJson(lv.diff, emptyDiff) + } + } + test("when mod") { val lv = LiveView(