No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Atributos en nuestro componente

20/22
Recursos

Aportes 15

Preguntas 6

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

Si observaron la consola, lo más probable es que les haya salido este error

Uncaught RangeError: Maximum call stack size exceeded.
    at HTMLElement.attributeChangedCallback (product-card.js:16)
    at HTMLElement.attributeChangedCallback (product-card.js:16)
    at HTMLElement.attributeChangedCallback (product-card.js:16)
    at HTMLElement.attributeChangedCallback (product-card.js:16)
    at HTMLElement.attributeChangedCallback (product-card.js:16)
    at HTMLElement.attributeChangedCallback (product-card.js:16)
    at HTMLElement.attributeChangedCallback (product-card.js:16)
    at HTMLElement.attributeChangedCallback (product-card.js:16)
    at HTMLElement.attributeChangedCallback (product-card.js:16)
    at HTMLElement.attributeChangedCallback (product-card.js:16)

Esto se debe a que estamos utilizando el atributo “title” y este ya es un atributo nativo de HTML por lo que la solución es simplemente cambiar el nombre de nuestro atributo.

Para ahorrarse hacer muchos if pueden hacer esto:

    static get observedAttribute() {
        return ['img', 'name', 'price', 'description', 'colllection'];
    }
    attributeChangeCallback(attr, oldValue, newVal) {
        const attributes = productCard.observedAttribute;
        if (attributes.includes(attr)) {
            this[attr] = newVal;
        }
    }

Si queremos agregar otro atributo al componente solo sería agregarlo al observedAttribute y no tener que añadir otra condición.

No hay link para los retos 🙋🏽‍♂️

Aquí están, tranquilo, sé que los estabas buscando

    getStyles() {
        return `
            <style>
            :host{
                --primary-bg: #5a6cb2;
                width: 90%;
                max-width: 900px;
                min-width: 280px;
            }
            
            .container {
                position: relative;
                display: flex;
                flex-wrap: wrap;
                justify-content: space-between;
                width: 900px;
                height: 600px;
                margin: 20px;
                background-color: #fff;
            }
            
            .container .imgBox{
                position: relative;
                display: flex;
                justify-content: center;
                width: 50%;
                height: 100%;
                background-color: var(--primary-bg);
            }
            
            .container .imgBox:before {
                position: absolute;
                top: 20px;
                left: 20px;
                font-size: 3em;
                font-weight: 800;
                color: #000;
                content: "Nike";
                opacity: 0.1;
            }
            
            .container .imgBox img{
                position: relative;
                top: 100px;
                left: -50px;
                width: 720px;
                height: 480px;
                transform: rotate(-30deg);
            }
            
            .container .details{
                display: flex;
                justify-content: center;
                align-items: center;
                width: 50%;
                height: 100%;
                box-sizing: border-box;
                padding: 80px;
            }
            
            .container .details h2{
                margin-bottom: 25px;
                font-size: 2.5em;
                line-height: 0.8em;
                color: #444;
            }

            .container .details button{
                margin-top: 35px;
                float: right;
                padding: 15px 20px;
                font-size: 16px;
                color: #fff;
                letter-spacing: 1px;
                font-weight: 600;
                text-transform: uppercase;
                border-radius: 40px;
                background-color: #5a6cb2;
                cursor: pointer;
            }

            @media (max-width: 1023px) {
                .container{
                    height: auto;
                    width: auto;
                }

                .container .imgBox{
                    padding: 40px;
                    width: 100%;
                    box-sizing: border-box;
                    height: auto;
                    text-align: center;
                }

                .container .imgBox img{
                    width: 430px;
                    top:80px;
                    height: auto;
                    transform: rotate(-38deg);
                }

                .container .details{
                    width:100%;
                    height: auto;
                    padding: 20px;
                }

                .container .details p{
                    max-width: 100%;
                    margin-left: 0;
                }
            }
            </style>
        `

Hecho

Les comparto el código del componente al inicio de la clase:

class productCard extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: "open" });
  }
  getTemplate(){
    const template = document.createElement("template");
    template.innerHTML = `
    <main class="container">
      <section class="imgBox">
        <img src="./imgs/nike-blue.png" alt="Zapatos deportivos para correr color azul"/>
      </section>
      <section class="details">
        <div class="content">
          <h2>Hola mundo</h2>
          <p>Lorem, ipsum dolor sit amet consectetur adipisicing elit. Assumenda quod deserunt itaque alias delectus earum, quis repellat veritatis ad temporibus ex recusandae quasi rem! Cumque sequi rem nam vero saepe!</p>
          <h3>$300 USD</h3>
          <button>Comprar</button>
        </div>
      </section>
    </main>
    ${this.getStyles()}
    `;
    return template;
  }
  getStyles() {
    return `
      <style>
      :host {
        --primary-background: #5a6cb2;
          width: 80%;
          max-width: 900px;
          min-width: 280px;
          margin: 0 auto;
      }
      .container {
          position: relative;
          display: flex;
          flex-wrap: wrap;
          justify-content: space-between;
          width: 900px;
          height: 600px;
          margin: 20px;
          background-color: #fff;
      }
      .container .imgBox {
          position: relative;
          display: flex;
          justify-content: center;
          width: 50%;
          height: 100%;
          background-color: var(--primary-background)
      }
      .container .imgBox:before {
          position: absolute;
          top: 20px;
          left: 20px;
          font-size: 8em;
          font-weight: 800;
          color: #000;
          content: 'Nike';
          opacity: 0.1;
      }
      .container .imgBox img {
          position: relative;
          top: 100px;
          left: -50px;
          width: 720px;
          height: 480px;
          transform: rotate(-30deg);
      }
      .container .details {
          display: flex;
          justify-content: center;
          align-items: center;
          width: 50%;
          height: 100%;
          box-sizing: border-box;
          padding: 40px;

      }
      .container .details h2 {
          margin-bottom: 25px;
          font-size: 2.5em;
          line-height: 0.8em;
          color: #444;
      }
      .container .details h2 span {
          font-size: 0.4em;
          text-transform: uppercase;
          letter-spacing: 2px;
          color: #999;
      }
      .container .details p {
          max-width: 85%;
          margin-left: 15%;
          margin-bottom: 35px;
          color: #333;
          font-size: 15px;
      }
      .container .details h3 {
          float: left;
          font-size: 2.5em;
          color: #a2a2a2;
      }
      .container .details button {
          margin-top: 35px;
          float: right;
          padding: 15px 20px;
          font-size: 16px;
          color: #fff;
          letter-spacing: 1px;
          font-weight: 600;
          text-transform: uppercase;
          border-radius: 40px;
          background-color: #5a6cb2;
          cursor: pointer;
      }
      @media (max-width: 1080px) {
          .container {
              height: auto;
              width: auto;
          }
          .container .imgBox {
              padding: 40px;
              width: 100%;
              box-sizing: border-box;
              height: auto;
              text-align: center;
          }
          .container .imgBox img {
              left: initial;
              width: 100%;
              height: auto;
              transform: rotate(0deg);
          }

          .container .details {
              width: 100%;
              height: auto;
              padding: 20px;
          }

          .container .details p {
              max-width: 100%;
              margin-left: 0;
          }
      }
      </style>
    `;
  }
  render(){
    this.shadowRoot.appendChild(this.getTemplate().content.cloneNode(true));
  }
  connectedCallback(){
    this.render();
  }
}

