Skip to content

Commit

Permalink
feat(util): add webcomponentsReady function to help bootstrap apps
Browse files Browse the repository at this point in the history
  • Loading branch information
hotforfeature committed May 4, 2017
1 parent 0396221 commit aa093a1
Show file tree
Hide file tree
Showing 11 changed files with 108 additions and 63 deletions.
31 changes: 9 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,15 @@ Make sure bower components are installed to a directory that is included in the
Next install Polymer and any other custom elements.

```
$ bower install --save Polymer/polymer#2.0.0-rc.5
$ bower install --save Polymer/polymer#^2.0.0-rc.7
$ bower install --save webcomponents/webcomponentsjs#^1.0.0-rc.11
```

Projects should add the `bower_components/` directory to their `.gitignore` file.

### Polyfills

When targeting browsers that do not natively support WebComponents, polyfills are required. The app must wait for the `WebComponentsReady` event before bootstrapping.
When targeting browsers that do not natively support WebComponents, polyfills are required. The app must wait for the polyfills before bootstrapping.

Origami recommends using the `webcomponents-loader.js` polyfill. This script will check for native browser support before loading the required polyfills.

Expand All @@ -96,39 +97,25 @@ index.html
<head>
<title>Paper Crane</title>

<!-- webcomponentsjs is included when installing Polymer -->
<script src="assets/bower_components/webcomponentsjs/webcomponents-loader.js"></script>
</head>
<body>
<app-root>Loading...</app-root>
<script>
// For browsers that support webcomponents, the loader will immediately fire the ready event
// before Angular bootstraps. This flag will let main.ts know to continue rather than wait for
// the event.
window.webComponentsReady = false;
window.addEventListener('WebComponentsReady', function() {
window.webComponentsReady = true;
});
</script>
</body>
</html>
```

main.ts
```ts
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { webcomponentsReady } from '@codebakery/origami';

