15

We have an existing Angular 8 project which includes a bunch of common components (like custom datepickers, numeric inputs, ...). The project itself is a standard Angular-app.

We want to extract some of these components as Angular elements that they can be used in other Angular apps or even other projects which are not Angular related.

Is it possible to build the project in a way that only the code specific to a single Angular element is included in the generated JavaScript files?

What I need is something like this: ng build --element myDatePicker

This should generate all the JavaScript files for the myDatePicker-Element only with all the needed dependencies but without any other code from the project (like other components, modules, ...).

The whole app should still normally build when I use ng build.

Sangwin Gawande
  • 7,658
  • 8
  • 48
  • 66
Stefan
  • 1,007
  • 1
  • 11
  • 32

7 Answers7

9

I suggest you to take a look at Angular Elements, more specifically createCustomElement. This will help you achieve exactly what you need.

I can give you a short guide on what you could do.

My Github Repo for example

1) First thing is to install @angular/elements

npm i @angular/elements --save

2) This step includes modifying your app.module.ts. If you want to build out only a set of chosen components (custom date-pickers, numeric inputs, etc...) you will have to remove the bootstrap: [ ] for a moment and replace it with entryComponents: [ ]. In entryComponents you will put the custom components you want to build.

entryComponents: [MyCustomDatePickerComponent, CustomButtonComponent]

3) Now it's time to tell the Angular to bootstrap your custom components. You will also have to define the selector for your custom components. You will call them with that selector from any other app where you inject them. Ex: <my-button-element></my-button-element>

export class AppModule {

  constructor(private injector: Injector) {}

  ngDoBootstrap() {
    const el = createCustomElement(CustomButtonComponent,
      { injector: this.injector });
    customElements.define('my-button-element', el);
    const el2 = createCustomElement(MyCustomDatePickerComponent,
      { injector: this.injector });
    customElements.define('custom-date-picker', el2);
  }
}

4) Building the components - If you run ng build --prod you will get multiple files. We want to merge those files in one to allow easier use and injection of custom elements we've built. There are two packages that can help us do that. npm install --save-dev concat fs-extra

Create an elements-build.js in root of your application and paste this. (It will merge the build output in one .js and .css file and put them in root/elements folder.

const concat = require('concat');
const fs = require('fs-extra');

(async function build() {
  // Instead of /CustomElements/ put your project name - just check a dist folder to see how its formatted and make it that way
  const files = [
    './dist/CustomElements/runtime-es2015.js',
    './dist/CustomElements/polyfills-es2015.js',
    './dist/CustomElements/main-es2015.js'
  ];

  await fs.ensureDir('elements');
  await concat(files, 'elements/myCustomElements.js');
  await fs.copyFile(
    './dist/CustomElements/styles.css',
    'elements/styles.css'
  );
})();

5) In your package.json add

"build:elements": "ng build --prod --output-hashing none && node elements-build.js",

6) That's it. You've build your custom elements and can reuse them anywhere in other Angular apps or even in non Angular apps. The only thing you have to do is to import the .js and .css you just created. You can even use the Input() & Output() as you would normally.

<custom-date-picker dateTitle="My Date Title"></custom-date-picker>

NOTE: If you decide to import it to another Angular application, you might have problems with zone.js as it will be imported twice. The solution is to import it only once (whether from core app, or an custom element).

Dino
  • 7,779
  • 12
  • 46
  • 85
  • 3
    This does not really solve my problem. I know how to create an angular element. My problem is that I somhow want to make a build with this custom component as starting point. This sould lead in a package which only includes the element and the needed dependencies. Currently the only approach I can see is to create an own Angular-Library for each component. – Stefan Aug 15 '19 at 15:13
  • First of I dont know why you bundle multiple components into one file, this was not asked. Also I dont think the answer answers the question as it shows how to create custom elements but not how to keep the app AND have separate custom element build commands. I downvoted your answer. – Mick Mar 03 '21 at 08:32
6

For component reusability, you can extract your components in a "common" npm dependency. The Angular doc is found here: https://angular.io/guide/creating-libraries

Keep in mind you will have to build your library project, then publish the package on an npm registry (the public one or a private one) to be used as a dependency.

