Search code examples
domdata-bindingscala.jslost-focusbinding.scala

My input html forms misses focus/empties when a Var changes


I want to create some UI from Binding.scala. The UI contains a textbox. When the user type text in the textbox, I want to change the background color according to the user input.

import com.thoughtworks.binding._, Binding._
import org.scalajs.dom._

@dom def render = {
  val color = Var("")
  val styleText: String = s"background-color: ${color.bind}"

  // <div> and <input> will be recreated once data changes.
  <div style={styleText}>
    <input id="myInput" type="text" oninput={ _: Any => color := myInput.value }/>
  </div>
}

dom.render(document.body, render)

The example runs on ScalaFiddle.

However, when I enter something in the textbox, the textbox misses focus and still keep empty.

How can I fix it?


Solution

  • Maybe you defined the <input ...> and .bind in the same @dom method and the <input ...> is after .bind. Try to refactor the .bind and <input ...> into separate @dom methods or let the .bind expression nested in another DOM.


    For example, if myInput is written before .bind, it will not be recreated:

    import com.thoughtworks.binding._, Binding._
    import org.scalajs.dom._
    
    @dom def render = {
      val color = Var("")
      val myInput = <input id="myInput" type="text" oninput={ _: Any => color := myInput.value }/>
      val styleText: String = s"background-color: ${color.bind}"
    
      // <div> will be recreated once data changes.
      // <input> will not be recreated.
      <div style={styleText}>
        {myInput}
      </div>
    }
    
    dom.render(document.body, render)
    

    The above example runs on ScalaFiddle.


    If the .bind expression is embedded in a XHTML literal, it will not affect its children:

    import com.thoughtworks.binding._, Binding._
    import org.scalajs.dom._
    
    @dom def render = {
      val color = Var("")
    
      // <div> and <input> will not be recreated when data changes.
      // Only the class attribute will be changed.
      <div style={s"background-color: ${color.bind}"}>
        <input id="myInput" type="text" oninput={ _: Any => color := myInput.value }/>
      </div>
    }
    
    dom.render(document.body, render)
    

    The above example runs on ScalaFiddle.


    Another approach is wrapping .bind expression in a @dom val:

    import com.thoughtworks.binding._, Binding._
    import org.scalajs.dom._
    
    @dom def render = {
      val color = Var("")
      @dom val styleText: Binding[String] = s"background-color: ${color.bind}"
    
      // <div> and <input> will not be recreated when data changes.
      // Only the class attribute will be changed.
      <div style={styleText.bind}>
        <input id="myInput" type="text" oninput={ _: Any => color := myInput.value }/>
      </div>
    }
    
    dom.render(document.body, render)
    

    The above example runs on ScalaFiddle.