customElements.define("product-card", productCard);

Como en nuestro componente de proyecto solo estamos recibiendo las propiedades desde el html, lo hice de una manera mas simple, pero igualmente valida, simplemente obteniendo los atributos de la data que necesito en el constructor, y funciona bien para el resultado que esperamos.

Que genial 😃

Creo que seria mejor si lo hiciéramos desde 0

Hey!, al momento de utilizar atributos nativos de html para nuestro componente se suele generar este error en la consola como lo muestran algunos compañeros en otros comentarios

Uncaught RangeError: Maximum call stack size exceeded.

si bien se puede cambiar simplemente el atributo por otro En este enlace pueden encontrar algunas explicaciones de porque se genera ese problema y como solucionarlo 😉

Deje el link de la rama equivocada este es el bueno Nike

Aquí les dejo una manera para importar los estilos desde una hoja de estilos. para esto se debe tener conceptos de asincronismo. el método lo llame loadStyles ```js class MyComponent extends HTMLElement { constructor() { // Aquí se pueden inicializar las propiedades y el estado del componente super(); this.attachShadow({ mode: "open" }); } connectedCallback() { // Aquí se pueden agregar los elementos HTML y la lógica de inicialización del componente this.render(); } disconnectedCallback() { // Aquí se pueden realizar tareas de limpieza y eliminación de elementos HTML console.log("El componente ha sido eliminado del DOM"); } static get observedAttributes() { // Aquí se pueden definir los atributos que se quieren observar y reaccionar a cambios en ellos return ["titulo", "parrafo"]; } attributeChangedCallback(attr, oldValue, newValue) { // Aquí se puede definir la lógica que se quiere ejecutar cuando un atributo observado cambia if (attr === "titulo") { this.titulo = newValue; } if (attr === "parrafo") { this.parrafo = newValue; } } getTemplate() { const template = document.createElement("template"); template.innerHTML = ` <section>

${this.titulo}

${this.parrafo}

<slot name="texto1"></slot> <slot name="texto2"></slot> </section>`; return template; } async loadStyles() { const res = await fetch("./my-component.css"); const css = await res.text(); const style = document.createElement("style"); style.textContent = css; this.shadowRoot.appendChild(style); } render() { // Aquí se pueden agregar los elementos HTML y la lógica de inicialización del componente this.shadowRoot.appendChild(this.getTemplate().content.cloneNode(true)); this.loadStyles(); } // Aquí se pueden definir otros métodos y propiedades del componente } customElements.define("my-component", MyComponent); ```

Pueden utilizar este codigo para no tener que hacer tantos if, sobre todo si la cantidad de dependencia va aumentando

attributeChangedCallback(attr, oldValue, newValue) {
        const attrValidation = {
            img: () => this.img = newValue,
            name: () => this.name = newValue,
            price: () => this.price = newValue,
            description: () => this.description = newValue,
            collection: () => this.collection = newValue,
        }
        attrValidation[attr]()
    }    

