A√ļn no tienes acceso a esta clase

Crea una cuenta y contin√ļa viendo este curso

attributeChangedCallback

13/22
Recursos

Aportes 26

Preguntas 6

Ordenar por:

¬ŅQuieres ver m√°s aportes, preguntas y respuestas de la comunidad? Crea una cuenta o inicia sesi√≥n.

Para evitar el error de consola:

Uncaught RangeError: Maximum call stack size exceeded. at HTMLElement.attributeChangedCallback

sería conveniente hacer lo siguiente:

attributeChangedCallback(attr, oldVal, newVal) {
        if (oldVal !== newVal) {
            this[attr] = newVal
        }
    }

Una alternativa más eficiente que se me ocurre para no poner tantos if’s es usar directamente el attr como key para el this, de esta forma:

    attributeChangedCallback(attr, oldVal, newVal) {

        this[attr] = newVal;

    }

Igual funciona ūüėĄ
.
Dejo el código de la clase:
Adicion del attribute changed callback

No usen atributos que ya son de html o le dara error
Maximum call stack size exceeded.

https://developer.mozilla.org/es/docs/Web/HTML/Atributos

Si no requieren hacer algo con con el valor obtenido pueden pasarlo directamente asi

attributeChangedCallback(attr, oldVal, newVal) {
    if (oldVal !== newVal) {
      this[attr] = newVal
    }
  }

Y si quieren modificar algo de ese valor pueden seguir usandolo en el if asi

if (attr === "title" && oldVal !== newVal) {
      this.title = newVal;
}

MY NOTES FOR ATTRIBUTE CHANGED CALLBACK

class myElement extends HTMLElement{
  
  constructor(){
    super();
   
    this.attachShadow({mode:"open"});
  }
    //Utilizaremos un observer, esto estara observando nuestros atributos
      //Es como indicarle al componente cuales son los atributos tendra
        //Si hay algo en otro que no esta en esta lista no es de el componente

    static get observedAttributes(){
      //Aqui le decimos al observador que atributos tendra nuestro componente
        //Si hay otro atributo que no este aqui este no sera de importancia
      return ['title','parrafo','img']

    }
    //Ya teniendo los atributos que seran observados podremos utilizar el
      //attributeChangedCallback
        //Esta funcion recibe tres parametro
          // 1. valor actual
          // 2. valor viejo
          // 3. nuevo valor
   attributeChangedCallback(attr, oldVal, newVal){

    //Aqui vamos a generar los cambios de acuerdo a lo que existe en los atributos

    //aqui hacemos una validacion para verificar si existe el atributo en nuestro componente
      if(attr === "title"){

        //le asignamos a la variable title que obtenemos desde el observador de
          //nuestro componente un nuevo valor 
    //Esto quiere decir que si hay un nuvelo valor se tiene que hacer el cambio de 
      //Forma dinamica
        this.title = newVal;
      }

      if(attr === "parrafo"){

        this.parrafo = newVal;
      }

      if(attr === "img"){

        this.img = newVal;
      }

      

   }
  




  getTemplate(){

    const template = document.createElement('template');

 
    template.innerHTML = `
      <section>

        <h2>${this.title}</h2>
        <div>
          <p>${this.parrafo}</p>
          <img src="${this.img}">
        </div>
      
      </section>

      ${this.getStyles()}

    `;
    return template
  }

  getStyles(){
    return`

      <style>
        h2{

          color:red;
        }
      </style>
    
    `
  }

  render(){

   
    this.shadowRoot.appendChild(this.getTemplate().content.cloneNode(true))

  }

  
  connectedCallback(){

    this.render();
    


  }

}

customElements.define('my-element', myElement);

Cuando agregamos el metodo ‚ÄúobservedAttributes‚ÄĚ y retornamos title como parte del arreglo, reacciona para todos los attributos title del DOM, como se puede corregir para que solo sean los suyos?
Porque retorna un error ‚ÄúUncaught RangeError: Maximum call stack size exceeded‚ÄĚ

Algo que de pronto no queda muy claro en esta clase sobre attributeChangedCallback() es que es un metodo util para mutar o actualizarlas las propiedades del componente en tiempo de ejecucion, una aproximacion a lo que podria ser un state del componente. Encontre este recurso donde se ve un ejemplo (un contador) un poco mas claro

