Uso de Templates en Componentes Web con JavaScript
Resumen
La API template nos permite conectar un web component de forma más profesional y organizada. También nos ayuda a clonar los elementos fácilmente. Como vimos en la clase anterior, el elemento no se clonaba, sino que se pasaba de etiqueta en etiqueta hasta la última en ser renderizada.
La etiqueta <template>
Template es una etiqueta que nos sirve como contenedor de código. Todo lo que escribamos adentro de esta etiqueta no se va a renderizar, sino que hay que activarlo mediante JavaScript. El profesor, sin embargo, no muestra cuál es dicho proceso y procede a utilizar una forma distinta. En el siguiente enlace vas a ver cómo se activa desde JS.
Escribir y activar el código dentro de la clase
De esta forma estamos armando toda la estructura HTML dentro de JavaScript, pero insertándola en la clase y fraccionando el HTML y CSS para más placer.
En este caso, creamos la clase, con su extensión y constructor, luego creamos un método que contendrá la estructura HTML (getElement). Adentro insertamos la variable template que contiene la estructura.
Para clonar el código debemos indicar mediante el método cloneNode que se puede clonar. Para eso invocamos el contenido de getTemplate y lo anidamos a la clase (que luego al ser invocada en el HTML se convierte en la etiqueta misma)
Basicamente la API Template nos permite conectar un web component de forma más profesional y organizada. También nos ayuda a clonar los elementos facilmente (Ya que como lo hicimos en la clase anterior el elemento no se clonaba, sino que se pasaba de etiqueta en etiqueta hasta la ultima en ser renderizada)
La etiqueta <template>
Es una etiqueta que nos sirve como contenedor de código. Todo lo que escribamos adentro de esta etiqueta no se va a renderizar , sino que hay que activarlo mediante Javascript. El profesor sin embargo no muestra cual es dicho proceso y procede a utilizar una forma distinta. En el siguiente enlace vas a ver cómo se activa desde JS:
Escribir y activar el código dentro de la clase
De esta forma estamos armando toda la estructura HTML dentro de Javascript, pero insertandola en la clase y fraccionando el HTML y CSS para más placer.
En este caso, creamos la clase, con su extension y constructor, luego creamos un método que contendrá la estructura HTML (getElement) . Adentro insertamos la variable template que contiene la estructura.
En otro metodo (getStyles) todo lo que hacemos es retornar un literal string que contiene el código CSS (si queremos podemos contenerla en una variable, eso es a comodidad del programador)
getStyles(){return`...(código CSS)`}
y luego al final del código de getElement la llamamos de esta forma
${this.getStyles()}
Clonar Elementos
Para clonar el código debemos indicar mediante el método cloneNode que se puede clonar. Para eso invocamos el contenido de getTemplate, y lo anidamos a la clase (que luego al ser invocada en el HTML se convierte en la etiqueta misma)
Si desean ver como se hace la transición primero pone el color azul y luego cambia al color rojo (minuto 11:50), se podría cambiar en la pestaña de network la velocidad de carga de la página:
Saludos :)
muy interesante tu dato. sirve para hacer ensayos
una simple linea pero poderosa
section>h2{Hola mundo!}+div>p{Soy más texto ejemplo}
Apuntes y código con comentarios:
Template
Esto es opcional. Pero si adentro de tu componente van a venir muchos elementos que se conviertan en nodos, lo idal sería ocupar templates.
Recuerda: todo lo que esté dentro de la etiqueta <Template></Template> NO se va a renderizar de inicio.
Es importante que cloneNode(true) tenga el valor de true porque así podrá copiar todos los elementos anidados del HTML.
Podemos meter estilos con JS pero entrarán en conclicto con otros estilos, pero para esto usaremos ShadowDOM.
<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>Custom Elements</title></head><body><template><h3>Hola desde h2 y template</h3><div><p>Soy más texto dentro de template</p></div></template><my-element></my-element><!!---EstovienedeJSencustomElements.define()--><scripttype="module"src="my-element.js"></script></body></html>
classmyElementextendsHTMLElement{constructor(){super();//* Obtenemos acceso a todos los elementos y métodos de la clase que extendemos (heredamos)}getTemplate(){//*Esto será puro HTMLconst template =document.createElement('template'); template.innerHTML=`<section><h3>Hola gente desde getTemplate()</h3><div><p>Soy un párrafo</p></div></section>${this.getStyles()} //*Aplicamos los estilos
`;return template;}getStyles(){//*Esto será puro CSSreturn` <style>
h2{
color:red;
}
</style>
`;}render(){this.append(this.getTemplate().content.cloneNode(true));//*Es importante que `cloneNode(true)` tenga el valor de true porque así podrá copiar todos los elementos anidados del HTML}//*Esto es lo que agregará cosas al DOMconnectedCallback(){this.render();}}customElements.define('my-element', myElement);//* Definimos que la clase se va a convertir en una etiqueta
De hecho el aplicar el método cloneNode no es necesario ya que por defecto va estar en true.
para los que no entendieron lo de “template”.
lo que pasa es que solo la etiqueta “template” tiene la propiedad “content” con la que puedes hacer el “cloneNode(true)”.
this.getTemplate().content.cloneNode(true)
aquí NO esta clonando de la etiqueta template que creo en el html, esta clonando el innerHTML de la etiqueta “template” que creó con “document.createElement(“template”)”, de hecho si borran la etiqueta template del html verán que no efecta en nada.
a continuación lo que hacer es definir un “innerHTML” para esa etiqueta “template” que creó en la linea anterior.
const template =document.createElement("template");// solo la etiqueta template tiene la propiedad "content" template.innerHTML=`<section><h3>a</h3><div><p>x</p></div></section>`return template;
luego en “render()” sucede la magia, si en render ustedes ponen “this.appendChild(this.getTemplate())” verán que les aparece una etiquete “template” dentro de “my-element”
js
this.appendChild(this.getTemplate()
html
<my-element><template></template></my-element>
sin embargo si ponen " this.appendChild(this.getTemplate().content.cloneNode(true))", verán que les aparece solamente lo que definieron en el “innerHTML”, tal que:
y esto solo es posible hacerlo con la etiqueta “template” ya que dentro de otras etiquetas no existe la propiedad “content”
Para evitar problemas de estilos, no sería mejor separar el css? Es decir aplicar los estilos directamente desde un archivo css y aplicar las clases a nuestros elementos de html. Considero que sería un poco más ordenado aunque también me interesa escuchar otras opiniones.
También es recomendable, yo por el momento desarrollo de esa manera. Claro que también html tiene ciertas etiquetas mínimas de diseño. Así que si es bueno también explorarlas con Js. 🤘
Hola
Separar los estilos CSS es quizá la práctica más utilizada y limpia. Sin embargo puede hacerse todo junto para enseñanza haciendo más directa la explicación. Cómo comenta marroking, es bueno conocer diferentes formas de aplicar estilos.
HTML Template
El elementoHTML <template> es un mecanismo para mantener el contenido HTML del lado del cliente que no se renderiza cuando se carga una página, pero que posteriormente puede ser instanciado durante el tiempo de ejecución empleando JavaScript.
ㅤ
Piensa en la plantilla como un fragmento de contenido que está siendo almacenado para un uso posterior en el documento. El analizador procesa el contenido del elemento <template> durante la carga de la página, pero sólo lo hace para asegurar que esos contenidos son válidos; sin embargo, estos contenidos del elemento no se renderizan.
ㅤ
Para lo que nos sirve la etiqueta <template> es justamente para tener una plantilla, que posteriormente utilizaremos para agregarle contenido dinámico con JavaScript para añadirlas al DOM, la plantilla como tal nunca se mostrará en la página, sólo se mostrarán los nodos creados desde JavaScript que la utilizaron como base para ser creados.
<!-- Creamos la plantilla --><templateid="template-card"><figure><img/><figcaption></figcaption></figure></template>
const $container =document.querySelector(".container"), $template =document.getElementById("template-card").content, $fragment =document.createDocumentFragment(), cardsContent =[{title:"Tecnología",img:"https://placeimg.com/200/200/tech",},{title:"Animales",img:"https://placeimg.com/200/200/animals",}];// Por cada imagen que obtenemos de una API (por ejemplo) utilizaremos la plantilla y sólo le agregaremos el contenidocardsContent.forEach((el)=>{ $template.querySelector("img").setAttribute("src", el.img); $template.querySelector("img").setAttribute("alt", el.title); $template.querySelector("figcaption").textContent= el.title;let $clone =document.importNode($template,true); $fragment.appendChild($clone);});$container.appendChild($fragment);
No entendi muy bien su uso.
Pienso que tiene que ver mas con optimizacion entonces 🤔
Al usar <Template> se crea un Document Fragment el cual no se renderiza, aqui entonces podemos usar innerHTML para definir la estructura HTML del template, dicho template (fragment) tiene un atributo de solo lectura que es content, el cual podemos clonar con cloneNode(true), este metodo recibe un parametro booleano que indica si el clonado sera profundo o no. Al clonarlo estamos transformando el string del template a nodos reales (elementos de HTML) que luego podemos insertar al DOM con append.
De esta forma nosotros solo le pasamos un solo Nodo con sus nodos hijos, en lugar de un innerHTML que el navegador tenga que traducir a nodos, lo cual deriva en mas seguridad y optimizacion. No estoy seguro si lo entendí correctamente.
es correcto así
MY NOTES FOR TEMPLATE
Cabe recalcar que el tema de css es algo de especificada y que saben que las líneas de css que son importante son las que se escriben al final, siempre van a ser mas importantes que las que están arriba es como si se sobrescribieran.
classmyElementextendsHTMLElement{constructor(){super();}//Generamos una funcion en cual tendremos nuestro template html y esta//Funcion retornara este templategetTemplate(){const template =document.createElement('template'); template.innerHTML=`<section><h3>Hola mundo!</h3><div><p> Soy text ejemplo<p></div></section><!-- Para utilizar nuestros estilos de nuestro metodo lo llamamos desde nuestro template html
para que se agreguen -->${this.getStyles()}`;return template
}//Podremos generar otro metodo para poner los estilos, pero esto mas que todo se hace por simples//Buenas practicas pero se puede hacer de otras maneras.getStyles(){return` <style>
h2{
color:red;
}
</style>
`}//Generamos otro metodo para que el contenido de nuestra etiqueta template se pueda clonar//Para poderlo agregar al domrender(){//Agregamos el elemento al dom por dentro utilzamos la funcion que genera el template//y utilizamos el content.clone(true) para clonar el elemento//Le ponemos true porque si este fuera false solo tomaria las etiquetas padre y no//Todo lo que tienen por dentro las etiquetasthis.appendChild(this.getTemplate().content.cloneNode(true))}connectedCallback(){//Inicializamos en el dom el metodo de renderthis.render();}}customElements.define('my-element', myElement);
especificidad**
La forma de usar los templates es, crear un código base entre las etiquetas <template>, es decir una plantilla.
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width, initial-scale=1.0"/><title>Template</title></head><body><h2 class="title">Soy un Template</h3><template><div><p>Texto de muestra</p></div></template><script type="module" src="./template.js"></script></body></html>
Luego en JavaScript procedes a clonar el código que esta entre las etiquetas <template>, le adicionar lo que requieres y lo renderizas, ejemplo:
const template =document.querySelector('template').content.querySelector('p');for(let i =1; i <10; i++){let clone = template.cloneNode(true); clone.textContent=`Ciclo: ${i}`;document.body.appendChild(clone);}
En el explorador se ve así:
Soy un TemplateCiclo:1Ciclo:2Ciclo:3Ciclo:4Ciclo:5Ciclo:6Ciclo:7Ciclo:8Ciclo:9
No logro entender cuál es la razón para agregar contenido dentro del tag template en el HTML si al final no lo vamos a usar...
Algo que no he visto comentado por otras personas es la necesidad de clonar el template o el resultado de getTemplates(). Cuando se crea un template, la información se guarda en la propiedad .content, que contiene el #document-fragment. Si no clonamos el template, la información del document-fragment se mueve al DOM y, si vuelves a llamar al template, no se mostrará nada porque el "template" original se ha movido.
Si no se clona, solo podrás llamar al template una vez, ya que el document-fragment se habrá trasladado al DOM y no estará disponible para futuros llamados. En cambio, al clonar el template, podrás llamarlo tantas veces como desees, ya que el document-fragment se mantendrá en la propiedad .content y la plantilla estará siempre disponible.
En el código de esta clase, si no se usa cloneNode(), al volver a llamar a render(), se creará un nuevo template y se añadirá al DOM este nuevo document-fragment. Dado que se crea un nuevo template en cada llamado, no habrá problema. Sin embargo, si intentas hacer esto fuera de una función, como en el código que incluiré a continuación, solo se mostrará una vez porque el document-fragment estará vacío debido a la falta de clonación.
Además, si deseas agregar etiquetas o nodos a tu template utilizando appendChild, debes hacerlo a través de this.template.content.appendChild(), ya que dentro de la propiedad content es donde se encuentra el document-fragment y donde deberías almacenar la estructura que estás creando.
let a =document.createElement("section");a.innerHTML=`<h3>Hola mundo!</h3><div><p>soy mas texto de ejemplo</p></div>`classmyNotificationCustomextendsHTMLElement{constructor(){super();this.template=document.createElement("template");this.template.content.appendChild(a)}render(){this.appendChild(this.template.content);}connectedCallback(){this.render();this.render()this.render();this.render()}}customElements.define("notification-custom",myNotificationCustom);
Fue curioso, intente crear mi template y su contenido con createElement y append pero parece que al hacerlo de esa manera no funciona bien la clonación (segui el proceso y funciono hasta donde se veia en consola pero faltaba clonarlo, al clonarlo ya no funcionaba), usando el template literal si funciono con normalidad y luego me pregunte si tenia que ver con que no inicializamos los nodos en el constructor pero seguia sin funcionar.
Ahora me pregunto si el nombre del "template literal" tendrá que ver con esta etiqueta template y me quede con la duda de porque solo funciona usando innerHTML y no de la otra manera.
quise probar tu escenario y efectivamente no lo agrego y averigue y esta fue la respuesta de porque no sirvio hacerle el append a template.
No se le entiende porque mezcla todo
Recomendación
No se si alguien mas a tenido este problema de que se le duplica el codigo o simplemente no se renderiza en lo particular es muy importante al trabajar con los web components
aperturar y cerrar de forma correcta cada etiqueta
<etiqueta></etiqueta> ya que podemos tener algunos inconvenientes al momento de renderizar
Un tip que lei no me acuerdo donde y me gusto es de cambiar el modo del lenguaje a HTML para escribir el template.innerHTML y que las etiquetas las cree de forma automatica
Para que se usa el template ¿?
Bueno al crear el nodo Template con Js nos sirve para simplemnte tener una base desde donde trabajar, en el ejemplo de la clase Diego genero la semantica dentro de un Section para tener un mejor control semantico y visual. La etiqueta simplente proporciono precisamente esa base para ahi colocar el codigo
La forma en como lo hizo el profesor hace que el código se vea mas limpio, increíble!!
la nueva funcionalidad que permite hacer preguntas en línea mientras estamos en la clase solo funciona con los nuevos cursos? No se activa en los cursos anteriores o hay alguna antiguedad de la clase para que aparezca?
me perdí cual es el punto de crear la etiqueta template dentro del html? porque no la estamos usando desde js, probé el codigo sin poner el template en el html porque no entendía por que era necesario y el resultado fue el mismo.
Esto se usa para poder usar el template junto al fragment. El template es una etiqueta que el navegador no va a renderizar, y que facilita la interaccion con js, ya que en nuestro archivo js solo tendremos que clonarlo y modificar los elementos que necesitamos que sean interactivos. Si no te queda claro, mira el siguiente video donde se explica mejor.
https://youtu.be/bC8Ed2mpewo