从 Elm 端口检索 DOM 值
Retrieving a DOM value from Elm ports
我的 elm 应用程序使用自动滚动功能,它获取元素的 Y 位置并使用 Dom.Scroll.toY 滚动到那里。
两个这样,我设置了两个端口;订阅和发件人。
ports.elm
port setYofElementById : Maybe String -> Cmd msg
port getYofElementById : (Value -> msg) -> Sub msg
index.html
app.ports.setYofElementById.subscribe(function(id) {
var element = document.getElementById(id);
var rect = element.getBoundingClientRect();
app.ports.getYofElementById.send({"number": rect.top});
})
侦听器是一个订阅
subscriptions : Model -> Sub Msg
subscriptions model =
Ports.getYofElementById getYofElementById
getYofElementById : Decode.Value -> Msg
getYofElementById value =
let
result =
Decode.decodeValue bSimpleIntValueDecoder value
in
case result of
Ok simpleIntValue ->
SetSelectedElementYPosition (Just simpleIntValue.number)
Err id ->
SetSelectedElementYPosition Nothing
SetSelectedElementYPosition
只是设置模型。
现在,执行此操作的动作做了两件事:调用 Port.setYofElementById
,然后滚动到模型中的 Y 值,假设它已经设置。
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
ScrollToY idString ->
model
=> Cmd.batch
[ Ports.setYofElementById (Just idString)
, Task.attempt (always NoOp) <| Dom.Scroll.toY "ul" model.selectedElementYPosition
]
但是,这不会按顺序发生。当动作第一次触发时,什么也没有发生。如果我再次触发它,它会滚动到第一个动作中要求的位置。所以它似乎在设置值之前调用 Dom.Scroll.toY
。
有没有办法强制 ScrollToY
中的 Cmd
按顺序发生?或者一般来说有更好的方法来做到这一点吗?
您可以让 Cmd
按顺序执行,方法是让第二个执行 Dom.Scroll.toY
的事件作为对第一个执行 [= 的响应的响应13=]。以下更新函数完成此操作:
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
ScrollToY idString ->
(model, Ports.setYofElementById idString)
SetSelectedElementYPosition (Just newY) ->
(model, Task.attempt (always NoOp) <| Dom.Scroll.toY "ul" newY)
SetSelectedElementYPosition Nothing ->
(model, Cmd.none)
NoOp ->
(model, Cmd.none)
在 Cmd
正确排序后,您需要确保 Dom.Scroll.toY
的 newY
参数在正确的参考系中以获得您想要的效果.
我终于通过将 Task.attempt (always NoOp) <| Dom.Scroll.toY "ul" model.selectedElementYPosition
的操作添加到 订阅 调用的操作而不是操作来实现它。这就是关键。
对于端口,subscribe
和 send
操作遵循完全不同的路径,因此任何对从 js 到 elm 的 send
做出反应的操作都不会在操作中被引用从 elm 到 js。
在这种情况下,由于 SetSelectedElementYPosition
是从订阅调用的,您必须在那里设置更新:
SetSelectedElementYPosition idString ->
({model | selectedElementYPosition = number }, Cmd.none)
|> andThen update GoToSelectedElementYPosition
我的 elm 应用程序使用自动滚动功能,它获取元素的 Y 位置并使用 Dom.Scroll.toY 滚动到那里。
两个这样,我设置了两个端口;订阅和发件人。
ports.elm
port setYofElementById : Maybe String -> Cmd msg
port getYofElementById : (Value -> msg) -> Sub msg
index.html
app.ports.setYofElementById.subscribe(function(id) {
var element = document.getElementById(id);
var rect = element.getBoundingClientRect();
app.ports.getYofElementById.send({"number": rect.top});
})
侦听器是一个订阅
subscriptions : Model -> Sub Msg
subscriptions model =
Ports.getYofElementById getYofElementById
getYofElementById : Decode.Value -> Msg
getYofElementById value =
let
result =
Decode.decodeValue bSimpleIntValueDecoder value
in
case result of
Ok simpleIntValue ->
SetSelectedElementYPosition (Just simpleIntValue.number)
Err id ->
SetSelectedElementYPosition Nothing
SetSelectedElementYPosition
只是设置模型。
现在,执行此操作的动作做了两件事:调用 Port.setYofElementById
,然后滚动到模型中的 Y 值,假设它已经设置。
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
ScrollToY idString ->
model
=> Cmd.batch
[ Ports.setYofElementById (Just idString)
, Task.attempt (always NoOp) <| Dom.Scroll.toY "ul" model.selectedElementYPosition
]
但是,这不会按顺序发生。当动作第一次触发时,什么也没有发生。如果我再次触发它,它会滚动到第一个动作中要求的位置。所以它似乎在设置值之前调用 Dom.Scroll.toY
。
有没有办法强制 ScrollToY
中的 Cmd
按顺序发生?或者一般来说有更好的方法来做到这一点吗?
您可以让 Cmd
按顺序执行,方法是让第二个执行 Dom.Scroll.toY
的事件作为对第一个执行 [= 的响应的响应13=]。以下更新函数完成此操作:
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
ScrollToY idString ->
(model, Ports.setYofElementById idString)
SetSelectedElementYPosition (Just newY) ->
(model, Task.attempt (always NoOp) <| Dom.Scroll.toY "ul" newY)
SetSelectedElementYPosition Nothing ->
(model, Cmd.none)
NoOp ->
(model, Cmd.none)
在 Cmd
正确排序后,您需要确保 Dom.Scroll.toY
的 newY
参数在正确的参考系中以获得您想要的效果.
我终于通过将 Task.attempt (always NoOp) <| Dom.Scroll.toY "ul" model.selectedElementYPosition
的操作添加到 订阅 调用的操作而不是操作来实现它。这就是关键。
对于端口,subscribe
和 send
操作遵循完全不同的路径,因此任何对从 js 到 elm 的 send
做出反应的操作都不会在操作中被引用从 elm 到 js。
在这种情况下,由于 SetSelectedElementYPosition
是从订阅调用的,您必须在那里设置更新:
SetSelectedElementYPosition idString ->
({model | selectedElementYPosition = number }, Cmd.none)
|> andThen update GoToSelectedElementYPosition