Веб-компоненты: есть ли эквивалент атрибута ChangedCallback для свойств? - PullRequest
1 голос
/ 01 мая 2019

Вы не должны помещать расширенные данные (объекты, массивы, функции) в атрибуты HTML-элементов.Вместо этого предлагается размещать в свойствах только расширенные данные (в соответствии с статьей о лучших практиках пользовательских элементов Google ).Мне нужно выполнить действия, когда эти свойства обновляются.У нас есть observedAttributes и attributeChangedCallback, но нет ничего похожего по свойствам.

Допустим, у меня есть user опора с такими вещами, как name, DoB и address.Я подумал, что смогу обмануть observedAttributes, поставив установщик койки а-ля

set user(val) {
  return;
}

Не сработало.return this.user = val дает бесконечный цикл.

Моя единственная идея на данный момент - иметь свойство с именем _user, которое просто устанавливается равным [Object object] при каждом изменении, которое запускает изменение, которое я на самом деле хочу.Хотя мне это не очень нравится.

ОБНОВЛЕНИЕ : Это то, чем я сейчас занимаюсь

В user-info.js:

class UserInfo extends HTMLElement {
  connectedCallback() {
    subscribers.push({ element: this, props: ['user'] });
    this._user = state.user;
    this.render();
  }
  static get observedAttributes() {
    return ['user'];
  }
  attributeChangedCallback(name, oldValue, newValue) {
    this.render();
  }
  get user() {
    return this._user;
  }
  set user(val) {
    if (JSON.stringify(val) !== JSON.stringify(this._user)) {
      this._user = val;
      return this.setAttribute('user', val);
    }
  }
  render() {
    this.innerHTML = `<span>${this._user.name}</span> was born on <span>${this._user.dob}</span>`;
  }
}

В main.js:

document.querySelector('.actions--user').addEventListener('input', e => {
  state.user = {...state.user, [e.target.dataset.action]: e.target.value};
})

1 Ответ

2 голосов
/ 01 мая 2019

Вы можете использовать Proxy для обнаружения обновленных свойств объекта.

customElements.define( 'user-info', class extends HTMLElement {
  connectedCallback() {
    this._user = {
        name: 'Bruno',
        dob: '1/1/2000'
    }
    this.render();
    this._proxy = new Proxy( this._user, {
        set: ( obj, prop, val ) => {
            if ( prop === 'name' ) 
                if ( this._user.name !== val ) {
                    console.log( 'username updated to ' + val )
                    this._user.name = val
                    this.render()
                }
        }
    } )
  }
  get user() {
    return this._proxy
  }
  set user(val) {
    if (JSON.stringify(val) !== JSON.stringify(this._user)) {
      this._user = val
      this.render()
    }
  }
  render() {
    this.innerHTML = `<span>${this._user.name}</span> was born on <span>${this._user.dob}</span>`
  }
} )
<user-info id=ui></user-info><br>
<label>Name: <input oninput="ui.user.name=this.value"></label>

В качестве альтернативы вы можете определить пользователя object / class с установщиками, которые будут взаимодействовать с пользовательским элементом.

class User {
  constructor( elem ) {
    this._elem = elem
    this._name = 'Bruno'
    this._dob = '1/1/2000'
  }
  set name( val ) {
    if ( val !== this._name ) {
      this._name = val
      this._elem.render()
    }
    return false
  }
  get name() {
    return this._name
  }
  get dob() {
    return this._dob
  }
  update( obj ) {
    this._name = obj.name
    this._dob = obj.dob
  }
}

class UserInfo extends HTMLElement {
  connectedCallback() {
    this._user = new User( this )
    this.render()
  }
  get user() {
    return this._user
  }
  set user(val) {
    this._user.update( val )
    this.render()
  }
  render() {
    this.innerHTML = `<span>${this._user.name}</span> was born on <span>${this._user.dob}</span>`
  }
}

customElements.define( 'user-info', UserInfo )
<user-info id=ui></user-info><br>
<label>Name: <input oninput="ui.user.name=this.value"></label>
...