Loading Polymer elements on demand - polymer

I'm trying to reduce the loading time for Polymer page which require lot of elements to be loaded.
So, Thinking about making elements loaded on demand, means once I use an element in the HTML code, if not loaded, it get loaded immediately.
Anybody have an idea how that can be done ?
Maybe some event is fired when unknown HTML element is being used ? So I can handle the loading using importHref() once that happen ?

It depends on your needs, there isn't a standard way of optimize it, but you can use Vulcanize tool and test perfomance.
If you don't want load all your webcomponents in one request, you can vulcanize all the elements that you need in your initial page/view. So, you'll load a set of elements in one request and use them in your first page.
Then, you can implement a lazy loading consisting on adding link tags to head section for each webcomponent. By this way, load process isn't been made during browser's parse task, so isn't blocking.

iron-lazy-pages may be useful to you:
<app-location route="{{route}}"></app-location>
<app-route
data="{{route_data}}"
pattern="/:page"
route="{{route}}"
></app-route>
<iron-lazy-pages attr-for-selected="data-route" selected="{{route_data.page}}">
<x-page1 data-route="page1" data-path="demo/x-page1.html"></x-page1>
<x-page2 data-route="page2" data-path="demo/x-page2.html"></x-page2>
<section data-route="page3">
My inline element.
</section>
</iron-lazy-pages>
You can install with the following:
bower install --save TimvdLippe/iron-lazy-pages

Related

Polymer 2.x : How do I scope Foreign stylesheets and / or Are #imports shimmed

There have been questions doing the rounds , making a lot of people try stubbornly force scoping an external css into a Polymer element..
by hook or crook
And then the heart break of an #import not working, or a link rel=stylsheet not scoped
So,
How do I as an author / consumer of a polymer element, scope foreign stylesheets?
What are some gotchas? is #import supported and shimmed?
Can I Scope external CSS Stylesheets to within a Polymer Custom Element
TL;DR :
use link rel=import and pass the url of your external stylesheet to the poly-style tool
Note Polymer still can not shim an #import , or stuff with external urls , that require a fetch before the shim
Also, <link rel=stylesheet... is not scoped , but can be used.It is also, not shimmed into your element.
Polymer up until 1.0 defined the use of <link> tags with attributes
rel = import
type = css
and whatever href as long as it obeyed CORS principles
Everything was all fancy and we all held hands and sang kumbaya
With Polymer 2.0 and actually even the later versions of polymer 1.0,
something called style-modules were introduced where, you encapsulate
all your handwritten styles inside of a template in a dom-module container
<dom-module id="my-style">
<template>
<style>
.class_1{
/*all class_1 styles*/
}
.class_2{
/*all class_1 styles*/
}
.class_3{
/*all class_1 styles*/
}
</style>
</template>
</dom-module>
And then you'd essentially import these handwritten styles into your custom element
<link rel=import href=my-style.html />
<dom-module id="my-app">
<template>
<style include=my-style>
</style>
</template>
</dom-module>
Oh Snap! Now, I'd have to copy over entire stylesheets first, then wrap them in a <dom-module>
and then import them over to my app! we retorted.
And then there were numerous queries about deduplication and all those sort of anxieties
So, the preferred or suggested solution rather, was to read and search through stylesheets and copy over only those classes deemed necessary.
Polymer Also has yet to rid itself with the deprecated
<link rel=import type=css... />
css shimmer .
basically, even with polymer 2.0, you can use the above, albeit, you now need to actually place it inside of your <dom-module>.
and polymer shims it.
Some gotchas :
you simply can not have the dreadful #import rules inside those stylesheets
#imports require polymer to fetch the resource async and hence can not guarantee a shim - hence no support.
Now on some browsers, you might see this lingering in the shadows of your dev console:
Resource interpreted as Document but transferred with MIME type text/css: "https://a.b.com/additional.css".
This doesn't necessarily mean the resource wasn't fetched, but hey if your web server has a no-sniff
or any content integrity header set, that'd mean, the browser can not interpret a css as an html.
now since <link rel=import... is meant for HTML Imports , that'd mean, the browser has to accept only html as a response content type.
Hence, style modules are the best way to go while importing and scoping css to your components.
How then would I import an external stylesheet.. say a font-awesome ?
well, polymer has the web tool residing at
this web based style module builder for polymer
Now this web tool , duly gives you the required content-type and access-control headers.
Since the response is text/html , no more warnings on the console either.
So, How do I build my style module?
simple, use link rel=import and pass the url of your external stylesheet to the poly-style tool
<link rel="import" href="https://poly-style.appspot.com?id=poly-fa-style-module&url=https://use.fontawesome.com/releases/v4.7.0/css/font-awesome-css.min.css">
<dom-module id="poly-fa">
<template id="poly-fa-styler" strip-whitespace>
Now, all you need to do is, do an <style include=poly-fa> within your apps. and you have a scoped font awesome.
Caution!
This will still not support #imports
Also, ::slotted DOM, can not be styled by style-modules via shared styles, except under some scenarios where, you import the stylesheet into a higher element in the hierarchy
Gulp
The above tool is ideal for authors, who may choose not to prefill required external styles for their components in their custom elements, and fetch external css when necessary
for people who might want to pre fill external styles into their components, I have heard of a gulp plugin here , although I am yet to use it.

