0
votes

I'm trying to build a component that can switch other components in and out, almost like pages, but with no change in URL.

I have a polymer element that contains a content div:

<link rel="import" href="../../packages/polymer/polymer.html">

<link rel="import" href="../../packages/paper_elements/paper_input.html">
<link rel="import" href="../../packages/paper_elements/paper_input.html">
<link rel="import" href="../../packages/paper_elements/paper_tabs.html">
<link rel="import" href="../../packages/paper_elements/paper_dialog.html">
<link rel="import" href="../../packages/paper_elements/paper_icon_button.html">
<link rel="import" href="../../packages/core_elements/core_toolbar.html">
<link rel="import" href="../../packages/core_elements/core_scaffold.html">
<link rel="import" href="../../packages/core_elements/core_header_panel.html">
<link rel="import" href="../../packages/core_elements/core_menu.html">
<link rel="import" href="../../packages/core_elements/core_item.html">
<link rel="import" href="../../packages/paper_elements/paper_input.html">
<link rel="import" href="../../packages/paper_elements/paper_button.html">

<polymer-element name="navigation-toolbar" class="dark-primary-color">
    <template>
        <style type="text/css">
            :host {
                display: block;
            }

        </style>

        <core-scaffold>

            <core-header-panel id="menu-panel" navigation flex>

                <core-toolbar id="navheader">
                    <span>Menu</span>
                </core-toolbar>
                <core-menu>
                    <core-item label="A" on-click="{{aClicked}}"></core-item>
                    <core-item label="B" on-click="{{bClicked}}"></core-item>
                    <core-item label="C" on-click="{{cClicked}}"></core-item>
                </core-menu>
            </core-header-panel>

            <span tool>{{title}}</span>
            <paper-tabs class="main-menu bottom fit" selected="0">
                <paper-tab on-click="{{aClicked}}">A</paper-tab>
                <paper-tab on-click="{{bClicked}}">B</paper-tab>
                <paper-tab on-click="{{cClicked}}">C</paper-tab>
            </paper-tabs>

            <div class="content" forceNarrow>
               <p>Lorem ipsum ...</p>
            </div>
        </core-scaffold>
    </template>
    <script type="application/dart" src="navigation-toolbar.dart"></script>
</polymer-element>

I have an eventbus that fires events and receives events, it is used all over the application to allow me to decouple components.

part of util;

class EB {

  static EventBus _eventBus = new EventBus();

  static fire(event){
    _eventBus.fire(event);
  }

  static Stream on([Type eventType]) {
    return _eventBus.on(eventType);
  }

}

PageEvent which is being used to fire / listen for page events is just a PODO:

part of event;

class PageEvent {

  String page;
  String title;
  PolymerElement element;


  PageEvent.create(String page, String title, PolymerElement element){
    this.page = page;
    this.title = title;
    this.element = element;
  }


}

Inside the event library the relevant imports is being done for PageEvent:

library event;

import 'package:polymer/polymer.dart';

part 'login-event.dart';
part 'logout-event.dart';
part 'page-event.dart';
...

This event bus is then used in my core-scaffold component:

import 'package:polymer/polymer.dart';
import 'util/util.dart';
import 'event/event.dart';
import 'page/page.dart';
import 'dart:html';

@CustomTag('navigation-toolbar')
class NavigationToolbar extends PolymerElement {

  @observable String page = "home";
  @observable String title = "Home";

