manolo / gwt-polymer-elements

Polymer Web Components for GWT. A collection of Material Design widgets for desktop and mobile.
Apache License 2.0
155 stars 49 forks source link

PaperDropdownMenu fails after initial import #145

Open cpboyd opened 7 years ago

cpboyd commented 7 years ago

The demo app works fine, since all dropdowns are limited to the same page...

However, if you have two pages with dropdowns, you'll notice something like the following error when opening the second page (whichever page is the second one you load):

Uncaught Error: com.google.gwt.event.shared.UmbrellaException: Exception caught: Cannot find element with id "gwt-uid-72". Perhaps it is not attached to the document body.
    at E2c_g$.ljb_g$ [as createError_0_g$] (Throwable.java:121)
    at E2c_g$.vjb_g$ [as initializeBackingError_0_g$] (Throwable.java:113)
    at E2c_g$.fjb_g$ (Throwable.java:74)
    at E2c_g$.Ljb_g$ (Exception.java:33)
    at E2c_g$.Tjb_g$ (RuntimeException.java:33)
    at E2c_g$.x2c_g$ (UmbrellaException.java:65)
    at E2c_g$ (UmbrellaException.java:26)
    at y1c_g$.B1c_g$ [as fireEvent_0_g$] (HandlerManager.java:129)
    at Q$e_g$._b_g$ [as fireEvent_0_g$] (Widget.java:129)
    at wi_g$ (DomEvent.java:125)
    at Q$e_g$.hc_g$ [as onBrowserEvent_0_g$] (Widget.java:177)
    at $be_g$ (DOM.java:1481)
    at Zbe_g$ (DOM.java:1420)
    at HTMLElement.Dhe_g$ (DOMImplStandard.java:317)
    at $sb_g$ (Impl.java:239)
    at btb_g$ (Impl.java:298)
    at HTMLElement.<anonymous> (Impl.java:77)

This also occurs on the initial page load if you have statically linked the paper-dropdown-menu.html.

This issue may be what was truly behind #85

cpboyd commented 7 years ago

Creating a Widget using the individual Elements (rather than trying to use the generated Polymer Widgets) seems to work fine:

        <paper-dropdown-menu-light no-label-float="" class="custom">
            <paper-listbox ui:field="menu" class="dropdown-content" selected="0">
                <paper-item>25</paper-item>
                <paper-item>50</paper-item>
                <paper-item>100</paper-item>
            </paper-listbox>
        </paper-dropdown-menu-light>
ghost commented 7 years ago

It has to do with how the UiBinder is building the DOM tree.

Using Elements inside the Dropdown like:

<p:PaperDropdownMenu>
     <paper-menu class="dropdown-content">
          <paper-item>One</paper-item>
          <paper-item>Two</paper-item>
     </paper-menu>
 </p:PaperDropdownMenu>

will be generated to:

private PaperDropdownMenu build_f_PaperDropdownMenu2() {
      // Creation section.
      final PaperDropdownMenu f_PaperDropdownMenu2 = new PaperDropdownMenu(template_html1().asString());
      // Setup section.

      return f_PaperDropdownMenu2;
}

with template_html1() like @Template("<paper-menu class='dropdown-content'> <paper-item>One</paper-item> <paper-item>Two</paper-item> </paper-menu>")

Using a Widget inside the Dropdown like:

<p:PaperDropdownMenu>
     <p:PaperMenu class="dropdown-content">
          <paper-item>One</paper-item>
          <paper-item>Two</paper-item>
     </p:PaperMenu>
 </p:PaperDropdownMenu>

will be generated to:

private PaperDropdownMenu build_f_PaperDropdownMenu2() {
  // Creation section.
  final PaperDropdownMenu f_PaperDropdownMenu2 = new PaperDropdownMenu(template_html2().asString());
  // Setup section.
  {
    // Attach section.
    UiBinderUtil.TempAttachment __attachRecord__ = UiBinderUtil.attachToDom(f_PaperDropdownMenu2.getElement());

    get_domId1Element().get();

    // Detach section.
    __attachRecord__.detach();
  }
  f_PaperDropdownMenu2.addAndReplaceElement(get_f_PaperMenu3(), get_domId1Element().get());

  return f_PaperDropdownMenu2;
}

In this case the template_html2() is just a SPAN holding an Id: @Template("<span id='{0}'></span>") It seems it is like a placeholder for the PaperMenu which will be replaced by the PaperMenu-Element after attaching to the DOM.

The error happens because get_domId1Element() returns a LazyDomElement and when calling the get()-method there is no element with this id.

The strange thing about this all is that this does not happen in general. It happens just for some Widgets like PaperDrawerPanel or PaperDropdownMenu and only if these Components are already registered (Polymer.isRegisteredElement returns true)

ghost commented 7 years ago

Ok, things become clearer. The core part of the Dropdown from the iron-dropdown is

<div id="contentWrapper">
    <content id="content" select=".dropdown-content"></content>
</div>

Since the DomId-Holder from the UiBinder is just a SPAN-element without the class dropdown-content the element will be dropped while attaching to the DOM and then the LazyDomElement can't find this element anymore.

Simple Test:

<p:PaperDropdownMenu>
     <span id="test-id" class="dropdown-content"></span>
</p:PaperDropdownMenu>

Without the class the span is missing after rendering in the browser.

Since the Vaadin PolymerWidget saves and restores the element properties when the webcomponent has to be downloaded first the effect may occur only on already registered components.

Conclusion: Using elements inside a webcomponent seems to be the way to go.

cpboyd commented 7 years ago

I wonder if it'd be possible to specify for certain widgets (like PaperDrawerPanel and PaperDropdownMenu) to always perform the save/restore regardless of whether or not the element has already been downloaded.

In my case, I don't really care about PaperDropdownMenuLight (using it more as a wrapper) but would rather have access to PaperListbox as a widget.

ghost commented 7 years ago

Sorry, my assumption seems wrong. It has nothing to do with this properties save /restore but more with the fact that when the webcomponent is not loaded yet it is just a HTMLUnknownElement (without any content selector) and therefore the widget placeholder (SPAN element) is still alive.

It has more to do with the webcomponent lifecycle (point of registration)