2

as the Polymer Starter Kit or the Polymer Shop demonstrate, we make use of lazy-loading in our Polymer application. This means we have a drawer-based layout and the app shell imports the drawer content as well as the main page content.

Now we introduce a behavior and use it in custom elements A and B where A is shown in the drawer and B in the main page. It's now the case that we get a browser (Chrome) warning, that flattenBehaviorsList couldn't find the behavior.

We assume this happens because the Polymer.importHref call (option asyncis true) for the main page recognizes the behavior (in custom element B) and adds its import to the HTML Import map. But before the content is imported, the Polymer.importHref (option asyncis true) call for the drawer wants to import the behavior for custom element A and takes the content from the HTML Import Map. But since the import isn't done, the behavior is null.

Is that assumption true? And if yes, is this a bug in Chrome – just an idea since it seems that Firefox handles this? Or is it bad design and we shouldn't share the same behavior in different lazy-loaded app parts?

What do you think?

Thanks in advance

Newlukai
  • 377
  • 1
  • 12

2 Answers2

2

I think I had the same problem today. When using importHref async I get errors like [paper-radio-button::_flattenBehaviorsList]: behavior is null, check for missing or 404 import, but when I change to async = false the error messages are gone.

It seems that this is a known bug of Polymer or probably Chrome https://github.com/Polymer/polymer/issues/2522

0

Lazy loading is the preferred way to load resources, that you don't need right away. But you have to do it right, which I will explain a bit later.

Having a behaviour in a separate file and re-using it across multiple custom elements is also very normal and even encouraged. However, all of the resources that a custom component needs must be loaded as a normal rel="import", meaning the async and defer flags must not be set.

For example, take a look at the following demo element, taken from the Polymer's devguide pages:

<link rel="import" href="highlight-behavior.html">

<script>
  Polymer({
    is: 'my-element',
    behaviors: [HighlightBehavior]
  });
</script>

As you can see, the behaviour is imported as a dependency. Without it, there would most likely be an error, as in your case.

Now comes the fun part - lazy loading. Here, you actually don't load everything that the component uses. So you must be well aware which resources you can, and which you cannot lazy load. As a rule of thumb, you must load everything that you use/instantiate directly inside the component.

Some common examples of lazy loading:

  • If you have an iron-pages element and all of the pages are custom elements, then you should eagerly load only the default (first) page, as others will not be used right away.
  • You can put non-esential GUI elements, such as slide-out sidebars inside a dom-if and render that dom-if after all of the components are lazy loaded. The easiest way for this is to use importHref.

A very good example for lazy loading elements is the Polymer's Shop app: source code, online demo.

To sum up: don't lazy load must-have dependencies for a custom element, but lazy load the element itself.

alesc
  • 2,690
  • 2
  • 24
  • 40
  • Thanks for your explanation. It matches my assumption that lazy-loading isn't as easy as to just use the `async`flag. And as you say, in the Polymer Shop they demonstrate when to eagerly load resources, i. e. the `shop-home.html`file in the shop shell since the `home` route is the default route. But what happens if one has the idea to forward a link to an article list to someone and the receiver thus skips the default route? – Newlukai Jan 06 '17 at 08:38
  • Correct, when you deep link to a non-default page, then you have a "problem". If you take a look at the `shop-app.html` at the `_pageChanged` method, you will see that they import the page upon route change, which actually solves this problem. Be also aware, that this import is near-instant, since the service worker most likely already cached it (except when a user first opens a deep linked page). – alesc Jan 06 '17 at 08:46
  • My scenario is as follows: The shell contains an `iron-pages` for the main content and a `sidebar` element. The `sidebar` itself also contains an `iron-pages`. This way I provide the user a viewer and an editor. In both `iron-pages` I use `importHref` (with `async` flag) to load the `viewer-page.html` and `viewer-sidebar.html`, and the editor elements as well. The elements in both parts load all resources eagerly and some of them share behaviors. Now I'm wondering if there are two workers loading page and sidebar and if there might be a race condition regarding the HTML Import map. – Newlukai Jan 06 '17 at 08:49
  • The browser will automatically deduplicate the requests for you, so you don't need to worry that a resource will be fetched multiple times. You can safely include it everywhere you need it. For example, all custom components refer to the `polymer.html` import and the browser only loads it once. This also works for `async` imports. – alesc Jan 06 '17 at 08:59
  • As good as it is to know that deduplication takes place, it's not the aspect I'm worrying about. Or maybe, it is. Let me explain: Two workers are asynchronously loading the page parts (main, sidebar). Worker A stumbles upon the `my-behavior.html` import, then puts a corresponding entry in the HTML Import map and starts loading. Meanwhile worker B also gets an import statement for `my-behavior.html`, looks it up in the map, finds it, reads its value and _BOOM_ gets `undefined`. Would that be possible? The same Polymer app throws `_flattenBehaviorsList` warnings in Chrome but works in FF. – Newlukai Jan 06 '17 at 09:24
  • Could you please provide a minimal working example, so that I can examine the problem? Update your question and I will update my answer. – alesc Jan 06 '17 at 16:09
  • I uploaded [an example on GitHub](https://github.com/newlukai/polymer-chrome-async-import-issue). It's not as minimal as possible, but should do the trick. Unfortunately, I didn't have time to create a perfect example: once you `polymer serve` and open the app, the console shows 3 404s which can be ignored. Then choose a menu entry from the nav bar and hit reload. The app is reloaded, the app shell asynchronously `importHref`s the current tab and page, and in the console you get an `Uncaught ReferenceError` regarding the namespace of the imported behavior. No matter which view you reload. – Newlukai Jan 09 '17 at 09:04
  • This is actually very interesting and doesn't make much sense, since the custom element for the page is executed before the behaviour, which is included at the top. Hence the behaviour is undefined. As @IntranetFactory has pointed out, the (temporary) solution is to change the `async` flag at both `importHref` to `false` (the last parameter). I have tried it locally and it works for me. – alesc Jan 09 '17 at 20:15
  • I also observed that the element is executed before the behavior. And as you mention, setting `async` to false would be a solution, but only a temporary one. Funny thing is that the example works well in Firefox. But I don't know if it's because Firefoy doesn't support HTML Imports... It's hard to determine if it's a Polymer or a Chrome issue. Or if it's bad design of the app. – Newlukai Jan 10 '17 at 08:30
  • I believe that this is a Chrome issue, since the `importHref` only imports the component itself and then the browser imports all of the dependencies as it starts to parse the downloaded HTML file. It seems that it also downloads those with `async` as well, even though it is not specified that way. – alesc Jan 10 '17 at 08:35
  • For me it seems to be related to [this Chrome issue](https://github.com/Polymer/polymer/issues/3095). Thanks for your patience and support! – Newlukai Jan 16 '17 at 08:55