With Polymer, it’s very easy to build a SPA (Single Page-App), and they are nice.
However, depending on how you code it, you can end up loading all your components into memory at the same time. It hurts performance (due to excessive amount of in-memory information) and can create other issues (suppose your “hidden page” has a timer doing some job, for example).
The solution is easy: you can load and create your elements on the fly. This post brings you a sample that can load elements on the fly and alternate between them.
How does it work
To create an element on the fly, you can use DOM Manipulation methods like:
var myElement = document.createElement("my-element"); this.$.container.appendChild(myElement); // The line above supposes you have a <div id="container> // which will hold your new my-element instance
To remove it, you can just find it in DOM and remove:
var element = this.$.container.querySelector("my-element"); element.parentNode.removeChild(element);
And if you need dynamic load a HTML Import, you must use Polymer’s utility function importHref
(docs here) :
this.importHref('path/to/myElement.html', function(e) { // Create your element here });
IMPORTANT NOTE:
You need to be aware that importHref
sees current path as being the same of your HTML address bar (*). This can be confusing when you are trying to use it inside a custom Polymer element whose HTML definition resides in a different folder than your HTML.
This means that if you have loaded a page from http://localhost/test/main.html which imports a custom element my-custom-element
from http://localhost/test/elements/myCustomElement.html, and you try to call importHref
from a method defined in this my-custom-element
, you must understand that importHref
will see paths relative to http://localhost/test (and not to http://localhost/test/elements as you could expect).
(*) In fact, there’s a way to make it relative to your element’s path using resolveUrl(url)
utility function. See my note at the end of this post.
Putting things together
Note 1: You must change methods _loadElement1
and _loadElement2
to load your own custom elements.
<link rel="import" href="../../bower_components/polymer/polymer.html"> <link rel="import" href="../../bower_components/paper-button/paper-button.html"> <dom-module id="app-test"> <style> #controls { height: 50px; width: 100%; display: block; } /* Lets occupy all screen */ #container { width: 100%; top: 50px; bottom: 0px; position: absolute; background-color: #e0f7fa; } /* This selector is applied to direct children of our container */ #container > * { width: 100%; height: 100%; display: block; } </style> <template> <div id="controls"> <paper-button on-click="_loadElement1">Load Element 1</paper-button> <paper-button on-click="_loadElement2">Load Element 2</paper-button> <paper-button on-click="_removeElement">Remove Element</paper-button> </div> <div id="container"></div> </template> </dom-module> <script language="javascript"> Polymer({ is: 'app-test', _myLoadElement: function(elementName, htmlImport) { // Let's remove last loaded element if exists this._removeElement(); // Now we load it on-the-fly this.importHref(htmlImport, function (e) { // Create a new instance var myElement = document.createElement(elementName); // And add it to the container this.$.container.appendChild(myElement); // Lets set a dummy property just to show how to do it myElement.myProperty = "anything"; }); }, _loadElement1: function () { // Pay attention to this path. // This IS NOT relative to this component. // Its relative to current loaded HTML page which contains app-test element // (read the "IMPORTANT NOTE:" section of this post before proceeding) // Load your element in the line below this._myLoadElement("my-element", "elements/myElement.html"); }, _loadElement2: function () { // Load your element in the next line. Pay attention to the path. this._myLoadElement("my-other-element", "elements/myOtherElement.html"); }, _removeElement: function () { var element = this.$.container.querySelector("*"); if (element) { element.parentNode.removeChild(element); } } }); </script>
PS: You can use resolveUrl(url)
utility function to translate paths in order to make them relative to element’s path (and not to loaded page). I haven’t use it in my example trying to keep things less confusing, but if you want, just change line 44 to:
this.importHref(this.resolveUrl(htmlImport), function (e) {