Se me ocurrió esto para cuando tengas muchos parámetros no tener que hacer muchos ifs y solo actualizar el valor del attr si ha cambiado

attributeChangedCallback(attr, oldVal, newVal) {
        oldVal !== newVal ? this[attr] = newVal : null;
    }

Creo que hubo un error de edicion o algo en la clase, no se demuestra que los valores de los atributos cambien en tiempo real.
.

Para lograrlo agregué la siguiente logica, por si a alguien le ayuda:

HTML

<my-element
      title="I'm a title"
      pharagraph="I'm a pharagraph"
      img="https://avatars3.githubusercontent.com/u/1905708?s=280&v=4"
    ></my-element>

En class:

static get observedAttributes() {
      return ["title", "pharagraph", "img"];
    }
attributeChangedCallback(attribute, oldVal, newValue) {
      if (attribute === "title") {
        this.title = newValue;
      }
      if (attribute === "pharagraph") {
        this.pharagraph = newValue;
      }
      if (attribute === "img") {
        this.img = newValue;
      }
    }

Se entendió el concepto pero el video no lo ejemplifica. El valor del atributo no estaá cambiando los el valor dentro del componente, se hizo un trabajo de edición para ocultar este hecho, puntualmente este video me decepcionó como ejemplo

No se si os pasar√° a vosotros tamb√≠en pero yo he tenido que a√Īadir una comprobaci√≥n adicional en attributeChangedCallback ya que sino me hacia un bucle infinito al intentar cambiar el attributo por javascript.
De echo, cuando se renderiza el componente y se le asigna el attributo, se llama a la función attributeChangedCallback y ésta entra en un bucle infinito.

    attributeChangedCallback(attr, oldVal, newVal) {
        if (oldVal === newVal) { return; }
...

Cambie para la imagen el nombre de la variable de ‚Äúimg‚ÄĚ a ‚ÄúimgSrc‚ÄĚ y funciono hasta que llegamos a este ejercicio. Lo regrese y ya sirvio, pero que restricciones hay en cuanto a nombres de variables?

Generalmente no uso Switch case, pero en este caso para mi queda mejor y m’as entendible así:

  attributeChangedCallback(currentValue, oldValue, newValue) {
    switch(currentValue) {
      case 'title':
        this.title = newValue;
        break;
      case 'paragraph':
        this.paragraph = newValue;
        break;
      case 'img':
        this.img = newValue;
        break;
    }
  }

attributeChangedCallback

attributeChangedCallback: Se invoca cada vez que los atributos del elemento se a√Īaden, quitan o cambian. Deben especificarse previamente en el m√©todo est√°tico¬†observedAttributes¬†los atributos que queremos que nos sean notificados.

El callback attributeChangedCallback()  se dispara cuando un atributo cambia y está observándose el atributo. Esto se hace mediante el método static get observedAttributes() dentro de la clase, este debería devolver un array que contiene los nombres de los atributos que se deben observar:

// Agregamos el observador
static get observedAttributes() { return ['attr1', 'attr2', ... 'attrn']; }

// Agregamos el attributeChangedCallback
attributeChangedCallback(attr, oldVal, newVal) {
		// console.log(attr, oldVal, newVal);
		// Si el valor anterior es diferente al nuevo
		if (oldVal !== newVal) {
			// Aquí guardamos cada atributo en la clase del componente
			this[attr] = newVal;
		}
	}

Como podemos ver, al cambiar atributos no se renderizan los cambios, es por eso que he decidido hacer un peque√Īo cambio en el metodo render() para utilizarlo tambi√©n en el attributeChangedCallback()

attributeChangedCallback(attr, oldVal, newVal){
	if(oldVal !== newVal){
		this[attr] = newVal;
	}
	this.render();
}

render() {
	this.shadowRoot.innerHTML = '';
		this.shadowRoot.append(this.getTemplate().content.cloneNode	(true));
}

Aqui dejo un articulo de buenas practicas de Google para complementar https://developers.google.com/web/fundamentals/web-components/best-practices

Pregunta: ¬ŅQu√© acaso no era contraproducente usar callbacks?

Muy interesante lo de cambios de estado. Me quede con jQuery y una breve introducción a React. Es interesante lo que se puede hacer con JS puro. Creo estos cambios de estado es lo que hace tan popular a frameworks como Reacr o Angular

Al hacer el mismo ejemplo del profesor no logré que se actualizara el contenido de forma dinámica, sin tener que recargar todo el componente o la página (que de hecho es lo que él hace al final del video), por lo que modifiqué el método **attributeChangedCallback()**para que se actualizara de forma dinámica.

attributeChangedCallback(name, oldVal, newVal) {
	//Compara si ya existe un valor, si no existe lo agrega al valor al atributo correspondiente
        if (oldVal === null) {
            this[name] = newVal
            return
        }
	//compara el nombre del atributo que ha cambiado y busca el elemento correspondiente para cambiar su contenido
        if (name === "titulo") {
            this.shadowRoot.querySelector('h2').textContent = newVal;
        }
        if (name === "texto") {
            this.shadowRoot.querySelector('p').textContent = newVal;
        }
        if (name === "img") {
            this.shadowRoot.querySelector('img').textContent = newVal;
        }
        
    } 

Esto es necesario porque si bien se ejecuta el método attributeChangedCallback(), no se esta llamando a al método connectedCallback() para volver a renderizar el elemento, ya que este solo se ejecuta cuando se crea el componente.
De esta manera solo se actualizan los datos específicos que uno necesite .

Usar un switch en lugar de los if’s puede ser una buena idea a mi parecer.

La manera que usamos atributos en el tema anterior, funciona. Pero lo correcto es hacer uso del ciclo de vida llamado attributeChangedCallback ya que así nos aseguramos de tener un componente completamente dinámico capaz de cambiar sus valores de acuerdo a las necesidades del desarrollador.
.
En resumen, esto lo logramos a√Īadiendo un static get observedAttributes(), que se√Īala cu√°les son los atributos que se van a estar observando sus cambios mediante un return de un array, y a√Īadiendo el ciclo de vida attributeChangedCallback(), que es donde vamos a definir los nuevos valores a renderizar.

class myElement extends HTMLElement {
  ...
  //observador de los atributos pertenecientes al componente
  static get observedAttributes() {
    return ["cardtitle", "description", "img"];
  }

  // asignar los cambios a los valores
  attributeChangedCallback(attr, oldVal, newVal) {
    this[attr] = newVal;
  }

  getTemplate() {
    ...
    <h2>${this.cardtitle}</h2>
    <div>
    <p>${this.description}</p>
    <img src=${this.img} />
    ...
  }
...
}

customElements.define("my-element", myElement);

Nota: Al momento de a√Īadir y escuchar los cambios de los atributos, se debe tener en cuenta las may√ļsculas. Quiz√° en JavaScript tengamos un atributo llamado cardTitle, pero el navegador al renderizar el atributo, este ser√° llamado cardtitle, todo en min√ļscula, lo que puede ocasionar problemas con la lectura de los valores. Tambi√©n hay que tener cuidado de no utilizar nombres que sean comunes dentro de nuestras etiquetas HTML, por ejemplo title.

Los métodos estáticos en una clase funcionan igual que en Python, Java o C. Son métodos que pertenecen a la clase misma pero no al objeto que se crea con la clase. Si por ejemplo tengo una clase Persona, y a partir de ella creo varios objetos (personas) cada una tendrá sus propios atributos y métodos. Pero por ahí queremos contar cuantas personas hay y para eso no sirven los metodos normales, ya que pertenecen al objeto y no a la clase. ENtonces creamos un método estático, que va a funcionar desde la clase pero al llamarse no va a instanciar una persona, sino que va a trabajar solo desde la clase, contando cuantos objetos hay en vez de iterar sobre un solo objeto

Apuntes y código:

<h4>attributeChangedCallback</h4>

Necesitamos un observer precisamente para estar observando los atributos.

Con esto el componente ya se hace reutilizable.

static get observedAttributes(){
    return ['title' , 'parrafo', 'img']; //*Solamente tomar√° en cuenta estos 3 atributos
  }

  attributeChangedCallback(actualValue, oldValue, newValue){
    //*Si el atributo es igual a la variable,
    //* haz que su contenido sea igual al nuevo valor
    if (oldValue !== newValue) {
      this[actualValue] = newValue;
    }
  }