2

I'm running into trouble getting a simple web component to work when transpiled to ES5. It appears to function perfectly fine under chrome, Edge, and Firefox, but IE11 is failing in the component's constructor with "The custom element constructor did not produce the element being upgraded."

UPDATE

The excellent work of Oliver Krull below has clearly pinned down the problem to Typescript's compiler output. Is it possible to make it work?

The original source (in TypeScript):

import "./AppDrawer.less"

class AppDrawer extends HTMLElement {
    get open() {
        return this.hasAttribute("open");
    }

    set open(val: boolean) {
        val ? this.setAttribute("open", '') : this.removeAttribute('open');
    }

    get disabled() {
        return this.hasAttribute("disabled");
    }

    set disabled(val: boolean) {
        val ? this.setAttribute("disabled", '') : this.removeAttribute('disabled');
    }

    static get observedAttributes() { return ["open"] };

    constructor() {
        super();
    }

    connectedCallback() {
        this.addEventListener("click", () => {
            this.open = !this.open;
        })
        this.textContent = this.open ? "OPEN": "CLOSED";
    }

    attributeChangedCallback(attr, oldVal, newVal) {
        this.textContent = this.open ? "OPEN": "CLOSED";
    }
}


customElements.define("app-drawer", AppDrawer)

The output (bundle.js):

(function () {
'use strict';

function __$styleInject(css) {
    if (!css) return;

    if (typeof window == 'undefined') return;
    var style = document.createElement('style');
    style.setAttribute('media', 'screen');

    style.innerHTML = css;
    document.head.appendChild(style);
    return css;
}

__$styleInject("app-drawer {\n  color: red;\n}\n");

function __extends(d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
}

var AppDrawer = (function (_super) {
    __extends(AppDrawer, _super);
    function AppDrawer() {
        _super.call(this);
    }
    Object.defineProperty(AppDrawer.prototype, "open", {
        get: function () {
            return this.hasAttribute("open");
        },
        set: function (val) {
            val ? this.setAttribute("open", '') : this.removeAttribute('open');
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(AppDrawer.prototype, "disabled", {
        get: function () {
            return this.hasAttribute("disabled");
        },
        set: function (val) {
            val ? this.setAttribute("disabled", '') : this.removeAttribute('disabled');
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(AppDrawer, "observedAttributes", {
        get: function () { return ["open"]; },
        enumerable: true,
        configurable: true
    });

    AppDrawer.prototype.connectedCallback = function () {
        var _this = this;
        this.addEventListener("click", function () {
            _this.open = !_this.open;
        });
        this.textContent = this.open ? "OPEN" : "CLOSED";
    };
    AppDrawer.prototype.attributeChangedCallback = function (attr, oldVal, newVal) {
        this.textContent = this.open ? "OPEN" : "CLOSED";
    };
    return AppDrawer;
}(HTMLElement));
customElements.define("app-drawer", AppDrawer);

}());

And my HTML:

<!doctype html>
<html>
    <head>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/1.0.0-rc.8/webcomponents-lite.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/1.0.0-rc.8/custom-elements-es5-adapter.js"></script>
        <script src="bundle.js"></script>
    </head>
    <body>
        <app-drawer open disabled></app-drawer>
    </body>
</html>

2 Answers 2

4

In order to get custom elements v1 working cross browser we need to add this native-shim (here more infos about it).

This works well in all major browsers (edge, safari, firefox etc..) but doesn't in ie11!

When we try to import it in ie11 we get a bunch of syntax errors because the shim is written with some es6.

A simple workaround is to compile the shim to es5 and add it in addition of the es6 version (first es6, then es5 to omit errors).

This isn't the cleanest solution, but at least it works.

I created a repo with a working example (your app-drawer (removed the disabled attribute because of conflicts)).

Again, it's not verry clean but it works ;-)

Sign up to request clarification or add additional context in comments.

20 Comments

@mseddon I'm at the office right now, i will look at it today evening (CEST, UTC+2)
@mseddon Ok I took the native-shim which is written in es6 and compiled it to es5 + bundled it, wen i import it in ie11 there are no errors anymore and the connectedCallback is fired, the problem i'm facing now, is that the click listener doesn't work, but the text content is set. Do you want me to send you the file in some way? so you can make more trials with it? Sorry i don't have the full solution yet ;-) and not that much time.
@mseddon It works, the problem with the eventlistener is because of the disabled attribute initialy set on the app-drawer. You have to use it differenty like checking disabled == true or false (or something similar). Then your ie11 issue will be solved :-) Tell me if you want me to describe it in details into the answer.
Regarding the event listener, it may be unrelated to this question so no need to sweat that, really I am after getting IE11 vaguely working - feel free to remove the disabled attribute, it sounds like a different SO question
Ok I will write it down and read your link (today evening again ;-))
|
2

The issue was that Typescript's ES5 class implementation is incompatible with the custom-elements-v1 shim. The immediately obvious solution, then, is to configure TypeScript to generate ES6 and pass it through Babel for transpilation.

let rollupTs = rollupTypescript(require('./tsconfig.json').compilerOptions);

rollup({
    entry: "src/client/main.ts",
    plugins: [
        rollupTs,
        less({insert: true}),
        resolve({module: true, jsnext: true, main: true}),
        babel({
            presets: [
                [es2015, { modules: false } ]
            ],
            plugins: ["external-helpers"],
            exclude: "node_modules/**"})
    ],
    dest: "bundle.js",
    format: "iife"
})

However, see the accepted answer- it seems that by adding the es5 transpiled shim to the typescript generated code fixes TypeScript's ES5, while having no effect on babel compiled code. I've accepted it since it's a fairly trivial drop-in fix for this issue, with the caveat that I don't actually understand why it works

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.