question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Limitations of the component model

See original GitHub issue

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:

<div><div class="dialog"/></div>

Please, see full source code here

@dom functions (official)

Official way is using a regular function with @dom annotation:

@dom
def dialog: Binding[Div] = <div class="dialog"/>

Which can be used in other @dom functions:

<div>{dialog.bind}</div>

User defined tags (unofficial)

This is undocumented, but it is possible to write composable user defined tags. The same component as above is:

implicit final class UserTags(x: TagsAndTags2.type) {
  object dialog {
    def render: Div = {
        val div = x.div.render
        div.className = "dialog"
        div
    }
  }
}

Which can be used in @dom functions:

<div><dialog/></div>

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

    @dom
    def dialog(id: String): Binding[Div] = <div id={id} class="dialog"/>
    

    and using:

    <div>{dialog("message").bind}</div>
    
  2. Nothing changed in the implementation of user defined tag.
    Use it like this:

    <div><dialog id="message"/></div>
    

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:

    @dom
    def dialog(id: String, caption: Binding[String]): Binding[Div] = <div id={id} class="dialog" data:dialog-caption={caption.bind}/>
    

    and using:

    val caption = Var("Caption")
    <div>{dialog("message", caption).bind}</div>
    
  2. It’s quite hard to implement the same for user defined tag. But easy to use.

    <div><dialog id="message" caption={caption.bind}/></div>
    

Please, see full source code here

Containment

Some components don’t know their children ahead of time. This is especially common for components like Sidebar or Dialog that represent generic “boxes”. (source)

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:

    @dom
    def dialog(children: BindingSeq[Node]): Binding[Div] = <div class="dialog">{children}</div>
    

    but it’s inconvenient to use:

    val warning = Some(<div>warning</div>)
    <div>{dialog(Constants(<div>Some text</div> +: warning.bind.toSeq:_*)).bind}</div>
    

    converting types to BindingSeq[Node] makes code less readable and kills a partial update

  2. Nothing changed in the implementation of user defined tag. And any kind of children can be used like a charm 😄:

    val warning = Some(<div>warning</div>)
    <div><dialog><div>Some text</div>{warning.bind}</dialog></div>
    

Please, see full source code here

Multiple “holes” in a component

While this is less common, sometimes you might need multiple “holes” in a component (the same source)

In both component models you can add BindingSeq[Node] parameter and have hard way on using it 😞

Painless HTML creation

  1. As you can see in previous examples, HTML creation is easy in @dom component (because of @dom magic):

    @dom
    def dialog(id: String, caption: Binding[String]): Binding[Div] = <div id={id} class="dialog" data:dialog-caption={caption.bind}/>
    
  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:

    object Dialog {
      @dom
      def apply(id: String, caption: Binding[String]): Binding[Div] = <div id={id} class="dialog" data:dialog-caption={caption.bind}/>
    }
    
    implicit final class UserTags(x: TagsAndTags2.type) {
      val dialog = Dialog
    }
    

    when using stays the same as before:

    <div><dialog id="message" caption={caption.bind}/></div>
    

Please, see full source code here

Ops, there is some ugly workaround

Conclusion

Current state

@dom tag
html element attribute ✔️ ✔️
component attribute ✔️ ✔️
bindable attribute ✔️ ✔️
containment ✔️
multiple “holes”
painless HTML creation ✔️

Possible improvements

  1. Painless conversion of any types of children to BindingSeq[Node] (or to any other more appropriate type)
  2. I think, user defined tags is powerful and elegant concept and it should get an official support 😉
  3. … etc.

List of references

  1. https://github.com/ThoughtWorksInc/Binding.scala/pull/110
  2. https://github.com/ThoughtWorksInc/Binding.scala/issues/43
  3. https://scalafiddle.io/sf/gII6UlB/0
  4. https://github.com/ThoughtWorksInc/Binding.scala/issues/42
  5. https://github.com/ThoughtWorksInc/Binding.scala/issues/4
  6. https://reactjs.org/docs/composition-vs-inheritance.html#containment

Issue Analytics

  • State:open
  • Created 5 years ago
  • Reactions:8
  • Comments:19 (6 by maintainers)

github_iconTop GitHub Comments

3reactions
lxohicommented, Jan 18, 2019

@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

  • What Antd does is provide components that using some nasty dynamic language features.
  • This makes components really easy to use and makes user code very clean.
  • The user of these components has no needs to deal with the states.
  • Code in Antd will looks like:
<Menu ...>
  <MenuItem ... />
  <MenuItem ... />
  <MenuItem ... />
</Menu>
  • User needs to use same kind of hacks that Antd does when they have some custom features requirements. Or they may have to copy & modify that very massy code pieces of Menu / MenuItem in Antd.
  • To achieve similar behavior in Scala will result in some really complicated and/or meta-programming code. Which may not be easy & clean anymore.

Material-UI’s approach

  • material-ui just provide the basic component with style and limited logic in it.
  • When user needs components like this, they provide the code piece that handles the states.
  • This makes user code more complicated compared to Antd. While still far more simple than the implementation of Antd component itself.
  • 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>
    )
}
  • User can simply do something similar to this code piece when they have some custom features requirements.
  • We can simply do the same thing in Scala.

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:

2reactions
Atrycommented, Dec 14, 2018

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?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Component Based Software Engineering and its Limitations
Limitations of Early Component Systems. In spite of the fact that the components can be used at the application level they lack the...
Read more >
Strengths and Limitations of the Tri Component Model - Quizlet
1. doesn't indicate strengths of an attitude, only direction · 2. doesn't indicate the accessibility of an attitude · Cognitive Dissonance. Having inconsistent ......
Read more >
CS 530 - Software Engineering class notes
The component model includes a specification of how components should be packaged for deployment as independent, executable entities. Component models are the ...
Read more >
Chapter 17 Component-based software engineering
The component model specifies how the interfaces should be defined and the elements, such as operation names, parameters and exceptions, which should be ......
Read more >
Component-based Software Engineering - Kreatx
Lack of context specification: Components should lack context specification. This way, they can be integrated into different environments and ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found