Skip to content
This repository was archived by the owner on Dec 19, 2017. It is now read-only.

Custom element's 'created' constructor called twice #248

Closed
DartBot opened this issue Jun 5, 2015 · 9 comments
Closed

Custom element's 'created' constructor called twice #248

DartBot opened this issue Jun 5, 2015 · 9 comments

Comments

@DartBot
Copy link

DartBot commented Jun 5, 2015

<img src="https://avatars.githubusercontent.com/u/3486855?v=3" align="left" width="96" height="96"hspace="10"> Issue by almstrand
Originally opened as dart-lang/sdk#20197


The following is the minimal code I came up with which demonstrates the problem.

pubspec.yaml
=============================

name: myapp
dependencies:
  polymer: ">=0.12.0-dev <0.12.0"
transformers:

  • polymer:
        entry_points: web/index.html

web/index.html:
=============================
<!DOCTYPE html>
<html>
<head>
    <title>index</title>
    <script src="packages/web_components/platform.js"></script>
    <script src="packages/web_components/dart_support.js"></script>
</head>
<body>
<link rel="import" href="packages/polymer/polymer.html">

<!-- parent element -->
<polymer-element name="my-parent">
  <script type="application/dart">
    import 'package:polymer/polymer.dart';
    @­CustomTag("my-parent")
    class MyParent extends PolymerElement {
      MyParent.created() : super.created() {
        print("parent created");
      }
    }
  </script>
</polymer-element>

<!-- child element -->
<polymer-element name="my-child" extends="my-parent">
  <script type="application/dart">
    import 'package:polymer/polymer.dart';
    @­CustomTag("my-child")
    class MyParent extends PolymerElement {
      MyParent.created() : super.created() {
        print("child created");
      }
    }
  </script>
</polymer-element>

<my-parent>parent elment</my-parent>
<my-child>child element</my-child>

<script type="application/dart">export 'package:polymer/init.dart';</script>
</body>
</html>

Expected console log:

  parent created (:1)
  child created (:1)