  NavigationToolbar.created() : super.created() {

    EB.on(PageEvent).listen((PageEvent e) {
      this.page = e.page;
      this.title = e.title;
      setPage(e.element);
    });

An event is fired upon clicking on one of the paper-tabs:

 // part of navigation-toolbar
 aClicked(event, detail, target) {
    EB.fire(new PageEvent.create("a", "A", new APage()));
  }

setPage is where the final magic is supposed to happen, but is not, both div and e is being printed out which tells me it is getting to the method and both e and div are not null:

 // part of navigation-toolbar
 setPage(PolymerElement e) {

    DivElement div = shadowRoot.querySelector("core-scaffold").querySelector(".content");
    Node node = div.lastChild;
    while (node != null) {
      node.remove();
      node = div.lastChild;
    }

    print(div);
    print(e);

    div.children.add(e);

  }

Printing out div, I can see the word div in the console which means it's not null, printing out e, I can see a-page, b-page, c-page, etc, that's not null either.

The code to clear the div works, it removes all content. The part that doesn't work is the div.children.add(e);

APage looks like this:

<link rel="import" href="../../../packages/polymer/polymer.html">

<polymer-element name="home-page" class="dark-primary-color">
    <template>
        <div>
            A Page
        </div>
    </template>
    <script type="application/dart" src="a-page.dart"></script>
</polymer-element>

and a-page.dart:

part of page;

@CustomTag('a-page')
class APage extends PolymerElement {

  APage.created() : super.created();

  factory APage() => new Element.tag('a-page');

}

Adding an import for a-page.html in navigation-toolbar.html

<link rel="import" href="page/a-page.html">

Gives me the following exception:

'package:falm/page/a-page.dart': error: line 1 pos 6: url expected
part of page;
     ^: package:falm/page/a-page.dart 

div.children.add(e); doesn't work, what am I missing here?

1
Hard to tell, your question doesn't show much relevant code. Do you have an import for the Polymer element you add somewhere (entry page or the Polymer element where you add it)? (like <link rel="import" href="packages/core_elements/core_icon.html">) - Günter Zöchbauer
You can simplify shadowRoot.querySelector("core-scaffold").querySelector(".content") to shadowRoot.querySelector("core-scaffold > .content") or just shadowRoot.querySelector(".content") if the <div> is the only element with that class. Otherwise I still can't see what could be wrong. I usually use div.append(e) instead of div.children.add(e) (should work as well but you could try the first one anyway). Where do you have the imports for a-page, b-page, c-page? - Günter Zöchbauer
Neither shadowRoot.querySelector("core-scaffold.content") nor shadowRoot.querySelector(".content") works, I need to the double query selector, otherwise the DivElement is null. I'm guessing the shadowRoot hiding the div element from the dom. The imports for a-page is all being done in page.dart which is the page library, then in navigation-toolbar.dart I just import page.dart. Seeing as I'm instantiating the components in dart, I don't see the need to import the a-page.html as well on the navigation-toolbar.html, but if I do, I get the exception mentioned in my question, so not doing it. - Jan Vladimir Mostert
I solved it, a whole day of googling finally resolved :-) Will post answer nownow, then we can figure out why it works and why the other way doesn't work. - Jan Vladimir Mostert
Every element needs an HTML import <link rel ...> no matter how it is instantiated. shadowRoot.querySelector("core-scaffold.content") can work of course, it looks for a <core-scaffold class="content"> element but shadowRoot.querySelector("core-scaffold .content") or shadowRoot.querySelector("core-scaffold > .content") or shadowRoot.querySelector("div.content") should definitively work because they are all within the same shadow DOM. - Günter Zöchbauer

1 Answers

0
votes

After messing around a bit, I found a way that works - it seems polymer elements doesn't play nice when included in a library.

This was what a-page.dart looked like:

part of page;

@CustomTag('a-page')
class APage extends PolymerElement {

  APage.created() : super.created();

  factory APage() => new Element.tag('a-page');

}

and the page.dart library:

library page;

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

part 'a-page.dart';
part 'b-page.dart';
part 'c-page.dart';

After removing the page library and putting all the dependencies straight inside a-page.dart:

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

@CustomTag('a-page')
class APage extends PolymerElement {

  APage.created() : super.created();

  factory APage() => new Element.tag('a-page');

}

inside my navigation-toolbar.dart, instead of importing page/page.dart, I need to import each page individually:

import 'page/a-page.dart';
import 'page/b-page.dart';
import 'page/c-page.dart';

...

  setPage(PolymerElement e) {

    DivElement div = shadowRoot.querySelector("core-scaffold .content")
    ..children.clear()
    ..append(e);

  }

and on navigation-toolbar.html, I need to import each html component as well for it to work:

<link rel="import" href="page/a-page.html">
<link rel="import" href="page/b-page.html">
<link rel="import" href="page/c-page.html">

Is it even possible to include polymer elements in a library?