Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Elm not allowing html `script` node in virtual DOM

I try to integrate to my ELM page a "login with" widget (the one from Telegram https://core.telegram.org/widgets/login)

I try to build the appropriate node, which is of type <script>, but when the page is rendered, the type is replaced by <p>. I guess this is a security feature. But how can I build this node ?

What I want:

<script
    async
    src="https://telegram.org/js/telegram-widget.js?21"
    data-telegram-login="botname"
    data-onauth="onTelegramAuth(user)"
></script>

What I do:

telegram : Html Model
telegram =
    node "script"
        [ attribute "async" ""
        , attribute "src" "https://telegrami.org/js/telegram-widget.js?21"
        , attribute "data-telegram-login" "botname"
        , attribute "data-onauth" "onTelegramAuth(user)"
        ]
        []

What I get:

<p
    async=""
    src="https://telegrami.org/js/telegram-widget.js?21"
    data-telegram-login="botname"
    data-onauth="onTelegramAuth(user)"
></p>

Thank for your help :)

like image 370
uben Avatar asked Oct 17 '25 05:10

uben


2 Answers

This is intentional. Elm doesn't allow script tags for security reasons. If you want that kind of functionality, wrap it around in a web component and import the web component in Elm. You can listen for Custom Events on Elm side in order to pass data from the web component to Elm and you can set attributes to the web component in order to pass data from Elm to the web component.

like image 52
pdamoc Avatar answered Oct 18 '25 22:10

pdamoc


Thanks to @pdamoc, I've managed to make it work like this:

Web component: telegram-button.js

export default class TelegramButton extends HTMLElement {
  constructor() {
    const self = super();

    self.onauth = (user) => {
      this.dispatchEvent(new CustomEvent('on-telegram-auth', {detail: user}))
    }

    return self;
  }

  connectedCallback() {
    const script = document.createElement('script');

    script.src = 'https://telegram.org/js/telegram-widget.js?21';
    script.async = true;

    const attributes = {
        'data-telegram-login': this.getAttribute('data-telegram-login'),
        'data-size': this.getAttribute('data-size'),
        'data-radius': this.getAttribute('data-radius'),
        'data-request-access': this.getAttribute('data-request-access'),
        'data-onauth': 'onTelegramAuth(user)',
    };

    for (const [k, v] of Object.entries(attributes)) {
        v !== undefined && script.setAttribute(k, `${v}`);
    }
    this.appendChild(script);
  }
}

const onTelegramAuth = (user) => {
    const button = document.querySelector("telegram-button")
    button.onauth(user)
}

if (!window.customElements.get('telegram-button')) {
    window.TelegramButton = TelegramButton
    window.customElements.define('telegram-button', TelegramButton)
    window.onTelegramAuth = onTelegramAuth
}

Import it inside your index.js

Then in Elm

button : Html Msg
button =
  Html.node "telegram-button"
    [ attribute "data-telegram-login" "MyBot"
    , attribute "data-size" "large"
    , attribute "data-radius" "6"
    , attribute "data-request-access" "write"
    , onTelegramAuthChange OnTelegramAuth
    ] []

onTelegramAuthChange : Msg -> Attribute Msg
onTelegramAuthChange toMsg =
    telegramDataDecoder
        |> Decode.map toMsg
        |> Html.Events.on "on-telegram-auth"

type alias TelegramAuth =
    { id : Int }

telegramDataDecoder : Decode.Decoder TelegramAuth
telegramDataDecoder =
    Decode.map TelegramAuth
        (Decode.at ["detail", "id"] Decode.int)
like image 20
fetsh Avatar answered Oct 18 '25 21:10

fetsh



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!