Serving Polymer App to a /path not at root

So the first thing I want to do with a new Polymer app is deploy to a directory on an existing website. The only thing that seems to work is deploying to root /.
Let's take the Shop example. I do:
polymer init and choose shop
polymer build
Robocopy.exe .\build\bundled\ C:\inetpub\wwwroot\p\ /MIR
start http://localhost/p/
You see I'm on Windows. I assume that using IIS is irrelevant, since I'm relying on the server just to serve static content.
What do I need to edit in the shop template to make it work at the url http://localhost/p/?
The polymer-cli created apps came with assumption of serving from root level '/'. In generated project index.html you will find two comments
<!--
The `<base>` tag below is present to support two advanced deployment options:
1) Differential serving. 2) Serving from a non-root path.
Instead of manually editing the `<base>` tag yourself, you should generally either:
a) Add a `basePath` property to the build configuration in your `polymer.json`.
b) Use the `--base-path` command-line option for `polymer build`.
Note: If you intend to serve from a non-root path, see [polymer-root-path] below.
-->
<base href="/">
<!-- ... -->
<script>
/**
* [polymer-root-path]
*
* By default, we set `Polymer.rootPath` to the server root path (`/`).
* Leave this line unchanged if you intend to serve your app from the root
* path (e.g., with URLs like `my.domain/` and `my.domain/view1`).
*
* If you intend to serve your app from a non-root path (e.g., with URLs
* like `my.domain/my-app/` and `my.domain/my-app/view1`), edit this line
* to indicate the path from which you'll be serving, including leading
* and trailing slashes (e.g., `/my-app/`).
*/
window.Polymer = {rootPath: '/'};
// ...
</script>
if in this index.html file you comment out base tag and set window.Polymer rootPath to something like '/0/polymer-test/build/es5-bundled/' you will be able to navigate in app on http://localhost/0/polymer-test/build/es5-bundled/
The Polymer shop-app assumes it will be deployed on the server root. Therefore it has all of the links and routes hard-coded to that assumption.
This means, that you will have to change all of the following:
all absolute links between the pages,
all pattern parameters in app-route elements (this is not necessary when useHashAsPath = true),
all absolute imports, including the lazy ones via importHref,
update the absolute locations within the service worker (use instructions from here) and
all references to static content (CSS, images, JS files)
I'm guessing your main goal isn't porting the shop-app, but rather future proofing your own app so that it can also be deployed to non-root locations on the server.
For this, I will mention two ways, depending on which value of useHashAsPath you use for the app-location element. This setting defaults to false, which means that you must use full URLs, instead of the hashbang equivalents.
Scenario 1: useHashAsPath = true
This is the easiest of both approaches, since you simply treat all URLs between the pages as absolute links. For example: Tabs.
The next step is to reference all static content and imports via relative links.
The last step is to update your service worker as shown here.
Scenario 2: useHashAsPath = false
If you dislike the hashbang URLs, go for this scenario. As you can figure out, this approach is a bit more difficult, but still manageable (especially when you start from scratch).
Firstly, you should still use absolute links, since relative links between a complex routing scheme can quickly cause problems (e.g. when not all pages are on the same directory level).
But since absolute links are a no-go, you will have to add some additional pre-processing upon build time. The point is to prefix all links with, say __ROOT__, and then replace all of those values with your actual document root. The links would then look like something this:
Some page
And you would use gulp-replace or something similar to replace __ROOT_ with /your-document-root across all of your source files in order to produce something like this:
Some page
At this point, you've got your links fixed. But this is only part of the problem. You must also apply the same fix to all of your app-route elements. For example:
<app-route pattern="__ROOT__/some/page" [...]></app-route> // Other parameters ommited
As with other resources, such as images and CSS files, you can also include them as absolute links and add the __ROOT__ prefix, but I would advise against this and would rather use relative paths.
The last step is to update your service worker as shown here.
Read more about routing: https://www.polymer-project.org/1.0/blog/routing

