问题描述
我想用Elm制作一个Web应用程序,其中将包含一些视频元素,其动态布局可以根据浏览器的宽度和高度而变化。当我尝试这样做时,很明显Elm正在为视频生成新元素,但这是行不通的,因为视频元素的状态需要保留。
为简单起见,我用计数器而不是视频来演示该问题。我试图用Html.lazy
和Keyed.node
解决问题,但问题仍然存在。
这里的代码也可以从https://github.com/ijt/elm-dom-elt-reuse克隆。
src/Main.elm
:
port module Main exposing (..)
import browser
import Html exposing (..)
import Html.Attributes as Attribute exposing (id,style)
import Html.Events exposing (onClick)
main =
browser.element
{ init = init,view = view,update = update,subscriptions = subscriptions
}
type alias Model =
{ layout : Layout }
type Layout
= Row
| Column
init : () -> ( Model,Cmd Msg )
init _ =
( { layout = Row },startCounters () )
type Msg
= ToggleLayout
update : Msg -> Model -> ( Model,Cmd Msg )
update msg model =
case msg of
ToggleLayout ->
let
l2 =
case model.layout of
Row ->
Column
Column ->
Row
in
( { model | layout = l2 },Cmd.none )
subscriptions : Model -> Sub Msg
subscriptions model =
Sub.none
view : Model -> Html Msg
view model =
div []
[ button [ onClick ToggleLayout ] [ text "Toggle Layout" ],counters model
]
counters : Model -> Html Msg
counters model =
case model.layout of
Column ->
div []
[ div [] [ counter1 ],div [] [ counter2 ]
]
Row ->
div [] [ counter1,spacer,counter2 ]
spacer : Html Msg
spacer =
text " "
counter1 : Html Msg
counter1 =
span [ id "counter1" ]
[ text "0" ]
counter2 : Html Msg
counter2 =
span [ id "counter2" ]
[ text "0" ]
port startCounters : () -> Cmd msg
static/index.html
:
<!DOCTYPE HTML>
<html>
<head>
<Meta charset="UTF-8">
<title>Main</title>
<style>body { padding: 0; margin: 0; }</style>
</head>
<body>
<pre id="elm"></pre>
<script src="Main.js"></script>
<script>
window.app = Elm.Main.init( { node: document.getElementById("elm") } );
window.app.ports.startCounters.subscribe(function() {
let c1 = document.getElementById("counter1");
let c2 = document.getElementById("counter2");
function increment(e) {
let n = parseInt(e.innerText);
e.innerText = n + 1;
}
requestAnimationFrame(function() {
setInterval(function() {
increment(c1);
increment(c2);
},100)
})
});
</script>
</body>
</html>
Makefile
:
static/Main.js: src/Main.elm
elm make src/Main.elm --output=static/Main.js
解决方法
将元素始终保持在DOM树中相同的深度即可。
这是新代码:
counters : Model -> Html Msg
counters model =
let
d =
case model.layout of
Column ->
"column"
Row ->
"row"
in
div
[ style "flex-direction" d,style "display" "flex"
]
[ counter1,counter2
]
counter1 : Html Msg
counter1 =
span [ id "counter1",style "padding" "8px" ]
[ text "0" ]
counter2 : Html Msg
counter2 =
span [ id "counter2",style "padding" "8px" ]
[ text "0" ]
port startCounters : () -> Cmd msg
感谢Elm Slack频道上的jessta。