To use your Angular components outside Angular, you can use Angular Elements to turn your components into standard WebComponents (https://angular.io/guide/elements).

Keep in mind that you will still need some sort of build pipeline to turn your Angular code into JS.

Videl
  • 840
  • 2
  • 7
  • 18
  • 1
    I'd have really loved to hear how the re-usability and Angular Elements parts worked together, and this "some sort of build pipeline". This answer is too high-level to be of any help to me, unfortunately. – Kevin Beal Mar 26 '20 at 16:17
  • @KevinBeal A gitlab CI/Jenkins pipeline/a human that executes `ng build`, cd into the built folder, and `npm publish` your package. That will generate html/js/css files that are usable. – Videl May 14 '20 at 09:59
1

There are many things that you need to do I would suggest to read this here

Also I would suggest to create a new project for those shared components that you want to export.

Then use them in your existing project as element. (You need to reference a javascript, a css file and html tag for your components). for example like this

    <link rel="stylesheet" href="https://xybot-v1-dev.web.app/dist/dlx-styles-1.0.css">

    <dlx-chat-fab 
      bot-name="Condo Bot" 
      popup-text="Hi I am your virtual concierge." 
      theme="blue_deep-orange" 
      agent="condo-dev">
    </dlx-chat-fab>

     <script type="text/javascript" src="https://xybot-v1-dev.web.app/dist/dlx-chatbot-1.0.js"></script>

and you should be ok.

Reza
  • 18,865
  • 13
  • 88
  • 163
0

you looking for module bundler for JavaScript which compiles small pieces of code into something larger and more complex, such as a library or application. try RollupJS : rollup.js guide

These commands assume the entry point to your application is named main.js, and that you'd like all imports compiled into a single file named bundle.js.

For browsers:

compile to a containing a self-executing function

$ rollup main.js --file bundle.js --format iife

Thedrcx
  • 5
  • 4
  • Actually no, hw wants to build a component of his exisiting as a web component. Not bundle anything. – Mick Mar 03 '21 at 08:35
0

Adding the angular component to a non-angular app was a little tricky, and came with some drawbacks. You may even want to consider creating certain components without Angular, and then import them into the angular project rather than the other way around.

I had an angular project (project A) and a jquery project (project B) and I wanted to share a component between the two. I was able to do this by creating a bare bones (no router, no extra dependencies) project (project C) and migrating the component from project A to C. Consuming the component from C by project A was relatively simple because they were both angular. This was before Angular 6. Now in Angular 6 or so they even created a ng generate library command in the CLI. Also check out this answer here for more info.

Angular has some requirements and when you build the app it will create some files in the dist folder for shims, the angular framework, vendor bundle, and your shared component code. If your shared component is consumed by your non-angular application, you will need to reference these files, because your component was written in Angular and so will require the presence of Angular to run. You might as well just add Angular to your non-Angular app at this point. I did this from my jQuery app and then used a combination of sending custom events and receiving events in Angular through Rxjs.

Jonathan Walton
  • 449
  • 3
  • 18
  • Thats a way how to share components, but not what was asked. He wants to build a component of his exisiting as a web component that can be used in any web project. – Mick Mar 03 '21 at 08:36
0

I have wrote an article about that: https://micklawitzke.medium.com/build-web-components-out-of-an-existing-angular-app-with-angular-elements-7dacf84b2fb3

Basically what you need is another app for each web component you want to extract in your Angular project. Start by adding a new app to your project:

ng generate application MyWebComponent --routing=false --skipInstall=true

Now you have a second app within a newly generated projects folder. This new app will be the web component / Angular element. There you can import everything you want from your base app.

You can then build your base app as usual with ng start or ng build --prod, because it is defined as the default project in angular.json, and do the same for each web component app by referencing its name, in this example: ng start MyWebComponent or ng build MyWebComponent --prod

Mick
  • 8,203
  • 10
  • 44
  • 66
  • Nice article. The only thing I cannot yet get working is to reuse the actual environment file from the base app. Although you explain to add the path to the tsconfig, somehow in the main file is not picked up and the angular.json needs also modifying to get the transform of the files correctly. Very helpful article though, well done! – Carlos Torrecillas Mar 15 '22 at 09:41
-1

You can create and publish new libraries to extend Angular functionality. If you find that you need to solve the same problem in more than one app (or want to share your solution with other developers), you have a candidate for a library.

The command for generating a library is ng generate library my-lib

A library typically includes reusable code that defines components, services, and other Angular artifacts (pipes, directives, and so on) that you simply import into a project. A library is packaged into an npm package for publishing and sharing, and this package can also include schematics that provide instructions for generating or transforming code directly in your project, in the same way that the CLI creates a generic skeleton app with ng generate component. A schematic that is combined with a library can, for example, provide the Angular CLI with the information it needs to generate a particular component defined in that library.

Source: https://angular.io/guide/creating-libraries

Bytech
  • 1,125
  • 7
  • 22
  • He does not want to create a library. He wants to build a component of his exisiting as a web component. – Mick Mar 03 '21 at 08:34