从 DOM 中删除并再次插入后如何更新聚合物元素绑定

Posted

技术标签:

【中文标题】从 DOM 中删除并再次插入后如何更新聚合物元素绑定【英文标题】:How to update polymer element bindings after it was removed from DOM and inserted again 【发布时间】:2014-01-15 07:41:33 【问题描述】:

假设我们有容器聚合物元素和另一个虚拟聚合物元素。容器聚合物元素具有用于插入其他聚合物的 div 块。

container_polymer.html

<polymer-element name='container-polymer'>
  <template>
    <div id="container">
    </div>
    <button on-click="first">show first</button>
    <button on-click="firstPrepared">show first prepared</button>
    <button on-click="second">show second</button>
  </template>
  <script type="application/dart" src="container_polymer.dart">
  </script>
</polymer-element>

有三个用于插入虚拟聚合物的按钮:

    第一个按钮将第一个虚拟聚合物插入容器。 第二个按钮也插入第一个虚拟聚合物,并调用 prepareElement() 在这种聚合物上。 第三个按钮将第二个虚拟聚合物插入容器中

container_polymer.dart

import 'package:polymer/polymer.dart';
import 'dart:html';
import 'dummy_polymer.dart';

@CustomTag('container-polymer')
class ContainerPolymer extends PolymerElement 
  PolymerElement firstPolymer, secondPolymer, currentPolymer;
  Element container;

  ContainerPolymer.created() : super.created();

  void enteredView() 
    super.enteredView();
    container = $['container'];
  

  void first(Event e, var detail, Node target) 
    showFirst(false);
  

  void firstPrepared(Event e, var detail, Node target) 
    showFirst(true);
  

  void showFirst(bool prepare) 
    if (firstPolymer == null) 
      DummyPolymer dummyPolymer = new Element.tag("dummy-polymer");
      dummyPolymer.title = "first";
      firstPolymer = dummyPolymer;
    

    if (currentPolymer != firstPolymer) 
      if (secondPolymer != null) 
        secondPolymer.remove();
      

      if (prepare) 
        firstPolymer.prepareElement();  
      
      currentPolymer = firstPolymer;
      container.children.add(firstPolymer); 
    
  

  void second(Event e, var detail, Node target)
    if (currentPolymer != secondPolymer) 
      DummyPolymer dummyPolymer = new Element.tag("dummy-polymer");
      dummyPolymer.title = "second";
      secondPolymer = dummyPolymer;
      if (firstPolymer != null) 
        firstPolymer.remove();
      

      currentPolymer = secondPolymer;
      container.children.add(secondPolymer);
    
  


虚拟聚合物具有几个可观察到的特性,可用于测试粘合效果。当您在该聚合物内部单击时,它会更改根 div 的背景颜色、标题 div 的背景颜色并增加计数器。它还具有用于检测聚合物元素状态是否已更改的输入,并带有白色背景的块以测试父样式的使用情况。

dummy_polymer.html

<polymer-element name='dummy-polymer'>
  <template>
    <div style="width: 500px; height: 300px; background-color: color" on-click="changeColor">
      <div id="title">
        <h1>title</h1>
        <span>Clicks: clicks</span>
      </div>
      <input type="text" />
      <div class="external">Parent style block: background should be white</div>
    </div>

  </template>

  <script type="application/dart" src="dummy_polymer.dart">
  </script>

</polymer-element>

dummy_polymer.dart

import 'package:polymer/polymer.dart';
import 'dart:html';

@CustomTag('dummy-polymer')
class DummyPolymer extends PolymerElement 
  @observable String color = "red";
  @observable String title;
  @observable num clicks = 0;
  Element titleElement;

  DummyPolymer.created() : super.created() 
    var root = getShadowRoot('dummy-polymer');
    root.applyAuthorStyles = true;
  

  void enteredView() 
    super.enteredView();
    titleElement = $['title'];
  

  void changeColor(Event e, var detail, Node target)
    clicks++;

    if (color == "red") 
      color = "green";
     
    else if (color == "green") 
      color = "blue";
    
    else 
      color = "red";
    
    titleElement.style.backgroundColor = color;
  


测试页面托管在这里http://dart-style-binding-test.herokuapp.com/

所以,要重现我的问题,请执行以下操作:

    单击“首先显示”按钮。确保单击内部会更改背景颜色并增加计数器。 在输入字段中输入内容 点击“显示第二个” 单击“首先显示”。确保第一个聚合物具有与以前相同的状态:背景颜色、计数器和输入字段文本。 在第一个聚合物内单击。现在它不会改变背景颜色和计数器,但会改变顶部区域的背景颜色。

绑定到可观察属性不再起作用。但是on-click 处理程序有效,您可以在顶部区域的背景颜色发生变化时看到它。它是通过直接改变元素的backgroundColor属性来实现的:

titleElement.style.backgroundColor = color;

那么,我的问题是:如何在现有聚合物再次插入 DOM 后正确更新绑定机制?

有一种方法可以使用“显示第一个准备好的”按钮来处理它。在插入容器之前,它会在第一个元素上调用 prepareElement()。因此,请执行以下操作:

    单击“首先显示”按钮。确保单击内部会更改背景颜色并增加计数器。 在输入字段中输入内容 点击“显示第二个” 单击“显示首先准备好的”。您可以看到输入元素为空,但计数器和背景颜色是最新的。 (如果您在 dartium 中运行此演示,白色块也会将其背景颜色更改为父样式,因为父样式未应用) 在第一个聚合物内单击。现在它改变了背景颜色和计数器。绑定正常工作,但我们在输入字段中丢失了文本。

注意:在使用“显示第一个准备”按钮插入第一个元素后,即使在使用“显示第一个”按钮插入第一个元素后,绑定也能正常工作。

代码在这里https://github.com/petalvlad/dart-style-binding-test

【问题讨论】:

【参考方案1】:

我自己没有尝试过,但它似乎是合理的。 希望作者不介意回复Detached observables when re-using element.

查看了 polymer.js 信息位后,我发现有一个 cancelUnbindAll 函数,必须在创建元素或将 preventDispose 属性设置为 true 时调用该函数。

对于任何可能需要这样做的人,在 Dart 实现中,您必须在 super 调用之后在分离的函数中调用 cancelUnbindAll,如下所示:

void detached()

    super.detached();
    this.cancelUnbindAll(preventCascade: true);

或者,您可以简单地覆盖自定义元素中的 preventDispose 属性:

bool get preventDispose => true;

【讨论】:

以上是关于从 DOM 中删除并再次插入后如何更新聚合物元素绑定的主要内容,如果未能解决你的问题,请参考以下文章

角度 5 - 如何从 dom 中删除动态添加的组件

Angular - 如何从 DOM 中删除我使用过 $compile 的元素?

如何使用一个按钮切换 IMG 删除和附加?

当复选框 true 或 false 时从 DOM 中删除并重新添加

如何获取元素的 xPath,并再次从 xPath 检索元素

从服务器同步时如何构建本地数据库以删除行