Open glmars opened 5 years ago
Agree!
converting types to
BindingSeq[Node]
makes code less readable and kills a partial update❗️
Would it be better if we introduce some type classes to convert things to BindingSeq
?
val warning = Some(<div>warning</div>) <div>{dialog(Constants(<div>Some text</div> +: warning.bind.toSeq:_*)).bind}</div>
The above code have been improved in Binding.scala for FXML, because the type of an HTML literal is Node
, while the type of an FXML literal is Binding[Node]
or BindingSeq[Node]
. See https://github.com/ThoughtWorksInc/Binding.scala/wiki/FXML#whats-different-from-dom
There was a proposal to implement a new annotation @html
, which behaves like @fxml
.
Would it be better if we introduce some type classes to convert things to
BindingSeq
?
Yes, I think it's a good idea anyway. When I implemented Option[Node]
support, I had an idea to implement Option[String]
support also, but It's not possible without extracting domBindingSeq
(from dom.scala
) to some type classes.
The above code have been improved in Binding.scala for FXML
I have to admit, I do not fully understand the benefits of @html
in general and in this case in particular. I should try make this composition with @fxml
:wink:
Good point! If I understand correctly, you introduced new extension point for Binding.scala to manage of children mounting. This is a useful feature, but it does not relate directly to the user defined tags.
We need ability to decompose and organize our code with something like "dom templates": https://github.com/ThoughtWorksInc/Binding.scala/pull/132/files#diff-83b72dae9d603e2ad2d906c1dc0621e7R88
User defined tags are really cool! And I think this is the missing part when comparing with ReasonML. I will definitely use a lot when it becomes available 😆.
In React there's some components that passing properties all the way through it children.
For example, in this code for Antd menu:
Menu
was passing a function as property onSelect
to it's children(here). onSelect
was then passed through SubMenu
(here) or MenuItemGroup
(here) to their children and finally used in Menu.Item
(here).Currently there are two ways to archive similar behavior in Binding.scala:
Menu
as a function accepts an ADT which contains MenuItemGroup
/ SubMenu
/ MenuItem
. And there will be only one Menu component exists. No children components.HList
(like shapeless's labelled generic) as parameter for these kind of properties. Then concat reduce element or new element by it's parent components. This will make the definitions of @dom
functions a little complicated but in a flexible & type safe way.Is there a way that I can passing there kind of "internal" properties around when using user defined tags?
Never mind my last comment. I'm now consider what they do in Antd for complicated components as not a good practice. And I will not implement my component in that structure. I'm now more preferring the approaches for complicated components in material-UI.
@lxohi could you describe here the material-UI approaches?
@glmars Yes.
Let's say that we want some components to build a navigation side panel. This panel have some states in it. (e.g. expand/collapse state of item group OR selected highlights for menu item)
Antd's approach
<Menu ...>
<MenuItem ... />
<MenuItem ... />
<MenuItem ... />
</Menu>
Menu
/ MenuItem
in Antd.Material-UI's approach
Code in material-ui will looks like:
function foo() {
const open = ...;
function clickHandler() {
... modify open ...
}
return (
<List ...>
<ListItem ...>
{open ? <ExpandLess /> : <ExpandMore />}
</ListItem>
<Collapse in={open} ...>
</Collapse>
)
}
Although I was decided to not make my Binding.scala components works like Antd does. (which means passing a lot of things under the water and/or modify the other component from outside of it.)
The Antd components are really easy to use and users will love it A LOT.
So then... My decision
Refs:
I prefer the material-ui's approach as well, except I thought clickHandler
should be encapsulated in another small "component" of mutator. For example, in https://scalafiddle.io/sf/y0ixoUP/0, check
or choice
are mutators to change the Var
state.
This example is genius! And I believe it can loops again and again without having Maximum call stack size exceeded error in the browser which is more interesting :)
Your suggestion is totally correct. That clickHandler
thing wasn't powerful enough. So I've modified navigation
recently adding a function param to deal with it
Hi, @glmars, recently I created bindable.scala, a library that provides type classes to convert other types to Binding
and BindingSeq
automatically.
I think bindable.scala solves the “containment” and “multiple holes” problem.
How do you think of it?
Hi, @Atry, I like it a lot! @lxohi will be happy too, I think :)
Only some questions:
1) Do we need Null
(Empty
) type (for ex. to implement typeclass for Option
)?
(I suppose, com.thoughtworks.binding.Binding.Empty
is a good candidate)
2) I do not have much experience with functional Scala, but we really need such a complex implementation with Lt
,Aux
and etc.?
3) I worry about the public API :), Bindable
(without .Lt
postfix) is more clear name to use in a many places of user defined code.
Hi, @glmars ,
Constants.empty
?Aux pattern
on Google. You can also have a look at https://github.com/mpilquist/simulacrum/pull/74 for discussion.Lt
is a naming convention stolen from https://github.com/milessabin/shapeless/blob/906875e9eaedbcdedb3a7b63e5d34bd2b70f2def/core/src/main/scala/shapeless/singletons.scala#L38 . You can renaming-import it as you wish.Constants.empty
(it's similar to Vars.empty
) :+1: Aux pattern
is clear for me now :wink: Lt
understanding... :smile: Free free to create a PR for Constants.empty
Introduction
In our company we have started using Binding.scala in several projects. The library is wonderful, especially when you write code like web designer does. But when we started decomposing our code, we faced the limitations of the component model, especially when processing child elements.
This issue is a meta-task to discuss this limitations
There are two component models in Binding.scala
Let's extract internal
div
in this example as a component:Please, see full source code here
@dom functions (official)
Official way is using a regular function with
@dom
annotation:Which can be used in other
@dom
functions:User defined tags (unofficial)
This is undocumented, but it is possible to write composable user defined tags. The same component as above is:
Which can be used in
@dom
functions:Features of the component model
Using attributes of an underline html element
It's easy to use attributes of an underline html element.
1) We should slightly change the implementation of our @dom component
2) Nothing changed in the implementation of user defined tag.
Use it like this:
Please, see full source code here
Creating a component attribute
In this chapter we'll create a
caption
attribute for our dialog. And second goal is check ability of using bindable attributes.1) It's just an additional parameter of @dom component function:
2) It's quite hard to implement the same for user defined tag. But easy to use.
Please, see full source code here
Containment
Children can be absent, can contain single node, list of nodes, option node, text node etc. and their combinations.
1) A common type for all of this is
BindingSeq[Node]
, you can easily add such parameter to @dom component function:2) Nothing changed in the implementation of user defined tag. And any kind of children can be used like a charm :smile::
Please, see full source code here
Multiple “holes” in a component
In both component models you can add
BindingSeq[Node]
parameter and have hard way on using it :disappointed:Painless HTML creation
1) As you can see in previous examples, HTML creation is easy in @dom component (because of
@dom
magic):2) Unfortunately, it isn't possible to use
@dom
for implementing user defined tag. It would be great if we could write something like this:Please, see full source code here
Ops, there is some ugly workaround
Conclusion
Current state
Possible improvements
BindingSeq[Node]
(or to any other more appropriate type)List of references