Une classe de base pour créer des composants Web à l'aide de Lit-HTML
lit-element peut être installé via le package NPM Lit-HTML-Element.
lit-element vous permet de créer des composants Web avec des modèles HTML exprimés avec des littéraux de modèle JavaScript, et rendent et renforce efficacement ces modèles à DOM.
lit-element y parvient en intégrant Lit-HTML et possède les fonctionnalités suivantes:
render()renderCallbackinvalidate()this ou destructriceid dans la racine de l'ombre en utilisant this.$(...)Les démos peuvent être trouvées ici.
Écrivez simplement votre code HTML à l'aide de lit-html en créant une méthode render() .
import { LitElement , html } from '/src/lit-element.js' ;
class HelloWorld extends LitElement {
render ( ) {
return html `
< div style =" font-weight: bold " > Hello World </ div >
` ;
}
}
customElements . define ( 'hello-world' , HelloWorld ) < hello-world > </ hello-world >id Après que le contenu a été rendu la première fois (c'est-à-dire après que connectedCallback() tire), vous pouvez accéder aux éléments dans la racine de l'ombre par id en utilisant this.$(...) .
Dans l'exemple ci-dessous, nous appelons this.changeColor() chaque fois que le bouton est enfoncé, ce qui accède au résultat à la div en utilisant this.$("wrapper") et modifie sa couleur d'arrière-plan.
class ColorMarker extends LitElement {
changeColor ( ) {
const color = Math . random ( ) . toString ( 16 ) . substr ( 2 , 6 ) ;
// Easily query the element by id:
this . $ ( "wrapper" ) . style . backgroundColor = `# ${ color } ` ;
}
render ( ) {
return html `
< style >
div {
background-color: yellow;
}
</ style >
< button on-click = ${ ( ) => this . changeColor ( ) } >
Change background color
</ button >
< div id =" wrapper " > < slot > </ slot > </ div >
` ;
}
}
customElements . define ( 'color-marker' , ColorMarker ) ; < color-marker > Horse </ color-marker > Dans cet exemple, nous utiliserons des propriétés. Chaque propriété définie dans Static Getter properties() s'assurera que le contenu est rendu au bon moment lorsqu'il est modifié.
Les propriétés peuvent avoir des valeurs par défaut et peuvent même être reflétées via des attributs (les changements vont dans les deux sens). Au lieu de faire de la magie et de convertir des cas après des règles spéciales comme upper-case vs upperCase , vous définissez à la place l'exemple à quel nom d'attribut la propriété devrait refléter, et donc éviter toute ambiguïté.
Remarque, lorsque vous utilisez des propriétés, vous devez this.withProperties appeler. Comme la méthode renvoie la classe elle-même, cela peut être fait dans le cadre de customElements.define(...)
Remarque, les attributs Les valeurs par défaut sont définies à partir des attributs d'élément eux-mêmes (présents ou manquants) et donc les valeurs par défaut définies via «valeur» sont ignorées.
import { LitElement , html } from '/src/lit-element.js' ;
class HelloWorld extends LitElement {
static get properties ( ) {
return {
uppercase : {
type : Boolean ,
attrName : "uppercase"
}
}
}
render ( ) {
return html `
< style >
.uppercase {
text-transform: uppercase;
}
</ style >
< div id =" box " class$ =" ${ this . uppercase ? 'uppercase' : '' } " >
< slot > Hello World </ slot >
</ div >
` ;
}
}
customElements . define ( 'hello-world' , HelloWorld . withProperties ( ) ) ; < hello-world > </ hello-world >
< hello-world uppercase > ¡Hola, mundo! </ hello-world > Lors de la création d'éléments personnalisés, un bon modèle consiste à utiliser des attributs au lieu de méthodes ou de propriétés. Cela permet à l'utilisation de l'élément de manière déclarative comme <my-dialog opened> .
Pour les éléments personnalisés consommés uniquement en interne dans d'autres éléments personnalisés, il est souvent plus rapide de simplement s'appuyer sur les propriétés. C'est également le cas si vous avez besoin de transmettre des données complexes telles que des tableaux ou des objets.
Afin de faciliter le travail avec les attributs, lit-html-element prend automatiquement le mappage entre les attributs et les propriétés, simplement en définissant le nom de l'attribut, la propriété devrait cartographier avec Via attrName: .
La présence d'attributs ou non (sur les éléments) entraîne des valeurs réelles , c'est-à-dire. Un attribut manquant pour une propriété booléenne signifie que la propriété sera false et pour tous les autres types de propriétés, undefined . Cela signifie que lorsque le mappage des propriétés aux attributs, il n'y a pas de valeur par défaut que les valeurs sont toujours définies en fonction de la présence, ou non, des attributs. Cela signifie que value: est ignorée lorsque attrName: est présent.
Les valeurs sont converties à l'aide de leurs constructeurs de type, IE String(attributeValue) pour String , Number(attributeValue) pour Number , etc.
Boolean a une manipulation spéciale afin de suivre les modèles de la plate-forme Web.
De la norme HTML:
La présence d'un attribut booléen sur un élément représente la valeur réelle, et l'absence de l'attribut représente la fausse valeur.
Si l'attribut est présent, sa valeur doit être la chaîne vide ou une valeur qui est une correspondance insensible à la cas ASCII pour le nom canonique de l'attribut, sans espace blanc de premier plan ou de fin.
Array et Object sont désactivés pour les attributs et n'ont pas de manipulation spéciale, donc les valeurs sont converties à l'aide de leurs constructeurs comme tout autre type de valeur, sauf booléen.
this est transmis à Render () pour vous, ce qui est plus propre. en particulier lorsqu'il est destructeur. Cependant, vous pouvez toujours les référencer manuellement.
class RenderShorthand extends LitElement {
static get properties ( ) {
return {
greeting : {
type : String ,
value : "Hello"
}
}
}
render ( { greeting } ) {
return html ` ${ greeting } World!` ;
}
}
customElements . define ( 'render-shorthand' , RenderShorthand . withProperties ( ) ) ; Lorsque l'une des propriétés de properties() change, lit-element redémarrera automatiquement. Il en va de même pour les attributs qui sont mappés aux propriétés via attrName .
Si vous devez renvoyer manuellement, vous pouvez déclencher une reprise via un appel pour invalidate() . Cela planifiera un microtasque qui rendra le contenu juste avant la prochaine requestAnimationFrame .
Les éléments personnalisés doivent être mis à niveau avant de travailler. Cela se produit automatiquement par le navigateur lorsqu'il dispose de toutes les ressources dont il a besoin.
Cela signifie que si vous effectuez un élément personnalisé qui dépend d'autres éléments personnalisés et utilisez des propriétés pour le flux de données, puis définissez ces propriétés avant la mise à niveau de l'élément, signifie que vous finirez par l'observation des propriétés lit-html-element , ce qui signifie que les mises à jour de la propriété et la réflexion d'attribut ne fonctionneront pas comme prévu.
Il y a une API whenAllDefined(result, container) pour résoudre ce problème, en permettant d'attendre que toutes les dépendances aient été améliorées. Une façon de l'utiliser est d'écraser le renderCallback() :
renderCallback ( ) {
if ( "resolved" in this ) {
super . renderCallback ( ) ;
} else {
whenAllDefined ( this . render ( this ) ) . then ( ( ) => {
this . resolved = true ;
this . renderCallback ( ) ;
} ) ;
}
}Mais vous pourriez toujours réussir à faire de l'ombre des propriétés si vous définissez des valeurs manuelles avant de mettre à niveau comme
document . getElementById ( 'ninja' ) . firstName = "Ninja" ;Alors gardez-les de la manière suivante:
customElements . whenDefined ( 'computed-world' ) . then ( ( ) => {
document . getElementById ( 'ninja' ) . firstName = "Ninja" ;
} ) ;Si vous avez besoin de certaines propriétés calculées et des mises à jour en fonction des autres propriétés, c'est possible en utilisant la valeur «calculée», qui a défini une méthode d'objet avec des arguments comme une chaîne.
Les propriétés calculées mettent à jour uniquement lorsque toutes les propriétés dépendantes sont définies . La valeur par défaut peut être définie en utilisant value:
Remarque, les propriétés calculées ne peuvent pas être réfléchies aux attributs.
Par exemple, par exemple
import { LitElement , html } from '/node_modules/lit-html-element/lit-element.js' ;
class ComputedWorld extends LitElement {
static get properties ( ) {
return {
firstName : {
type : String ,
attrName : "first-name"
} ,
doubleMessage : {
type : String ,
computed : 'computeDoubleMessage(message)'
} ,
message : {
type : String ,
computed : 'computeMessage(firstName)' ,
value : 'Hej Verden'
}
}
}
computeDoubleMessage ( message ) {
return message + " " + message ;
}
computeMessage ( firstName ) {
return `Konichiwa ${ firstName } ` ;
}
render ( ) {
return html `
< div style =" font-weight: bold " > ${ this . doubleMessage } </ div >
` ;
}
}
customElements . define ( 'computed-world' , ComputedWorld . withProperties ( ) ) < computed-world > </ computed-world >
< computed-world first-name =" Kenneth " > </ computed-world > Il est possible d'utiliser lit-html-element à partir de TypeScript au lieu de JavaScript. Lorsque vous utilisez TypeScript, vous pouvez opter pour utiliser des décorateurs au lieu de définir les propriétés statiques Accessor static get properties() .
Lorsque vous utilisez des décorateurs de propriétés, un tel accessoire de propriété statique sera ignoré, et vous n'avez pas non plus besoin d'appeler .withProperties() .
import {
LitElement ,
html ,
TemplateResult ,
customElement ,
property ,
attribute ,
computed
} from '../../src/lit-element.js' ;
@ customElement ( 'test-element' )
export class TestElement extends LitElement {
@ computed ( 'firstName' , 'lastName' )
get fullName ( ) : string {
return ` ${ this . firstName } ${ this . lastName } ` ;
}
@ property ( ) firstName : string = 'John' ;
@ property ( ) lastName : string = 'Doe' ;
@ property ( ) human : boolean = true ;
@ property ( ) favorite : any = { fruit : 'pineapple' } ;
@ property ( ) kids : Array < string > = [ 'Peter' , 'Anna' ] ;
@ attribute ( 'mother' ) mother : string ;
@ attribute ( 'super-star' ) superStar : boolean ;
render ( ) : TemplateResult {
return html `
< h2 > Name: ${ this . fullName } </ h2 >
< h2 > Is human?: ${ human ? "yup" : "nope" } </ h2 >
< h2 > Favorites: ${ JSON . stringify ( this . favorite ) } </ h2 >
< h2 > Kids: ${ JSON . stringify ( this . kids ) } </ h2 >
< h2 > Mother: ' ${ this . mother } ' </ h2 >
< h2 > Superstar?: ' ${ this . superStar } ' </ h2 >
` ;
}
} < test-element super-star mother =" Jennifer " > </ test-element > Afin d'utiliser des décorateurs à partir de typescript, vous devez activer le paramètre de compilateur experimentalDecorators dans votre tsconfig.json ou utiliser l'indicateur --experimentalDecorators .
{
"compilerOptions" : {
"experimentalDecorators" : true
}
}Avec ce qui précède, vous pouvez commencer à utiliser les décorateurs mais doit spécifier manuellement les informations de type:
@ property ( { type : String } )
myProperty: string ;Comme le type peut souvent provenir de la propriété, en particulier dans TypeScript où vous définissez le type, cela ressemble à un peu de double travail. Heureusement, il existe une nouvelle proposition de spécification appelée réflexion sur les métadonnées qui vise à résoudre ce problème. Cette proposition n'a pas encore été officiellement proposée au groupe de travail TC39 (définit la norme JavaScript) mais il existe déjà un polyfill fonctionnel disponible et un support expérimental dans TypeScript.
Avec la réflexion des métadonnées activée, il est possible de définir plus concise des types de propriétés:
@ property ( ) myProperty: string ;Afin d'utiliser les décorateurs à partir de dactylographiés, suivez les étapes suivantes.
emitDecoratorMetadata dans votre tsconfig.json ou utiliser le drapeau --emitDecoratorMetadata . {
"compilerOptions" : {
"emitDecoratorMetadata" : true
}
}$ npm install --save-dev rbuckton/reflect-metadata < script src =" /node_modules/reflect-metadata/Reflect.js " > </ script >La documentation API suivante utilise Web IDL.
PropertyOptions PropertyOptions est utilisée pour configurer les propriétés de l'élément personnalisé. Dans JavaScript, vous devez implémenter un accessoire de propriété statique appelé properties , qui renvoie un objet où chaque propriété de cet objet a une PropertyOptions associée:
class {
static get properties ( ) {
return { selfDefinedObjectProperty : ... }
}
} Le dictionnaire PropertyOptions a 4 propriétés facultatives, illustrées ci-dessous dans le format Web IDL.
typedef (BooleanConstructor or DateConstructor or NumberConstructor or StringConstructor or ArrayConstructor or ObjectConstructor) PropertyType ;
dictionary PropertyOptions {
attribute PropertyType type ;
attribute any value ;
attribute USVString attrName ;
attribute USVString computed ;
}type La propriété type n'est facultative que lors de l'utilisation de décorateurs et de réflexion des métadonnées.
value La propriété value définit une valeur par défaut pour la propriété. En cas de mappage d'attribut / propriété via attrName (voir ci-dessous), value est ignorée. Lorsque vous utilisez des décorateurs, la valeur tire de la définition de la propriété elle-même:
@ property ( ) myProperty: string = "Hello World" ; attrName L' attrName définit le nom de l'attribut qui doit être reflété avec la propriété et l'inverse. Avec attrName , les valeurs par défaut sont ignorées et déterminées à partir de l'élément personnalisé à la place, c'est-à-dire. en fonction de la présence ou non des attributs.
Le nom d'attribut, beaucoup en lettres latins (AZ), y compris '-' (Hyphen). Tous les attributs sur les éléments HTML dans les documents HTML sont automatiquement classés ASCII-LOWERSED, et le trait d'union initial ('-') est ignoré.
Sachez que les attributs de données, c'est-à-dire. Les attributs commençant par data- sont accessibles en tant que propriétés automatiquement via element.dataset .
Lorsque les propriétés mappées sont définies sur l'élément, l'attribut est mis à jour avec la représentation de chaîne de la nouvelle valeur, sauf si la nouvelle valeur undefined dans laquelle l'attribut est supprimé.
Il y a une exception à cela, en tant que propriétés booléennes qui se reflètent différemment. La définition de la propriété sur true et l'attribut (Say attr ) est définie sur la chaîne vide '' (l'attribut de sens est présent, c'est-à-dire <div attr> ). Définition de la propriété sur false et l'attribut est supprimé, c'est-à-dire. <div> .
Lorsque les attributs sont définis, les valeurs sont converties à l'aide de leurs constructeurs de type, IE String(attributeValue) pour String , Number(attributeValue) pour Number , etc.
Boolean a une manipulation spéciale afin de suivre les modèles de la plate-forme Web.
De la norme HTML:
La présence d'un attribut booléen sur un élément représente la valeur réelle, et l'absence de l'attribut représente la fausse valeur.
Si l'attribut est présent, sa valeur doit être la chaîne vide ou une valeur qui est une correspondance insensible à la cas ASCII pour le nom canonique de l'attribut, sans espace blanc de premier plan ou de fin.
En savoir plus dans la section de réflexion d'attribut ci-dessus.
computed Les propriétés peuvent être calculées à partir d'autres propriétés à l'aide de computed , il faut une chaîne comme 'methodName(property1, property2)' , où methodName est une méthode sur l'élément et property1 et property2 sont définies.
Les propriétés calculées mettent à jour uniquement lorsque toutes les propriétés dépendantes sont définies . La valeur par défaut peut être définie en utilisant value:
Remarque, les propriétés calculées ne peuvent pas être réfléchies aux attributs.
renderCallback Le renderCallback permet des crochets personnalisés avant et après le rendu.
Si vous devez effectuer un travail supplémentaire avant le rendu, comme la définition d'une propriété en fonction d'une autre propriété, une sous-classe peut remplacer renderCallback() pour travailler avant ou après les appels de la classe de base render() , y compris la définition de la propriété dépendante avant render() .
withProperties()FAIRE:
render(HTMLElement this)TODO: déplacer les documents ici
async invalidate()TODO: déplacer les documents ici
$(DOMString id)TODO: déplacer les documents ici
whenAllDefined(TemplateResult result)TODO: déplacer les documents ici
@customElement(USVString tagname)Un décorateur de classe pour enregistrer l'élément personnalisé
@ customElement ( 'my-element' )
class extends HTMLElement {
...
}@property(optional PropertyOptions options) Un décorateur de propriétés pour s'accrocher au système de propriété lit-html-element .
Lorsque vous utilisez le décorateur de propriétés, vous n'avez pas besoin de définir les propriétés statiques accessoires static get properties() .
Lorsque vous utilisez des décorateurs de propriétés, un tel accessoire de propriété statique sera ignoré, et vous n'avez pas non plus besoin d'appeler .withProperties() .
@ property ( { type : String } )
myProperty: string ;Vérifiez les extensions pour TypeScript pour plus d'informations.
@attribute(USVString attrName) Un décorateur de propriétés pour s'accrocher au système de propriété lit-html-element et associer une propriété à un attribut d'élément personnalisé.
Consultez la propriété attrName pour plus d'informations.
@computed(any dependency1, any dependency2, ...) Un décorateur de propriétés pour s'accrocher au système de propriété lit-html-element et créer une propriété automatique à partir d'autres propriétés.
Vérifiez la propriété computed pour plus d'informations.
@listen(USVString eventName, (USVString or EventTarget) target) Un décorateur de méthode pour ajouter un écouteur d'événements. Vous pouvez utiliser une chaîne pour Target et il recherchera un élément dans le Shadowroot avec cet id .
Les auditeurs d'événements sont ajoutés après le premier rendu, ce qui crée l'ombre DOM.