Dynamically load an entire angular app

So I have a curious situation and I don't think it's going to work, but I figured I'd ask in case it is and someone knows how to. I am using a 3rd party website to create marketing funnels. You can add your own custom html and javascript, but it parses out the html in a rather unfavorable manor. Basically you specify an element on the page and it appends it as a data attribute and dynamically loads it into the DOM. Since this is happening this way, my app isn't being initialized because it's not in the DOM on page load. Is there a way to make this work? I'll explain a little deeper my configuration.
I add custom html such as:
<div data-ng-app="AppName"><div data-ng-controller="ControllerName"><div>perform controller logic here</div></div>
As you can see, this needs to be in the DOM for the app to initialize and, well work. Is there a way to initialize the app after the page has loaded dynamically? I can add my own JS files in the custom html field, but thats about as far as I can go for customization. Any help is appreciated!
Maybe you should execute angular's bootstrap function manually in your script after the required dom loaded.
var app = angular.module('appName', []);
app.controller([...]);
angular.bootstrap(document.getElementById('divId'), ['appName']);
For more information, you can see this doc https://docs.angularjs.org/api/ng/function/angular.bootstrap

HTML Imports with the Async flag - strange behaviour in Chrome

I am trying to optimise the loading of Polymer Elements in my Polymer based web app. In particular I am concentrating my effort around the initial start up screens. Users will have to log on if they don't have a valid jwt token held in a cookie.
index.html loads an application element <pas-app> which in turn loads an session manager (<pas-eession>). Since the normal startup will be when the user is already logged on the element that handles input of user name and password (<pas-logon>) is hidden behind a <template is="dom-if"> element inside of <pas-session>and I have added the async flag to its html import line in that element as well - thus :
<link rel="import" href="pas-logon.html" async>
However, in chrome (I don't experience this in firefox, where html imports are polyfilled) this async seems to flow over embedded <script> element inside the custom element. In particular I get a type error because the script to cause it to be regestered as a custom element thinks Polymer is not a function.
I suspect I am using the wrong kind of async flag - is there a way to specify that the html import should not block the current element, but should block the scripts inside itself when loaded.
I think I had the same problem today and found this question when searching for a solution. When using importHref async I get errors like [paper-radio-button::_flattenBehaviorsList]: behavior is null, check for missing or 404 import and dependencies are not loaded in the right order. 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

CodeIgniter + jQuery(ajax) + HTML5 pushstate: How can I make a clean navigation with real URLs?

I'm currently trying to build a new website, nothing special, nice and small, but I'm stuck at the very beginning.
My problems are clean URLs and page navigation. I want to do it "the right way".
What I would like to have:
I use CodeIgniter to get clean URLs like
"www.example.com/hello/world"
jQuery helps me using ajax, so I can
.load() additional content
Now I want to use HTML5 features like pushstate to
get rid of the # in the URL
It should be possible to go back and forth without a page refresh but the page will still display the right content according to the current URL.
It should also be possible to reload a page without getting a 404 error. The site should exist thanks to CodeIgniter. (there is a controller and a view)
For example:
A very basic website. Two links, called "foo" and "bar" and a emtpy div box beneath them.
The basic URL is example.com
When you click on "foo" the URL changes to "example.com/foo" without reloading and the div box gets new content with jQuery .load(). The same goes for the other link, just of course different content and URL.
After clicking "foo" and then "bar" the back button will bring me back to "example.com/foo" with the according content. If I load this link directly or refresh the page, it will look the same. No 404 error or something.
Just think about this page and tell me how you would do this.
I would really love to have this kind of navigation and so I tried several things.
So far...
I know how to use CodeIgniter to get the URLs like this. I know how to use jQuery to load additional content and while I don't fully understand the html5 pushstate stuff, I at least got it to work somehow.
But I can't get it to work all together.
My code right now is a mess, that's the reason I don't really want to post it here. I looked at different tutorials and copy pasted some code together. Would be better to upload my CI folder I guess.
Some of the tutorials I looked at:
Dive into HTML5
HTML5 demos
Mozilla manipulating the browser history
Saner HTML5 history
Github: History.js
(max. number of links reached :/)
I think my main problem is, that everybody tries to make it compatible with all browsers and different versions, adds scripts/jQuery plugins and whatnot and I get confused by all the additional code. There is more code between my script-tags then actual html content.
Could somebody post the most basic method how to use HTML5 for my example page?
My failed attemp:
On my test page, when I go back, the URL changes, but the div box will still show the same content, not the old one. I also don't know how to change the URL in the script according to the href attribute from the link. Is there something like $(this).attr('href'), that changes according to which link I click? Right now I would have to use a script for every link, which of course is bad.
When I refresh the site, CodeIgniter kicks in and loads the view, but really only the view by itself, the one I loaded with ajax, not the whole page. But I guess that should be easy to fix with a layout and the right controller settings. Haven't paid much attention to this yet.
Thanks in advance for any help.
If you have suggestions, ideas, or simple just want to mention something, please let me know.
regards
DiLer
I've put up a successful minimal example of HTML5 history here: http://cairo140.github.com/html5-history-example/one.html
The easiest way to get into HTML5 pushstate in my opinion is to ignore the framework for a while and use the most simplistic state transition possible: a wholesale replacement of the <body> and <title> elements. Outside of those elements, the rest of the markup is probably just boilerplate, although if it varies (e.g., if you change the class on HTML in the backend), you can adapt that.
What a dynamic backend like CI does is essentially fake the existence of data at particular locations (identified by the URL) by generating it dynamically on the fly. We can abstract away from the effect of the framework by literally creating the resources and putting them in locations through which your web server (Apache, probably) will simply identify them and feed them on through. We'll have a very simple file system structure relative to the domain root:
/one.html
/two.html
/assets/application.js
Those are the only three files we're working with.
Here's the code for the two HTML files. If you're at the level when you're dealing with HTML5 features, you should be able to understand the markup, but if I didn't make something clear, just leave a comment, and I'll walk you through it:
one.html
<!doctype html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.js"></script>
<script src="assets/application.js"></script>
<title>One</title>
</head>
<body>
<div class="container">
<h1>One</h1>
Two
</div>
</body>
</html>
two.html
<!doctype html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.js"></script>
<script src="assets/application.js"></script>
<title>Two</title>
</head>
<body>
<div class="container">
<h1>Two</h1>
One
</div>
</body>
</html>
You'll notice that if you load one.html through your browser, you can click on the link to two.html, which will load and display a new page. And from two.html, you can do the same back to one.html. Cool.
Now, for the history part:
assets/application.js
$(function(){
var replacePage = function(url) {
$.ajax({
url: url,
type: 'get',
dataType: 'html',
success: function(data){
var dom = $(data);
var title = dom.filter('title').text();
var html = dom.filter('.container').html();
$('title').text(title);
$('.container').html(html);
}
});
}
$('a').live('click', function(e){
history.pushState(null, null, this.href);
replacePage(this.href);
e.preventDefault();
});
$(window).bind('popstate', function(){
replacePage(location.pathname);
});
});
How it works
I define replacePage within the jQuery ready callback to do some straightforward loading of the URL in the argument and to replace the contents of the title and .container elements with those retrieved remotely.
The live call means that any link clicked on the page will trigger the callback, and the callback pushes the state to the href in the link and calls replacePage. It also uses e.preventDefault to prevent the link from being processed the normal way.
Finally, there's a popstate event that fires when a user uses browser-based page navigation (back, forward). We bind a simple callback to that event. Of note is that I couldn't get the version on the Dive Into HTML page to work for some reason in FF for Mac. No clue why.
How to extend it
This extremely basic example can more or less be transplanted onto any site because it does a very uncreative transition: HTML replacement. I suggest you can use this as a foundation and transition into more creative transitions. One example of what you could do would be to emulate what Github does with the directory navigation in its repositories. It's an intermediate manoever that requires floats and overflow management. You could start with a simpler transition like appending the .container in the loaded page to the DOM and then animating the old container to {height: 0}.
Addressing your specific "For example"
You're on the right track for using HTML5 history, but you need to clarify your idea of exactly what /foo and /bar will contain. Basically, you're going to have three pages: /, /foo, and /bar. / will have an empty container div. /foo will be identical to / except in that container div has some foo content in it. /bar will be identical to /foo except in that the container div has some bar content in it. Now, the question comes to how you would extract the contents of the container through Javascript. Assuming that your /foo body tag looked something like this:
<body>
foo
bar
<div class="container">foo</div>
</body>
Then you would extract it from the response data through var html = $(data).filter('.container').html() and then put it back into the parent page through $('.container').html(html). You use filter instead of the much more reasonable find because from some wacky reason, jQuery's DOM parser produces a jQuery object containing every child of the head and every child of the body elements instead of just a jQuery object wrapping the html element. I don't know why.
The rest is just adapting this back into the "vanilla" version above. If you are stuck at any particular stage, let me know, and I can guide you better though it.
Code
https://github.com/cairo140/html5-history-example
Try this in your controller:
if (!$this->input->is_ajax_request())
$this->load->view('header');
$this->load->view('your_view', $data);
if (!$this->input->is_ajax_request())
$this->load->view('footer');