-
Notifications
You must be signed in to change notification settings - Fork 22
Web components
[From wikipedia]
Web Components are a set of features currently being added by the W3C to the HTML and DOM specifications that allow for the creation of reusable widgets or components in web documents and web applications. The intention behind them is to bring component-based software engineering to the World Wide Web. The components model allows for encapsulation and interoperability of individual HTML elements.
Web Components consist of 4 main features which can be used separately or all together:
- Custom Elements - APIs to define new HTML elements
- Shadow DOM - Encapsulated DOM and styling, with composition
- HTML Imports - Declarative methods of importing HTML documents into other documents
- HTML Templates - The
Integrate a web component in an Angular 2 application is very easy. It depends on web component type.
SystemJs is the most used module loader in many of the Angular 2 examples currently available.
- Configure systemjs.config.js
System.config({ map: { ..., 'angular2-library-example': 'node_modules/angular2-library-example', ... }, packages: { ..., 'angular2-library-example': { main: 'index' } } });
- Bootstrap the app (index.html)
System.import('build/App')
- Bootstrap the app (index.html)
`<script src="bundle.js"></script>`
- Bundle is created according to the following webpack configuration
module.exports = { entry: "./src/app.ts", output: { filename: "bundle.js" }, devtool: 'source-map', resolve: { extensions: ['', '.webpack.js', '.web.js', '.ts', '.js'] }, module: { loaders: [ { test: /\.ts$/, loader: 'ts-loader' } ] } };
- Add the polymer webcomponent references in the index.html
`<script src="bower_components/webcomponentsjs/webcomponents.min.js"></script>`2. Enable shadowDOM (index.html)
`<script> window.Polymer = { dom: 'shadow' }; </script>`3. Import web component (index.html)
``
There are 2 ways to see this :
- either we want to use a simple/single component
- either we want to use a full module
Angular-cli seems to be a great way creating angular 2 app/components. But I couldn't install it on my machine. But you should definitely use it if possible.
Suppose we have created a SuperComponent like this:
import { Component } from 'angular2/core'; import { SuperService } from 'app/super.service'; @Component({ selector: 'super-component', providers : [SuperService], styles : [` div { background-color:#EFEFEF; margin-bottom:15px; padding:15px; border:1px solid #DDD; box-shadow:2px 2px 2px 0 rgba(0, 0, 0, 0.3); border-radius:3px; } h2 { text-align: center; } `], template : ` <h2>Hello from the {{componentName}}!</h2> <div *ngFor="#s of stuff"> <h4> Name : {{s.name}} </h4> <h4>Age: {{s.age}}</h4> </div> ` }) export class SuperComponent { componentName: 'SuperComponent'; //Assign constructor(_superService: SuperService ) { this.stuff = _superService.getStuff(); } }
Note that it is better to keep at least html template code inside the same file for exporting/sharing the component later. It forces developers to create "simple" component or at least with minimum of code.
Let's say that this SuperComponent belongs to an app with an app.module.ts file:
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; import { HttpModule } from '@angular/http'; import { AppComponent } from './app.component'; import { SuperComponent } from './super.component' @NgModule({ imports : [ BrowserModule, DtmModule, HttpModule, SuperComponent ], declarations : [ AppComponent ], exports: [SuperComponent], //==> SuperComponent must be also declared as to be exported providers : [], bootstrap: [ AppComponent ] }) export class AppModule { }
In the tsconfig.json file of your app, must add following lines:
{ "compilerOptions ": { "target": "es5", "module": "commonjs", "moduleResolution": "node", "sourceMap": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, "removeComments": false, "noImplicitAny": false, "outDir": "app/lib/super", "declaration": true } }
The "outDir" indicates the folder path where WebComponent files will be exported.
At the root of the app, create a components.js file to set which component from the lib folder will be publicly exposed. Like this:
exports.SuperComponent = require('./lib/super/SuperComponent').SuperComponent;
Then to use auto-completion with our component, we also need to create a component.d.js file at root folder of the module too:
export * from './lib/super/SuperComponent';
Then after a npm start (or maybe without), lib files for export should have been generated inside the lib/super folder:
- super.component.js
- super.component.js.map
- super.component.d.ts
There is a way to publish those components to npm but for now we'll only use them locally.
Go to the other angular project in which you want to use the component and put the lib/ folder and its content (super component files) inside the webapp/ folder for example.
We want to tell to the webapp that it can use a new component. In the systemjs.config.js, indicates new map and packages:
map: { .... 'super': 'lib/super' }, packages : { ... 'super ' : { main: './super.component.js', defaultExtension: 'js' }
Ok so now our app is configured to find our new super component. But we have to declare it inside the app module so it can be used by the module:
- We have to import and declare the super component inside the module
- We have to tell that module that we're going to use a custom component
Example of the app.module.ts file:
import { NgModule, CUSTOM_ELEMENTS_SCHEMA} from '@angular/core'; // ==> import CUSTOM_ELEMENTS_SCHEMA from angular core import { BrowserModule } from '@angular/platform-browser'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { HttpModule } from '@angular/http'; import { routing } from './app.routing'; import { AppComponent } from './app.component'; import { SuperComponent } from '../lib/super/super.component'; @NgModule({ imports : [ BrowserModule, FormsModule, HttpModule, routing ], declarations : [ AppComponent, SuperComponent ], providers : [ ], schemas: [ CUSTOM_ELEMENTS_SCHEMA ], // ==> add the schema bootstrap: [AppComponent] }) export class AppModule { }
Now that our SuperComponent is imported and declared inside the @NgModule, it can be used anywhere we need inside this module !!!
As we use CUSTOM_ELEMENTS_SCHEMA, we can now call it in html using
Instead of simply use components, we can create a full NgModule, export it, an share it between multiple apps.
We'll use the DTM as an example:
First thing is to separate DtmModule from the AppModule where it was "created". We want to have an independant module from the webapp.
Like the example here : https://angular.io/docs/ts/latest/guide/ngmodule.html #!#feature-modules
A module is fully independant and has its own routing "table". Be careful with conflicting routes like "home" for example...
Once everything has been set, it's pretty much like a simple component:
- Set the "export" folder in tsconfig.js file
"outDir": "app/lib/dtm", "declaration": true
Now when you modify a file in your app, typescript will compile it to the outDir. Be careful to run your app from there.
The app and all files regarding it can be build using npm and scripts in packages.json. If you open the packages.json file. There is a “scripts” section :
"scripts": { "start": "concurrently \"tsc -w\" \"lite-server\" ", "lite": "lite-server", "postinstall": "typings install", "tsc": "tsc", "tsc:w": "tsc -w", "typings": "typings", "htmlcss": "copyfiles app/**/*.html app/**/*.css lib/", "build": "rm -rf lib && tsc -p ." }
I added 2 scripts : “htmlccss” and “build”.
The build will remove and recreate the lib directory and export the module files inside the lib folder. The htmlcss will copy all html and css files to the lib folder. Just be careful with the path to have all the files you want in the right folders.
So first we need to install npm copyfiles :
npm install copyfiles –save-dev
And now we can run those scripts:
Go to the folder where are the tsconfig.json and package.json. The root of your app. And run the build script :
npm run build
Then finish by copying html and css
npm run htmlcss
All files exported for the dtm module should now be in the lib/dtm folder. You can now use it anywhere you want.
You can either use the whole dtm-app. Or use just the dtm module. It depends of the folder you share from the outDir lib folder : app or app/dtm.
HOW TO USE THIS EXTERNAL MODULE INSIDE ANOTHER APP?
Once we have our module in its folder, we can use it wherever we want. Copy/paste the dtm folder in a new webapp/lib folder inside the webapp where you want to use it.
webapp/ app/ node_modules/ … lib/ dtm/ systemjs.config.js ...
Then tell the systemjs.config.js that there is a new package to use:
map: { .... 'dtm': 'lib/dtm' }, packages : { ... 'dtm ' : { main: './dtm.module.js', defaultExtension: 'js' }
NOTE that this time the main file in package is dtm.module.js which is the "root" file of our dtm module.
Then we just have to import our new module in the "main" app, in app.module.ts:
import { DtmModule } from '../lib/dtm/dtm.module';
And declare it in the NgModule imported modules to use it:
@NgModule({ imports : [ BrowserModule, FormsModule, HttpModule, routing, DtmModule ], ...
AND THAT'S IT !
As our DtmModule already has its routing set inside itself, when we'll call /transfers or /dtm-home from the webapp, the corresponding components we'll be automatically called and displayed.
There's still work to do for HTML/CSS automatic export...
Documentation:
- http://blog.angular-university.io/how-to-create-an-angular-2-library-and-how-to-consume-it-jspm-vs-webpack/
- https://angular.io/docs/ts/latest/guide/ngmodule.html#!#feature-modules
- http://stackoverflow.com/questions/37631098/how-to-bundle-and-angular-2-app-for-production
- http://stackoverflow.com/questions/38708200/angular-2-typescript-compiler-copy-html-and-css-files