Elm with webcomponents - 这是一个错误还是我做错了什么?
Elm with webcomponents - Is this a bug or am I doing something wrong?
问题总结
我在 elm 0.19.1 中使用 webcomponents。
目标是让 web 组件在 web 组件属性“requeststate”发生变化时发出事件。
事件已发出(我可以在 webcomponent 构造函数的日志记录中看到),但 elm 没有正确触发 'WebcomponentEvent'。
windows 10 和 ubuntu 16.04、firefox 和 chrome 都有问题。
没有测试旧的 elm 版本。
重现步骤:
- 我在这个最小的 repo 中重现了这个问题:
https://github.com/Derryrover/elm_webcom_bug
- 编译榆树:
elm make src/Main.elm --output elm.js --debug
- 使用网络服务器(例如 'serve')在本地托管
- 单击按钮 'request'。这将更改 web 组件上的属性 'requeststate'。这反过来会触发 web 组件上的自定义事件 'created'。但是elm没有收到这个事件..
打开 elm 调试器或再次单击 'request' 按钮将神奇地使事件在 elm 中触发。奇怪的。
我还创建了分支 'tom-experiment'。在这个分支中,我从 webcomponent-click 工作中获得了事件,但前提是我直接单击 webcomponent 本身。
这个问题的重要性
为什么要通过更改属性来触发 Web 组件上的事件?
这种方法的目标也是 JavaScript 互操作。
例如,我现在可以使用此 Web 组件来创建日期时间或 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")
] []
]
这是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);
这是我的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>
这在 Elm Slack 上进行了讨论。这个问题最终成为一个时间问题。决议是从
this.dispatchEvent(customEvent)
至
requestAnimationFrame(() => this.dispatchEvent(customEvent))
在自定义元素中,可以看到这个ellie https://ellie-app.com/cqGkT6xgwqKa1.
感谢@antew 的最终解决方案。
还有一个潜在的原因:
这个attributeChangedCallback
运行s before元素连接到DOM
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);
}
}
}
添加
connectedCallback(){
console.log("Now I am ready to emit Events");
}
验证您的 dispatchEvent
不会 运行 太早。
requestAnimationFrame
(或setTimeout
)是等待事件循环为空的解决方法(因此 connectedCallback 运行)
(我不知道您的用例)您还可以在 attributeChangedCallback
中测试 oldValue === null
或 this.isConnected
另请注意,当涉及 shadowDOM 时,您可能需要在该 CustomEvent 上使用 bubbles:true
和 composed:true
。
问题总结 我在 elm 0.19.1 中使用 webcomponents。 目标是让 web 组件在 web 组件属性“requeststate”发生变化时发出事件。 事件已发出(我可以在 webcomponent 构造函数的日志记录中看到),但 elm 没有正确触发 'WebcomponentEvent'。 windows 10 和 ubuntu 16.04、firefox 和 chrome 都有问题。 没有测试旧的 elm 版本。
重现步骤:
- 我在这个最小的 repo 中重现了这个问题: https://github.com/Derryrover/elm_webcom_bug
- 编译榆树: elm make src/Main.elm --output elm.js --debug
- 使用网络服务器(例如 'serve')在本地托管
- 单击按钮 'request'。这将更改 web 组件上的属性 'requeststate'。这反过来会触发 web 组件上的自定义事件 'created'。但是elm没有收到这个事件..
打开 elm 调试器或再次单击 'request' 按钮将神奇地使事件在 elm 中触发。奇怪的。 我还创建了分支 'tom-experiment'。在这个分支中,我从 webcomponent-click 工作中获得了事件,但前提是我直接单击 webcomponent 本身。
这个问题的重要性 为什么要通过更改属性来触发 Web 组件上的事件? 这种方法的目标也是 JavaScript 互操作。 例如,我现在可以使用此 Web 组件来创建日期时间或 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")
] []
]
这是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);
这是我的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>
这在 Elm Slack 上进行了讨论。这个问题最终成为一个时间问题。决议是从
this.dispatchEvent(customEvent)
至
requestAnimationFrame(() => this.dispatchEvent(customEvent))
在自定义元素中,可以看到这个ellie https://ellie-app.com/cqGkT6xgwqKa1.
感谢@antew 的最终解决方案。
还有一个潜在的原因:
这个attributeChangedCallback
运行s before元素连接到DOM
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);
}
}
}
添加
connectedCallback(){
console.log("Now I am ready to emit Events");
}
验证您的 dispatchEvent
不会 运行 太早。
requestAnimationFrame
(或setTimeout
)是等待事件循环为空的解决方法(因此 connectedCallback 运行)
(我不知道您的用例)您还可以在 attributeChangedCallback
oldValue === null
或 this.isConnected
另请注意,当涉及 shadowDOM 时,您可能需要在该 CustomEvent 上使用 bubbles:true
和 composed:true
。