function bootstrap() {
webcomponentsReady().then(() => {
platformBrowserDynamic().bootstrapModule(AppModule);
}

if ((<any>window).webComponentsReady) {
// Polyfills not needed
bootstrap();
} else {
// Wait for polyfills before bootstrapping
window.addEventListener('WebComponentsReady', bootstrap);
}
}).catch(error => {
// No WebComponent support and webcomponentsjs is not loaded
console.error(error);
});
```

### Templates
Expand Down
3 changes: 2 additions & 1 deletion bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"homepage": "https://github.com/hotforfeature/origami",
"private": true,
"dependencies": {
"polymer": "Polymer/polymer#^2.0.0-rc.6",
"polymer": "Polymer/polymer#^2.0.0-rc.7",
"webcomponentsjs": "webcomponents/webcomponentsjs#^1.0.0-rc.11",
"paper-input": "PolymerElements/paper-input#2.0-preview",
"paper-checkbox": "PolymerElements/paper-checkbox#2.0-preview",
"iron-selector": "PolymerElements/iron-selector#2.0-preview"
Expand Down
1 change: 1 addition & 0 deletions demo/bower.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"name": "origami-demo",
"dependencies": {
"polymer": "Polymer/polymer#^2.0.0-rc.5",
"webcomponentsjs": "webcomponents/webcomponentsjs#^1.0.0-rc.11",
"paper-tabs": "PolymerElements/paper-tabs#2.0-preview",
"app-layout": "PolymerElements/app-layout#2.0-preview",
"paper-styles": "PolymerElements/paper-styles#2.0-preview",
Expand Down
6 changes: 0 additions & 6 deletions demo/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,5 @@
</custom-style>

<app-root>Loading...</app-root>
<script>
window.webComponentsReady = false;
window.addEventListener('WebComponentsReady', function() {
window.webComponentsReady = true;
});
</script>
</body>
</html>
15 changes: 5 additions & 10 deletions demo/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { webcomponentsReady } from './origami/origami';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';
Expand All @@ -8,14 +9,8 @@ if (environment.production) {
enableProdMode();
}

function bootstrap() {
webcomponentsReady().then(() => {
platformBrowserDynamic().bootstrapModule(AppModule);
}

if ((<any>window).webComponentsReady) {
// Polyfills not needed
bootstrap();
} else {
// Wait for polyfills before bootstrapping
window.addEventListener('WebComponentsReady', bootstrap);
}
}).catch(error => {
console.error(error);
});
12 changes: 0 additions & 12 deletions docs/importing-elements.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,6 @@ index.html
</head>
<body>
<app-root>Loading...</app-root>
<script>
window.webComponentsReady = false;
window.addEventListener('WebComponentsReady', function() {
window.webComponentsReady = true;
});
</script>
</body>
</html>
```
Expand Down Expand Up @@ -55,12 +49,6 @@ index.html
</head>
<body>
<app-root>Loading...</app-root>
<script>
window.webComponentsReady = false;
window.addEventListener('WebComponentsReady', function() {
window.webComponentsReady = true;
});
</script>
</body>
</html>
```
6 changes: 0 additions & 6 deletions docs/polymer-templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,6 @@ index.html
</dom-module>

<app-root>Loading...</app-root>
<script>
window.webComponentsReady = false;
window.addEventListener('WebComponentsReady', function() {
window.webComponentsReady = true;
});
</script>
</body>
</html>
```
Expand Down
6 changes: 0 additions & 6 deletions docs/production-build.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,6 @@ index.html
</head>
<body>
<app-root>Loading...</app-root>
<script>
window.webComponentsReady = false;
window.addEventListener('WebComponentsReady', function() {
window.webComponentsReady = true;
});
</script>
</body>
</html>
```
Expand Down
1 change: 1 addition & 0 deletions src/origami.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export * from './style/custom-style.service';
export * from './templates/polymer-template.directive';
export * from './util/customElements';
export * from './util/Polymer';
export * from './util/webcomponents';
20 changes: 20 additions & 0 deletions src/util/webcomponents.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import {} from 'jasmine';

import { webcomponentsReady, webcomponentsSupported } from './webcomponents';

describe('webcomponentsReady', () => {
it('should return a Promise', () => {
expect(webcomponentsReady()).toEqual(jasmine.any(Promise));
});

it('should not create multiple Promises', () => {
const result = webcomponentsReady();
expect(webcomponentsReady()).toBe(result);
});
});

describe('webcomponentsSupported', () => {
it('should return true in test environment', () => {
expect(webcomponentsSupported()).toBe(true);
});
});
70 changes: 70 additions & 0 deletions src/util/webcomponents.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
declare global {
interface Window {
Promise: PromiseConstructor;
WebComponents: {
ready: boolean;
};
}
}

export function webcomponentsSupported(): boolean {
// HTML imports
/* istanbul ignore if */
if (!('import' in document.createElement('link'))) {
return false;
}

// Shadow DOM
/* istanbul ignore if */
if (!('attachShadow' in Element.prototype && 'getRootNode' in Element.prototype)) {
return false;
}

// Custom elements
/* istanbul ignore if */
if (!window.customElements) {
return false;
}

// Templates
/* istanbul ignore if */
if (!('content' in document.createElement('template')) ||
// Edge has broken fragment cloning which means you cannot clone template.content
!(document.createDocumentFragment().cloneNode() instanceof DocumentFragment)) {
return false;
}

return true;
}

// webcomponents-lite.js will override Promise. Angular provides its own Promise polyfill, so we
// want to make sure we keep that.
const OriginalPromise = window.Promise; // tslint:disable-line:variable-name
let readyPromise: Promise<void>;

export function webcomponentsReady(): Promise<void> {
if (!readyPromise) {
/* istanbul ignore next */
readyPromise = new Promise<void>((resolve, reject) => {
if (window.WebComponents) {
if (window.WebComponents.ready) {
window.Promise = OriginalPromise;
resolve();
} else {
// tslint:disable-next-line:only-arrow-functions
document.addEventListener('WebComponentsReady', function onready() {
window.Promise = OriginalPromise;
resolve();
document.removeEventListener('WebComponentsReady', onready);
});
}
} else if (webcomponentsSupported()) {
resolve();
} else {
reject(new Error('WebComponent support or polyfills are not present'));
}
});
}

return readyPromise;
}

0 comments on commit aa093a1

Please sign in to comment.