Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
DartBot opened this issue Jul 25, 2014 · 11 comments
Closed

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

DartBot opened this issue Jul 25, 2014 · 11 comments
Assignees
Labels
area-pkg Used for miscellaneous pkg/ packages not associated with specific area- teams.

Comments

@DartBot
Copy link

DartBot commented Jul 25, 2014

This issue was originally filed by @almstrand


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)

@sigmundch
Copy link
Member

Thanks for the bug report and the nice repro instructions!

Sounds like this is a duplicate of issue #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 #15844.

@blois
Copy link
Contributor

blois commented Jul 29, 2014

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


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

@blois
Copy link
Contributor

blois commented Jul 29, 2014

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 Jul 29, 2014

This comment was originally written 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.

@jakemac53
Copy link
Contributor

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.

@jakemac53
Copy link
Contributor

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

@jakemac53
Copy link
Contributor

Discovered the actual cause of this when investigating issue #21466. 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).

@jakemac53
Copy link
Contributor

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.

@jakemac53
Copy link
Contributor

Set owner to @jakemac53.

@DartBot
Copy link
Author

DartBot commented Jun 5, 2015

This issue has been moved to dart-archive/polymer-dart#248.

@vishnumohan89
Copy link

I had this same issue with an element (my-login), this was used inside my-menu element. This my-menu element was used twice. So this in turn, initializes my-login element twice also. I fixed the issue, by using conditional binding.

Hope this helps someone.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-pkg Used for miscellaneous pkg/ packages not associated with specific area- teams.
Projects
None yet
Development

No branches or pull requests

6 participants