Actual console log:

  parent created (:1)
  parent created (http://127.0.0.1:8080/packages/polymer/src/js/polymer/polymer.js:12)
  child created (:1)

Reproduced in:

  Dart SDK version: 1.5.3
  Polymer package version: 0.12.0-dev
  Operating system: Ubuntu 14.04 64-bit
  Browser (if any): Dartium, version 36.0.1985.97 (280598)

@DartBot
Copy link
Author

DartBot commented Jun 5, 2015

<img src="https://avatars.githubusercontent.com/u/2049220?v=3" align="left" width="48" height="48"hspace="10"> Comment by sigmundch


Thanks for the bug report and the nice repro instructions!

Sounds like this is a duplicate of issue dart-lang/sdk#15844. I'll make a note in that bug to try to use the instructions you provide here to investigate further.


Added Duplicate label.
Marked as being merged into dart-lang/sdk#15844.

@DartBot
Copy link
Author

DartBot commented Jun 5, 2015

<img src="https://avatars.githubusercontent.com/u/2152569?v=3" align="left" width="48" height="48"hspace="10"> Comment by blois


I cannot repro 15844 but can repro this bug, so unlinking.


cc @sigmundch.
Added Triaged label.
Marked as being merged into #.

@DartBot
Copy link
Author

DartBot commented Jun 5, 2015

<img src="https://avatars.githubusercontent.com/u/2152569?v=3" align="left" width="48" height="48"hspace="10"> Comment by blois


The root of the issue appears to be that the child element class (confusingly named MyParent in the repro, but the intention is clear) extends from PolymerElement, but has an extends attribute for 'my-parent'.

document.registerElement validates the extends tag, but since that only gets the native element name, it is passed null for the extends tag (native element is HtmlElement).

The second parent creation is occurring within Polymer.js during registration of the my-child element.

@DartBot
Copy link
Author

DartBot commented Jun 5, 2015

<img src="https://avatars.githubusercontent.com/u/3486855?v=3" align="left" width="48" height="48"hspace="10"> Comment by almstrand


The initial sample code is updated below to:

  1. correct the naming of the child element class: MyParent -> MyChild, and
  2. break out the Dart and Web component code into separate files to work-around the following (possibly irrelevant) console warning:

warning: more than one Dart script tag in http://localhost:8080/index.html. Dartium currently only allows a single Dart script tag per document.

pubspec.yaml
=============================

    name: myapp
    dependencies:
    polymer: ">=0.12.0-dev <0.12.0"
    transformers:
    - polymer:
        entry_points: web/index.html

web/index.html
=============================

    <!DOCTYPE html>
    <html>
    <head>
        <title>index</title>
        <script src="packages/web_components/platform.js"></script>
        <script src="packages/web_components/dart_support.js"></script>
    </head>
    <body>
    <link rel="import" href="my-parent.html">
    <link rel="import" href="my-child.html">

    <my-parent>parent element</my-parent>
    <my-child>child element</my-child>

    <script type="application/dart">export 'package:polymer/init.dart';</script>
    </body>
    </html>

web/my-parent.html
=============================

    <link rel="import" href="packages/polymer/polymer.html">
    <polymer-element name="my-parent">
      <script type="application/dart" src="MyParent.dart"></script>
    </polymer-element>

web/my-child.html
=============================

    <link rel="import" href="packages/polymer/polymer.html">
    <link rel="import" href="my-parent.html">
    <polymer-element name="my-child" extends="my-parent">
      <script type="application/dart" src="MyChild.dart"></script>
    </polymer-element>

web/MyParent.dart
=============================

    import 'package:polymer/polymer.dart';

    @­CustomTag("my-parent")
    class MyParent extends PolymerElement {
      MyParent.created() : super.created() {
        print("parent created");
      }
    }

web/MyChild.dart
=============================

    import 'package:polymer/polymer.dart';

    @­CustomTag("my-child")
    class MyChild extends PolymerElement {
      MyChild.created() : super.created() {
        print("child created");
      }
    }

This produces the same console output as in the original report, i.e.:

    parent created (:1)
    parent created (http://127.0.0.1:8080/packages/polymer/src/js/polymer/polymer.js:12)
    child created (:1)

Per comment #­3, I updated MyChild.dart as follows:

web/MyChild.dart
=============================

    import 'MyParent.dart'; // <-- changed

    @­CustomTag("my-child")
    class MyChild extends MyParent { // <-- now extends MyParent instead of PolymerElement
      MyChild.created() : super.created() {
        print("child created");
      }
    }

And then the base class constructor is invoked trice! :

    parent created (:1)
    parent created (http://127.0.0.1:8080/packages/polymer/src/js/polymer/polymer.js:12)
    parent created (:1)
    child created (:1)

Assuming this implementation is correct, the issue seems fairly important as invoking a constructor more than once is bound to cause bugs due to initialization problems that may be hard to track down.

@DartBot
Copy link
Author

DartBot commented Jun 5, 2015

<img src="https://avatars.githubusercontent.com/u/984921?v=3" align="left" width="48" height="48"hspace="10"> Comment by jakemac53


Just to narrow things down, below is a slightly smaller and possibly more telling repro, https://gist.github.com/jakemac53/06adf5aca6c3a3aa1684.

I am not adding any parent or child tags (only definitions), yet I still get a log from the parent. It seems that adding the extends="foo" causes an instance of the extended class to be created, at least temporarily.

@DartBot
Copy link
Author

DartBot commented Jun 5, 2015

<img src="https://avatars.githubusercontent.com/u/984921?v=3" align="left" width="48" height="48"hspace="10"> Comment by jakemac53


Ran across this in a test I was porting as well, it looks like ready() is also being fired on the extra element.

@DartBot
Copy link
Author

DartBot commented Jun 5, 2015

<img src="https://avatars.githubusercontent.com/u/984921?v=3" align="left" width="48" height="48"hspace="10"> Comment by jakemac53


Discovered the actual cause of this when investigating issue #334. Whenever registering a custom element that extends from another element, dart:html creates an instance of the base element and compares its type to make sure you are actually extending that class.

When registering a custom element in javascript, there is no such check. You can register an element which claims to extend 'input' with any arbitrary prototype.

This seems pretty broken to me, since custom elements can have static state that is affected whenever a new element is created, and they can be potentially expensive to create (firing off ajax requests for example).

@DartBot
Copy link
Author

DartBot commented Jun 5, 2015

<img src="https://avatars.githubusercontent.com/u/984921?v=3" align="left" width="48" height="48"hspace="10"> Comment by jakemac53


Edit: It actually looks like this only happens when extending native html types, which is much safer.

Polymer.js was creating instances of the base tag when trying to figure out if the element had been registered or not. We are now registering all dart custom elements with polymer so that it doesn't have to do that.

@DartBot
Copy link
Author

DartBot commented Jun 5, 2015

<img src="https://avatars.githubusercontent.com/u/984921?v=3" align="left" width="48" height="48"hspace="10"> Comment by jakemac53


Set owner to @jakemac53.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Development

No branches or pull requests

2 participants