пользовательские элементы не устанавливают / не получают атрибуты - PullRequest
0 голосов
/ 24 мая 2018

Если я создаю два пользовательских элемента и создаю один из них внутри другого, атрибуты не работают для новых дочерних элементов первого элемента.

class Bar extends HTMLElement {
  constructor() {
    super();
    const val = this.getAttribute('val') || 'no value';

    const shadow = this.attachShadow({mode: 'open'});
    const wrapper = document.createElement('div');
    wrapper.innerHTML = `
      <div class='bar'>
      	<span>${val}</span>
      </div>
    `;

    shadow.appendChild(wrapper);
  }
}
customElements.define('x-bar', Bar);

class Foo extends HTMLElement {
  constructor() {
    super();
    const loop = this.getAttribute('loop') || 10;

    const shadow = this.attachShadow({mode: 'open'});
    const wrapper = document.createElement('div');
    
    for(let i=0; i<loop; i++){
    	const b = document.createElement('x-bar');
        b.setAttribute('val', `value #${i}`);
      
        wrapper.appendChild(b);
    }

    shadow.appendChild(wrapper);
  }
}
customElements.define('x-foo', Foo);
<x-foo loop='3'></x-foo>

Я ожидаю, что мой результат будет

значение # 0

значение # 1

значение # 2

, так как я установил attr следующим образом b.setAttribute('val', value #${i});

, но я получаю 3x no value

Любые входные данные о том, почему это?и / или как это исправить, спасибо!

Ответы [ 2 ]

0 голосов
/ 24 мая 2018

Большинство людей не знают о правилах конструктора веб-компонентов:

Вот официальные документы:

https://w3c.github.io/webcomponents/spec/custom/#custom-element-conformance

Резюме:

Ваш код конструктора:

  • должен иметь беспараметрический вызов super () в качестве первого оператора в конструкторе.
  • не должен иметьоператор возврата в любом месте конструктора.
  • не должен вызывать document.write () или document.open ().
  • не должен проверять атрибуты элемента.
  • не должен изменять или добавлятьлюбые атрибуты или дочерние элементы.

В общем случае конструктор следует использовать для установки начального состояния и значений по умолчанию, а также для настройки прослушивателей событий и, возможно, теневого корня.

InВ общем, я согласен с @TJ Crowder, но я бы сделал одну незначительную модификацию объекта Bar:

class Bar extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({mode: 'open'});
  }
  
  static get observedAttributes() {
    // Indicate that we want to be notified
    // when the `val` attribute is changed
    return ['val'];
  }
  
  connectedCallback() {
    // Render the initial value
    // when this element is placed into the DOM
    render(this.getAttribute('val'));
  }
  
  attributeChangedCallback(attrName, oldVal, newVal) {
    if (oldVal != newVal) {
      // If the value for the `val` attribute has changed
      // then re-render this element
      render(newVal);
    }
  }  
  
  render(val = 'no value') {
    this.shadowRoot.innerHTML = `
      <div class='bar'>
      	<span>${val}</span>
      </div>
    `;
  }
}

customElements.define('x-bar', Bar);

При этом использовались стандарты attributeChangedCallback и observedAttributes.Хотя переопределение функциональности setAttribute работает, оно не является будущим.Если в будущем API-интерфейс для setAttribute изменится, вам нужно будет не забыть исправить свой компонент.И выполнение этого со многими компонентами увеличивает долги разработчиков.

class Bar extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({mode: 'open'});
    // Render the blank DOM
    this.shadowRoot.innerHTML = '<div class="bar"><span>no value</span><div>';
    this._span = this.shadowRoot.querySelector('span');
  }
  
  static get observedAttributes() {
    // Indicate that we want to be notified
    // when the `val` attribute is changed
    return ['val'];
  }
  
  attributeChangedCallback(attrName, oldVal, newVal) {
    if (oldVal != newVal) {
      // If the value for the `val` attribute has changed
      // then insert the value into the `<span>`
      this._span.textContent = newVal || 'no value';
      // OR: this._span.innerHTML = newVal || 'no value';
      // But make sure someone has not tried to hit you
      // with a script attack.
    }
  }
  
  get val() {
    return this._span.textContent;
  }
  set val(newVal) {
    if (newVal == null || newVal === false || newVal === '') {
      this.removeAttribute('val');
    }
    else {
      this.setAttribute('val', newVal);
    }
  }
}

customElements.define('x-bar', Bar);

Этот второй метод не выполняет рендеринг всего DOM, он просто вставляет измененное значение атрибута в тег <span>.

Он также предоставляет свойстватак что вы можете установить значение через атрибут, а также через JavaScript:

var el = document.querySelector('x-bar');
if (el) {
  el.val = "A New String";
  setTimeout(()=>el.val = '';,2000);
}
0 голосов
/ 24 мая 2018

Вы не устанавливаете атрибут до после , конструктор уже был вызван;обратите внимание на ведение журнала:

class Bar extends HTMLElement {
  constructor() {
    super();
    const val = this.getAttribute('val') || 'no value';
    console.log("In constructor, val = " + val);

    const shadow = this.attachShadow({mode: 'open'});
    const wrapper = document.createElement('div');
    wrapper.innerHTML = `
      <div class='bar'>
      	<span>${val}</span>
      </div>
    `;

    shadow.appendChild(wrapper);
  }
}
customElements.define('x-bar', Bar);

class Foo extends HTMLElement {
  constructor() {
    super();
    const loop = this.getAttribute('loop') || 10;

    const shadow = this.attachShadow({mode: 'open'});
    const wrapper = document.createElement('div');
    
    for(let i=0; i<loop; i++){
      console.log("Constructing...");
    	const b = document.createElement('x-bar');
      console.log("Setting attribute");
        b.setAttribute('val', `value #${i}`);
      
        wrapper.appendChild(b);
    }

    shadow.appendChild(wrapper);
  }
}
customElements.define('x-foo', Foo);
<x-foo loop='3'></x-foo>

Вам нужно будет переместить логику рендеринга из конструктора, чтобы можно было принимать во внимание атрибуты, заданные после построения.Возможно, переопределив setAttribute:

class Bar extends HTMLElement {
  constructor() {
    super();

    const shadow = this.attachShadow({mode: 'open'});
    this.wrapper = document.createElement('div');

    this.render(); // Though calling methods from the constructor isn't ideal
    shadow.appendChild(this.wrapper);
  }
  
  setAttribute(name, value) {
    super.setAttribute(name, value);
    if (name === "val") {
      this.render();
    }
  }
  
  render() {
    const val = this.getAttribute('val') || 'no value';
    this.wrapper.innerHTML = `
      <div class='bar'>
      	<span>${val}</span>
      </div>
    `;
  }
}
customElements.define('x-bar', Bar);

class Foo extends HTMLElement {
  constructor() {
    super();
    const loop = this.getAttribute('loop') || 10;

    const shadow = this.attachShadow({mode: 'open'});
    const wrapper = document.createElement('div');
    
    for(let i=0; i<loop; i++){
      console.log("Constructing...");
    	const b = document.createElement('x-bar');
      console.log("Setting attribute");
        b.setAttribute('val', `value #${i}`);
      
        wrapper.appendChild(b);
    }

    shadow.appendChild(wrapper);
  }
}
customElements.define('x-foo', Foo);
<x-foo loop='3'></x-foo>

Вызов методов из конструктора не идеален, хотя, возможно, вы захотите немного поиграть с этим

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...