Polymer 2 - 每次通过 iron-pages/iron-selector 显示元素时执行操作

Polymer 2 - Perform action every time element is shown via iron-pages/iron-selector

我正在尝试创建一个注销页面,该页面即使在将该元素附加到 DOM 后也能正常工作。当您登录、注销、再次登录并尝试重新注销时,会发生这种情况。

例如,shell 有

<iron-selector selected="[[page]]" attr-for-selected="name">
  <a name="logout" href="[[rootPath]]logout">
    <paper-icon-button icon="my-icons:sign-out" title="Logout" hidden$="[[!loggedIn]]"></paper-icon-button>
  </a>
  <a name="login" href="[[rootPath]]login">
    <paper-icon-button icon="my-icons:sign-in" title="Login" hidden$="[[loggedIn]]"></paper-icon-button>
  </a>
</iron-selector>

<<SNIP>>

<iron-pages selected="[[page]]" attr-for-selected="name" fallback-selection="view404" role="main">
  <my-search name="search"></my-search>
  <my-login name="login"></my-login>
  <my-logout name="logout"></my-logout>
  <my-view404 name="view404"></my-view404>
</iron-pages>

我在 shell:

中也有一个页面更改观察员
static get observers() {
  return [
    '_routePageChanged(routeData.page)',
  ];
}

_routePageChanged(page) {
  this.loggedIn = MyApp._computeLogin();
  if (this.loggedIn) {
    this.page = page || 'search';
  } else {
    window.history.pushState({}, 'Login', '/login');
    window.dispatchEvent(new CustomEvent('location-changed'));
    sessionStorage.clear();
    this.page = 'login';
  }
}

这很好用,因为当我单击图标注销时,它会很好地附加 my-logout 元素并执行 ready()connectedCallback() 中的操作。

my-logout

ready() {
  super.ready();
  this._performLogout();
}

当您重新登录并尝试再次注销时,如果没有刷新浏览器并导致 DOM 刷新,就会出现问题。由于 DOM 从未被清除,my-logout 仍然存在,所以 ready()connectedCallback() 都不会触发。

我已经想出了解决这个问题的方法,但感觉很笨拙。基本上,我可以将事件侦听器添加到选择图标时将执行this._performLogout();的元素:

ready() {
  super.ready();
  this._performLogout();

  document.addEventListener('iron-select', (event) => {
    if (event.detail.item === this) {
      this._performLogout();
    }
  });
}

就像我说的,它有效,但我不喜欢有一个全局事件监听器,而且我必须在元素第一次附加时调用注销函数我必须听因为监听器直到第一次附加元素后才处于活动状态。

似乎没有 "one size fits all" 解决方案。核心问题是,"Do you want the parent to tell the child, or for the child to listen on the parent?"。如果你想听 parent,我在问题中提出的 "answer" 是可行的,但因为我不喜欢全局事件监听器的想法,下面是如何使用 <iron-pages> 告诉 一个 child 元素它已被选中进行查看。

我们将selected-attribute属性添加到<iron-pages>

<iron-pages selected="[[page]]" attr-for-selected="name" selected-attribute="selected" fallback-selection="view404" role="main">
  <my-search name="search"></my-search>
  <my-login name="login"></my-login>
  <my-logout name="logout"></my-logout>
  <my-view404 name="view404"></my-view404>
</iron-pages>

是的,考虑到 attr-for-selected 属性,这看起来有点混乱。 attr-for-selected 说,"What attribute should I match on these child elements with the value of my selected property?" 所以当我点击

<iron-selector selected="[[page]]" attr-for-selected="name">
  <a name="logout" href="[[rootPath]]logout"><paper-icon-button icon="my-icons:sign-out" title="Logout" hidden$="[[!loggedIn]]"></paper-icon-button></a>
</iron-selector>

它会在内部将<my-logout>设置为选中的元素并显示它。 selected-attribute="selected" 所做的是在 child 元素 上设置属性 。如果您查看浏览器 JS 控制台,您会看到该元素现在看起来像

<my-login name="login"></my-logout>
<my-logout name="login" class="iron-selected" selected></my-logout>

我们可以在 <my-logout> 元素中定义一个观察者来检查变化

static get properties() {
  return {
    // Other properties
    selected: {
      type: Boolean,
      value: false,
      observer: '_selectedChanged',
    },
  };
}

_selectedChanged(selected) {
  if (selected) {
    this._performLogout();
  }
}

if 语句是为了让我们只在显示时触发逻辑,而不是在我们离开时触发逻辑。这样做的一个好处是我们不关心该元素是否已经附加到 DOM 或没有。当 <iron-selector>/<iron-pages> 第一次选择 <my-logout> 时,属性被设置,元素附加,观察者触发,观察者看到 selected 现在是 true(相对于定义的 false) 并运行逻辑。