dejo mi aporte pues estuve dando vueltas a los tributos y me encontré con lo siguiente:
- deben nombrarse diferente a etiquetas HTML
-no deben contener caracteres en mayúsculas
-no deben contener numeros
espero te sirva

Les dejo mi v:

class CardComponent extends HTMLElement {

    constructor() {
        super();
        this.attachShadow({ mode: "open" });
    }

    connectedCallback() {
        this.render();
    }

    attributeChangedCallback(attr, oldValue, newValue) {
        if (newValue !== oldValue) {
            this[attr] = newValue;
        }
    }

    render() {
        this.shadowRoot.append(this.templates.content.cloneNode(true));
    }

    get templates() {
        const container = document.createElement('template');
        container.innerHTML = `
            <div class="card-container">
                <section class="card-container__img-content">
                    <h2>${this.mark}</h2>
                    <img src="${this.img}" alt="image product ${this.mark}">
                </section>
                <section class="card-container__main-content">
                    <h3>${this.product} <span>${this.category}</span></h3>
                    <h4>${this.title}</h4>
                    <p>${this.text}</p>
                </section>
                <section class="card-container__footer-content">
                    <h3>${this.price}</h3>
                    <button type="button">${this.button}</button>
                </section>
            </div>
            
            ${this.styles}
        `;
        return container;
    }

    get styles() {
        return `
            <style> 
            
            :host {
                --background-color-main-content: #758cf1;;
                --text-color-img-content: #00000054;
                --title-color-main-content: grey;
                --button-color-footer-content: white;
                --font-family: sans-serif, "Roboto Light";
                width: 100%;
                height: 100%;
                font-family: var(--font-family);
            }
            :host .card-container {
                width: 100%;
                height: 100%;
                display: grid;
                position: relative;
                grid-template-columns: 1fr;
                grid-template-rows: 250px auto 100px;
            }
            :host .card-container__img-content {
                width: 100%;
                height: 250px;
                background-color: var(--background-color-main-content);
            }
            :host .card-container__img-content h2 {
                color: var(--text-color-img-content);
                font-size: 100px;
                margin: 0;
                padding: 15px;
            }
            :host .card-container__img-content img {
                width: 100%;
                height: 250px;
                position: absolute;
                transform: translateY(-85px);
            }     
            :host .card-container__main-content {
                width: 100%;
            }
            :host .card-container__main-content h3 {
                font-size: 25px;
                font-weight: bold;
                margin: 30px 20px 10px 20px;
            }
            :host .card-container__main-content span {
                font-size: 15px;
                text-transform: uppercase;
                color: var(--title-color-main-content);
            }
            :host .card-container__main-content h4 {
                text-transform: uppercase;
                color: var(--title-color-main-content);
                margin: 0 10px 10px 20px;
                font-size: 15px;
            }
            :host .card-container__main-content p {
                padding: 0 20px 0 20px;
                margin-bottom: 0;
                font-size: 18px;
                line-height: 1.4;
                font-weight: 100;
            }
            :host .card-container__footer-content {
                    width: 100%;
                    height: 80px;
                    display: flex;
                    align-content: center;
                    justify-content: space-between;
                    align-items: center;
            }
            :host .card-container__footer-content h3 {
                color: var(--title-color-main-content);
                font-size: 30px;
                margin: 20px;
            }
            :host .card-container__footer-content button {
                margin-right: 20px;
                display: flex;
                color: var(--button-color-footer-content);
                background-color: var(--background-color-main-content);
                border-radius: 15px;
                width: 100px;
                height: 40px;
                align-items: center;
                justify-content: center;
                align-self: center;
                border: 1px solid transparent;
                font-weight: bold;
                font-size: 15px;
            }
            @media (min-width: 800px) {
                :host .card-container {
                    height: auto;
                    grid-template-columns: 1fr 1fr;
                    grid-template-rows: 1fr 1fr;
                }
                :host .card-container__img-content {
                    height: auto;
                    grid-column: 1 / 2;
                    grid-row: 1 / 3;
                }
                :host .card-container__img-content img {
                    width: 700px;
                    height: 400px;
                    transform: translate(-145px, -50px) rotate(330deg);
                }
                :host .card-container__main-content {
                    grid-column: 2 / 3;
                    grid-row: 1 / 2;
                }
                :host .card-container__main-content h3 {
                    margin: 30px 20px 10px 10px;
                }
                :host .card-container__footer-content {
                    height: 200px;
                    grid-column: 2 / 3;
                    grid-row: 2 / 3;
                }
                :host .card-container__footer-content h3 {
                    font-size: 40px;
                }
                :host .card-container__footer-content button {
                    width: 130px;
                    height: 50px;
                    font-size: 20px;
                    margin-right: 40px;
                }
               
            } 
            </style>
        `;
    }

    static get observedAttributes() {
        return ['mark', 'img', 'product', 'category', 'title', 'text', 'button', 'price'];
    }


}

customElements.define('card-component', CardComponent);