Elm with webcomponents - 这是一个错误还是我做错了什么?

Stu*_*iek 3 web-component elm

问题总结 我在 elm 0.19.1 中使用 webcomponents。目标是让 webcomponent 在 webcomponent 属性“requeststate”发生变化时发出一个事件。事件被发出(我可以在 webcomponent 构造函数的日志中看到),但 elm 没有正确触发“WebcomponentEvent”。问题出在 windows 10 和 ubuntu 16.04、firefox 和 chrome 上。没有测试旧的 elm 版本。

重现步骤:

  • 我在这个最小的 repo 中重现了这个问题:https : //github.com/Derryrover/elm_webcom_bug
  • 编译 elm: elm make src/Main.elm --output elm.js --debug
  • 使用网络服务器(例如“服务”)在本地托管
  • 单击“请求”按钮。这将更改 webcomponent 上的属性“requeststate”。这反过来会触发 webcomponent 上的自定义事件“created”。但是榆树没有收到这个事件..

打开 elm-debugger 或再次单击“请求”按钮将神奇地在 elm 中触发事件。奇怪的。我也做了分支'tom-experiment'。在这个分支中,我从 webcomponent-click 工作中获得了事件,但前提是我直接点击了 webcomponent 本身。

这个问题的重要性 为什么我要通过改变一个属性来触发一个webcomponent上的事件?这种方法的目标也是 JavaScript 互操作。例如,我现在可以使用这个 webcomponent 来创建日期时间或 uuid 或做一些其他的 javascript 魔术。这样我就可以完全解决端口问题。这个问题的解决方案可能会解决整个 Javascript 互操作讨论!

代码

这是我的 Main.elm:

module Main exposing (..)

import Browser
import Html
import Html.Attributes
import Html.Events
import Json.Decode

main =
    Browser.sandbox
        { init = init
        , view = view
        , update = update
        }

type alias Model =
    { request : Bool
    , received: Bool
    }

init : Model
init = { request = False
       , received = False 
       }


type Msg
    = Request
    | Reset
    | WebcomponentEvent


update : Msg -> Model -> Model
update msg model =
    case msg of
        WebcomponentEvent ->
            { model | received = True}
        Request ->
            { model | request = True }
        Reset -> 
            { model | request = False , received = False}
        


view : Model -> Html.Html Msg
view model =
    Html.div []
        [ Html.button [Html.Events.onClick Request] [Html.text "Request"]
        , Html.div 
            [] 
            [ Html.text "requested:"
            , Html.text (if model.request then "true" else "false")
            ]
        , Html.div 
            [] 
            [ Html.text "received:"
            , Html.text (if model.received then "true" else "false")
            ]
        , Html.button [Html.Events.onClick Reset] [Html.text "Reset"]
        , Html.node "webcomponent-test" 
            [ Html.Events.on "created" (Json.Decode.succeed WebcomponentEvent) 
            , Html.Attributes.attribute "requeststate" (if model.request == True then "requested" else "idle")
            ] []
        ]
Run Code Online (Sandbox Code Playgroud)

这是 webcomponent.js

class Webcomponent extends HTMLElement {

  static get observedAttributes() { 
    return [
    "requeststate"
    ]; 
  }
  attributeChangedCallback(name, oldValue, newValue) {
    switch (name) {
      case  "requeststate":
        if (newValue === "requested") {
          console.log(`requested in webcomponent triggered`);
          const customEvent = new CustomEvent('created', {detail: ""});
          this.dispatchEvent(customEvent);
        }
    }
  }
  
  constructor() {
    super();
    
    this.addEventListener('created', function (e) {
      console.log("event triggered as sensed by javascript: ", e.detail, e);
    }, false, true);
    
  }
}

customElements.define('webcomponent-test', Webcomponent);
Run Code Online (Sandbox Code Playgroud)

这是我的 index.html

<!doctype html>
<html>
  <head>
    <script type="module" src="./webcomponent.js"></script>
    <script src="./elm.js"></script>
  </head>
  <body>
    <div id="elm_element"></div>
    <script>
    var app = Elm.Main.init({
      node: document.getElementById('elm_element')
    });
    </script>
  </body>
</html>
Run Code Online (Sandbox Code Playgroud)

wol*_*dex 6

这是在 Elm Slack 上讨论的。该问题最终成为时间问题。决议是从

this.dispatchEvent(customEvent)
Run Code Online (Sandbox Code Playgroud)

requestAnimationFrame(() => this.dispatchEvent(customEvent))
Run Code Online (Sandbox Code Playgroud)

在自定义元素中,可以在这个 ellie https://ellie-app.com/cqGkT6xgwqKa1 中看到。

感谢@antew 提供最终解决方案。

  • 正如对其他找到这里的人的补充说明一样,Elm 确实提供了有关如何将自定义 Web 组件合并到 Elm 应用程序中的建议:https://guide.elm-lang.org/interop/custom_elements.html 用于使用浏览器像 Intl 这样的 API 在这里进行了演示。该页面上的链接中还有其他示例。 (2认同)