i missed something that i had to say.
if there are one more textarea tags, above workaround causes focus changing always.
so there should be some logic to prevent this.
module Works =
type MsgX = | InputX of string * int * int
| InputX2 of string
| ChangeTime
let subjectX , observableX = FSharp.Control.AsyncRx.subject<MsgX>()
type ModelX = {
// use timestamp to determine whether there should be setting cursor position or not.
time : int
input : int * string * int * int
input2 : string
}
let updateX (modelX : ModelX) (msgX : MsgX) : ModelX =
match msgX with
| MsgX.InputX(s, start, ``end``) ->
{ modelX with
time = modelX.time + 1;
input = (modelX.time + 1, s, start, ``end``)
}
| ChangeTime -> { modelX with time = modelX.time + 1 }
| MsgX.InputX2 s -> { modelX with input2 = s }
type ComponentX(modelX : ModelX) as this =
inherit Fable.React.Component<ModelX, unit>(modelX) with
let mutable refTextArea : Option<Browser.Types.HTMLTextAreaElement> = None
override _.render() : ReactElement =
let _, s, _, _ = this.props.input
div []
[
textarea
[
Props.Ref(fun e -> refTextArea <- Some(e :?> Browser.Types.HTMLTextAreaElement))
OnChange this.OnChange
OnBlur this.OnBlur
Value s
Rows 10
Cols 40
]
[]
textarea
[
OnChange this.OnChange2
Value this.props.input2
]
[]
]
override _.componentDidUpdate(_ : ModelX, _ : unit) : unit =
refTextArea
|> Option.filter (fun _ ->
// determine whether there should be setting cursor position or not.
let time, _, _, _ = this.props.input
time = this.props.time
)
|> Option.iter (fun elem ->
let _, _, start, ``end`` = this.props.input
elem.selectionStart <- start;
elem.selectionEnd <- ``end``
)
member _.OnChange(e : Browser.Types.Event) : unit =
let target = e.target :?> Browser.Types.HTMLTextAreaElement
let x = target.value
let start = target.selectionStart
let ``end``= target.selectionEnd
async {
do! subjectX.OnNextAsync(MsgX.InputX(x, start, ``end``))
} |> Async.Start
member _.OnChange2(e : Browser.Types.Event) : unit =
subjectX.OnNextAsync(MsgX.InputX2 e.Value) |> Async.Start
member _.OnBlur(e : Browser.Types.Event) : unit =
subjectX.OnNextAsync(MsgX.ChangeTime) |> Async.Start
let viewX (modelX : ModelX) (_ : Dispatch<MsgX>) : ReactElement =
Fable.React.Helpers.ofType<ComponentX, ModelX, unit>(modelX) []
let componentX =
Fable.Reaction.Reaction.StreamView
{time = 1; input = 0, "", 0, 0; input2 = ""}
viewX
updateX
(fun _ o ->
o
|> FSharp.Control.AsyncRx.merge observableX
|> Fable.Reaction.AsyncRx.tag "x"
)
mountById "elmish-app" (ofFunction componentX () [])
the key point is to use timestamp.
of course, i admit that this workaround is quite complex and ugly.
just reference it please.