Hi,

On 1/28/22 11:00 AM, Martin Terra wrote:
pe 28. tammik. 2022 klo 11.46 Matthias Metzger (noobyma...@yahoo.de.invalid)
kirjoitti:

Hi,

On 1/28/22 9:18 AM, Martin Terra wrote:
pe 28. tammik. 2022 klo 10.12 Matthias Metzger
(noobyma...@yahoo.de.invalid)
kirjoitti:

Hi,

On 1/28/22 1:47 AM, Martin Terra wrote:
to 27. tammik. 2022 klo 23.00 Matthias Metzger
(noobyma...@yahoo.de.invalid)
kirjoitti:

Hi,

On 1/27/22 5:52 PM, Martin Terra wrote:
Hi!

I am curious how this would compare to a declarative ui.

You would define your ui in pojo, almost like swing, and then you
have
a
generic rendering helper/factory layer tailored to your project,
which
then
handles *all* the nitty gritty stuff.

Only when you create a new feature, would you worry about its targets
etc
which you would implement onto the rendering helper/factory layer.

There might be parts of such structure that could be reusable across
projects.

Would this overcome the need for DSL, or would you consider it DSL as
well?


Disclaimer: The following is just my opinion.

I think, something like this would be a DSL, because it's a language
specifically designed to describe UIs. Internally, there might even be
two DSLs. One for modeling and describing a UI as pojos, XML, JSON or
functions (in this specific example it's the sealed interface Html)
and
one for creating instances of these things (what you see as div {
text("Hello") }). And then there might be other DSLs for specific
frameworks.

Creating a DSL for describing UIs generally and trying to abstract
away
Wicket specifics, to then write some Wicket specific renderer again is
something I have tried in the past and I just don't see the practical
value, since it just makes the implementation much more complex. But
maybe I misunderstood you.


For us the benefit is two-three-fold. First, I'd say
all formcomponents/labels in an ui follow patterns which can be
canonized.

Second, reuse.

You can use a label on a panel and you can use the same label in a
table
to
render (or search) the same data. You can also use it to render a
column
in
a spreadsheet. Goal is to never implement a label twice.

Or you can use a command item in a menu (or in a button on a panel) to
access the same action. Goal is to never implement an action twice.

Lastly, there are efficiency ganis from centrally tested components
which
now furthermore can more easily be automatically tested as they have
standard types and standard expected outcomes.

No more "I forgot to handle that special case again" when you are
reusing
the same component type you once already solved.

If you have a new developer, they can start by using the existing
components or creating similar ones. No need to explain wicket potholes
"ah, this is a wicket trick, here you need to do this first before
doing
that, and you need to remember this....and in this special case...do
that
don't do that" instead they will pick the closest one from library and
see
all implementation requirements or hooks it has (optional and required
implementations/extensions).


I am not sure, I am following here. To clarify: You are arguing, that
the DSL I've shared should not be done like that, because it would be
better to solve the problem of a declarative, generic UI, that can be
"compiled" (in the sense of factories/adapters) down to Wicket?


Well, there might be some (cross-project) reusable paradigms and patterns
and templates, which we could call dsl.

I'm not arguing, just bringing up an alternative approach for
consideration, maybe they could co-exist as well.

Gotcha! So, to answer your question from the beginning: How does a
declarative UI compare to the approach I shared (whether we call it a
DSL or something else). I think a declarative UI would just be a
generalization of the approach I took here. They might co-exist or the
this approach could iteratively be generalized. Not sure, how good that
would work though, since especially the Ajax-Handling in Wicket is
probably difficult to abstract. Phoenix LiveView might be an inspiration
here.


In my experience the ajax is mostly between the components and you can plug
the data to the components via the models.

Normally you would have patterns, but if you adhere well enough to
patterns, you could use reusable components as well.

Following this line of thinking, why stop there, make the whole page
reusable, as it is quite rare that different pages of the same application
would be so different that reuse wouldn't bring huge benefits.


However, as far as I can see, when it comes to actually implementing the
adapters - the nitty gritty, as you rightly called it - (which is
essentially, what I am doing here), the same questions would come up in
both approaches:

- Is using the IMarkupResourceStreamProvider and the
IMarkupCacheKeyProvider the way to go or are there any other ways? For
example, would it be wise to subclass MarkupContainer or Component to
implement this?


We use IMarkupResourceStreamProvider mostly to implement "unorthodox"
components (which can still be reusable), but if a reusable component is
easy to do in wicket, I would prefer to do it the old way.

Ofcourse one extreme would be to generate the html/css/js via dsl also, but
that might be a bigger project than the whole wicket?

How do you see your proposal w.r.t something like
https://github.com/xmlet/HtmlFlow ?

It is essentially that, except in Kotlin and with Wicket components mixed into this DSL, so I don't separate HTML and Wicket components.

val name = Model.of<String>()

div {
    h1 { text("Choose a name") }
    form("nameForm") {
        component("select", BootstrapSelect(
            "choice", name, listOf("Martin", "Matthias")
        )

        component(LambdaSubmitLink("submit", body = { "Submit" }) {
            println("Hi ${name.`object`}")
        })

        div {
            text("Here is something else")
        }
    }
}

Now I could just extract the BootstrapSelect and wouldn't have to worry about writing HTML or an extra Panel or a FormComponentPanel or a Border. I just write some functions and am done with it. And these functions are simple and easily reusable across the whole project and even other projects, if I wanted to.

fun PageBuilder.nameSelect(model: IModel<String>) {
    component("select", BootstrapSelect(
        "choice", model, listOf("Martin", "Matthias")
    )
}

div {
    h1 { text("Choose a name") }
    form("nameForm") {
        nameSelect(name)

        component(LambdaSubmitLink("submit") {
            println("Hi ${name.`object`}")
        })

        div {
            text("Here is something else")
        }
    }
}

Or if I wanted to write a generic form field.

fun PageBuilder.formField(label: String, init: PageBuilder.() -> Unit) {
    div(classes = "form-row") {
        label(classes = "") { text(label) }
        init()
    }
}

div {
    h1 { text("Choose a name") }
    form("nameForm") {
        formField("Name") {
            nameSelect(name)
        }

        formField("Age") {
            component(TextField("age", ageModel))
        }

        component(LambdaSubmitLink("submit") {
            println("Hi ${name.`object`}")
        })

        div {
            text("Here is something else")
        }
    }
}

And that's all the Kotlin code I need. I don't need to write a Border.

class FormField(id: String): Border(id)

with some additional markup files

<wicket:border>
    <div class="form-row">
        <label wicket:id="label"></label>
        <wicket:body></wicket:body>
    </div>
</wicket:border>

and then in the final form, I need to define another HTML element and the select

<div wicket:id="nameBorder">
    <select wicket:id="name"></select>
</div>

and then add all of that to the page

add(FormField("nameBorder").apply { add(BootstrapSelect("name"...)) })

I cannot just reuse the BootstrapSelect in ordinary Wicket, because I always need to define it in HTML or write a Panel for it, which is even more code. With this approach, I don't need to write all of this code and have basically the same outcome, while still being able to drop down and write a Wicket component, if I need to.

In java, this would only be possible with some kind of DSL like the HtmlFlow project you shared.



- Are there performance/caching/framework problems if you re-render the
markup and components on every request?


We have actually used this to improve performance, and cache the result
also.


- Is mitigating that, by just rendering the markup once per
component/page viable or would that lead to a MarkupCache growing
linearly with the amount of rendered pages and therefore explode the
memory usage?


This might be a "premature optimization" issue, probably just do it and
optimize on demand.


Thanks, that helps a lot!


Or am I missing something? I don't see, how a declarative UI would
circumvent these questions. Except, if there was a compile step, which
would compile it down to Wicket code.


Now it feels like the declarative ui would be the higher level (think "the
marionette puppeteer") and this dsl would be on the implementation or
framework level, how the components are actually built.

One question is, assume you have a canonical (minimum) set of declarative
(or otherwise) reusable components, what is the benefit from the dsl if
those are quite easy to implement without dsl?


In case of Wicket, not separating markup and components. That's all there is to it.

**
Martin




Best regards

Matthias


Reply via email to