No tienes acceso a esta clase

隆Contin煤a aprendiendo! 脷nete y comienza a potenciar tu carrera

Atributos en nuestro componente

20/22
Recursos

Aportes 17

Preguntas 6

Ordenar por:

驴Quieres ver m谩s aportes, preguntas y respuestas de la comunidad?

o inicia sesi贸n.

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 鈥渢itle鈥 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.

Hola les dejo el link del proyecto un poco personalizado y los aportes de varios de ustedes en el redme Nike

Espero les sirva 馃槂

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.

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);

Que genial 馃槂