Skip to content

Cross framework UI Component

LEDUNOIS Simon edited this page Jul 11, 2019 · 2 revisions

Test avec Lit-Element @SLedunois 08/07/2019


Tester Lit-Element avec ReactJS, VueJS et Angular2+


Installer Lit-Element

Pour utiliser Lit-Element, il faut installer :

  • Lit-Element
  • Un polyfill webcomponent

Dans le package.json du module, ajouter :

    "lit-element": "^2.2.0",
    "@webcomponents/webcomponentsjs": "^2.2.10"

Modifier le fichier tsconfig.json et ajouter dans les options du compiler la configuration suivante :

"experimentalDecorators": true

Modifier le fichier webpack.config.js:

loaders: [
            {
                test: /\.(ts|js)$/,
                loader: 'ts-loader'
            }
        ]

Modifier le fichier gulpfile.js afin d'ajouter la copie du polyfill :

gulp.task('copy-polyfill', ['rev'], () => {
    gulp.src('./node_modules/@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js')
        .pipe(gulp.dest('./src/main/resources/public/dist'));
});

gulp.task('build', ['copy-polyfill'], () => {
    var refs = gulp.src("./src/main/resources/view-src/**/*.html")
        .pipe(revReplace({manifest: gulp.src("./rev-manifest.json") }))
        .pipe(gulp.dest("./src/main/resources/view"));

    var copyBehaviours = gulp.src('./src/main/resources/public/dist/behaviours.js')
        .pipe(gulp.dest('./src/main/resources/public/js'));

    return merge[refs, copyBehaviours];
});

Modifier la vue de l'application pour ajouter le polyfill webcomponent :

<title>{{#i18n}}mediacentre.title{{/i18n}}</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<script src="/mediacentre/public/dist/custom-elements-es5-adapter.js" type="text/javascript"></script>
<script src="/mediacentre/public/dist/entcore/ng-app.js" type="text/javascript" id="context"></script>
<script src="/mediacentre/public/dist/application.js" type="text/javascript"></script>

Créer un composant

Créer un composant dans un dossier elements dans src/main/resources/public/ts :

import {css, html, LitElement} from "lit-element";

class ResourceCard extends LitElement {

    static get styles() {
        return css`
            :host {
                background-color: #FFF;
                border-radius: 10px;
                box-shadow: 0px 5px 5px rgba(0, 0, 0, .2);
            }
        `
    };

    render(): any {
        return html`<div class="card">
            <slot></slot>
        </div>`
    }
}

export const element = {
    name: 'resource-card',
    element: ResourceCard
};

Ajouter un fichier index.ts dans le dossier elements:

export * from './ResourceCard';

Dans le fichier app.ts importer et injecter les Web components :

import * as elements from './elements';

for (let customElement in elements) {
	let {name, element} = elements[customElement];
	customElements.define(name, element)
}

Problèmes rencontrés

Lors de l'expérimentation, un problème de compatibilité entre AngularJS et lit-element est rapidement arrivé. En effet, le data binding d'Angular rentre en conflit avec le converter de Lit-Element (élément qui transforme les propriétés des webcomponents en fonction du paramètre cf. documentation). Ainsi, à chaque modification par le binding Angular, on retrouve cette erreur :

VM1408:1 Uncaught SyntaxError: Unexpected token g in JSON at position 1

Le problème intervient dans la fonction d'update au niveau du data binding, plus précisément : https://github.com/Polymer/lit-element/blob/master/src/lib/updating-element.ts#L155

Lors d'une modification de la donnée, les propriétés du composant de type Object et Array sont soumis à un JSON.parse.Cependant, lors du repaint par Angular, l'updater de Lit Element semble intercepter la valeur décrite dans l'HTML et tente donc de parser [[myValue]] générant l'erreur précédente.

La solution trouvée est d'utiliser une bibliothèque d'aide trouvée sur internet (et surement non maintenue) : https://github.com/oriweingart/ng-lit

Il suffirait ainsi de modifier les composants créés et d'y ajouter un appel à NgLit :

import {css, html, LitElement} from "lit-element";
import {NgLit} from "ng-lid"

class ResourceCard extends NgLit(LitElement) {

    static get styles() {
        return css`
            :host {
                background-color: #FFF;
                border-radius: 10px;
                box-shadow: 0px 5px 5px rgba(0, 0, 0, .2);
            }
        `
    };

    render(): any {
        return html`<div class="card">
            <slot></slot>
        </div>`
    }
}

export const element = {
    name: 'resource-card',
    element: ResourceCard
};

Pour rendre cross framework et rétro-compatible (pour les applications AngularJS) l'infra front, il faudrait forker la librairie ngLit et la modifier pour que la surcharge de LitElement ne s'effectue que lorsque l'application utilisée est une application AngularJS.

Clone this wiki locally