{"id":20186113,"url":"https://github.com/leolanese/angular-web-elements","last_synced_at":"2026-05-02T08:32:45.346Z","repository":{"id":220373957,"uuid":"667339989","full_name":"leolanese/Angular-Web-Elements","owner":"leolanese","description":"Angular (16+) Web Elements POC","archived":false,"fork":false,"pushed_at":"2025-03-18T16:20:37.000Z","size":233,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-01-13T03:33:21.411Z","etag":null,"topics":["angular","css-grid","html5","modernangular","reactjs","standalone"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/leolanese.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2023-07-17T09:31:42.000Z","updated_at":"2025-08-14T09:41:51.000Z","dependencies_parsed_at":"2024-02-01T19:10:18.312Z","dependency_job_id":"a0cb4bf6-af25-4b13-87b7-3ed30eb2eeb5","html_url":"https://github.com/leolanese/Angular-Web-Elements","commit_stats":null,"previous_names":["leolanese/angular-web-elements"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/leolanese/Angular-Web-Elements","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leolanese%2FAngular-Web-Elements","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leolanese%2FAngular-Web-Elements/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leolanese%2FAngular-Web-Elements/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leolanese%2FAngular-Web-Elements/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/leolanese","download_url":"https://codeload.github.com/leolanese/Angular-Web-Elements/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/leolanese%2FAngular-Web-Elements/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32528223,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-02T01:12:54.858Z","status":"online","status_checked_at":"2026-05-02T02:00:05.923Z","response_time":132,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["angular","css-grid","html5","modernangular","reactjs","standalone"],"created_at":"2024-11-14T03:16:01.940Z","updated_at":"2026-05-02T08:32:45.289Z","avatar_url":"https://github.com/leolanese.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Angular Web Component\n\n## Angular Elements\n- The project leverages Angular Elements to package Angular components as custom HTML elements (Web Components). This allows these components to be used in any web application, regardless of the underlying framework (or lack thereof). This is a key point, the main goal is to `have a component that can be reused in any web environment`.\n\n## Standalone Components:\n- The project underline the use of standalone components, a feature introduced in newer Angular versions. This simplifies module management and improves tree-shaking, resulting in smaller bundle sizes. This removes the need of NgModules in many cases.\n\n## Internationalization (i18n):\n- The project demonstrates how to implement i18n in Angular Elements using Angular's built-in i18n features.\n- It covers extracting translation strings, creating translation files, and building localized versions of the application.\n\n## Lazy Loading:\n- The project shows how to lazy-load routes and standalone web components, improving application performance by loading components only when needed.\n- It also shows how to lazy load entire modules.\n\n## Preloading Strategies:\n- The project shows how to preload component data, using resolvers.\n\n## Build and Deployment:\n- It provides instructions on building the Angular Element for production, including generating a single JavaScript file that can be included in any HTML page.\n- It also shows how to build and serve multilingual versions of the application.\n\n## Reverse Proxy:\n- It shows how to configure a reverse proxy using Express and http-proxy-middleware to handle API requests.\n\n## Project Goals:\n- To demonstrate how to create reusable Angular Web Components.\n- To showcase modern Angular features and best practices.\n- To provide a POC for building multilingual Angular Elements.\n- To illustrate how to lazy-load components and modules.\n- To show how to configure a reverse proxy.\n\n---\n\n## Setup environment\n\n```js\nnvm ls node\nnvm i 18.10\n\nnpm i -g @angular/cli@latest\n\nng new my-angular-element\n\ncd my-angular-element\n\nnpm i @angular/elements \n```\n\n### Would you like to add Angular routing?\n\nAdding Angular routing to an application built with Angular Elements can be a bit complex, and may not be necessary depending on your use case.\n\nWhen you build an application using Angular Elements, the main use case is to create \"reusable\", \"standalone\" components that can be used in any web application, regardless of the framework it's built with. These components are often \"isolated\", meaning they don't depend on the rest of the Angular application and don't use services like the Angular Router\n\n## i18n\n\nTo make the code i18n compliant, we'll use `Angular's built-in internationalization` (i18n) features\n\n```js\n// i18n extraction requires the '@angular/localize' package.\nnpm i --save-dev @angular/localize\n```\n\n```js\n// tsconfig.json\n\"compilerOptions\": {\n    \"paths\": {\n      \"@angular/localize/*\": [\n        \"./node_modules/@angular/localize/*\"\n      ]\n    }\n  }\n```\n\n```js\n// example i18n tags in place\n\u003ch1 i18n\u003eHello {{ state?.name }}\u003c/h1\u003e\n\u003cp i18n\u003eAge: {{ state?.age }}\u003c/p\u003e\n```\n\n```js\n// now, let's extract the translation strings and generate: `messages.xlf`\nng extract-i18n\n```\n\n### messages.xlf\n\n```js\n...\n\u003csource\u003eHello: \u003cx id=\"INTERPOLATION\" equiv-text=\"{{ state?.value1 }}\"/\u003e\u003c/source\u003e\n...\n```\n\n- Having the `tags` elements extracted to `messages.xlf`\n- Translate the extracted strings in the `messages.xlf` file to different language(s)\n- Create separate translation files for each supported language. For example, you can create a `messages.en.xlf` file for English, `messages.es.xlf` for Spanish, and so on.\n- Update: `angular.json` to include these new translation files within i18n section:\n\n```js\n// angular.json\n\n```\n- Build App using `--localize` flag to generate the localiex versions of the App\n```js\nng build --localize\n```\n- Serve the application with the appropriate locale\n```js\nng serve --configuration=es\nng serve --configuration=en\n```\n\n\n## Create a New Component\n\n```js\nng g c hello-world\n```\n\n\u003e Now, let's include the createCustomElement and `MyElementModule` functionality\n\n\n## Build\n\n```js\nng build --configuration production --output-hashing none\n```\n\n\u003e Now, we created a single JavaScript file in your `/dist` directory. This file is your custom element and can be used in any HTML file\n\n## `Build` \u0026 `Serve` multilingual `i18n`\n\n\u003e By using the `ng build --localize` command followed by `ng serve --configuration=locale`, you can test the i18n functionality without performing a full build\n\n```js\n// build '/dist/es'\nng build --configuration=es\n\n// build '/dist/en'\nng build --configuration=en\n\n// also we can build all the languages at ones\n// build '/dist/en' + '/dist/es'\nng build --localize\n\n// After running this command use localized version(s) of your application\n// Serve the '/dist/es'\nng serve --configuration=es\n```\n\n## Serve both version at the same time\n\nFor testing porpouses, we can serve the English version: `http://localhost:4201` and the Spanish version: `http://localhost:4202`\n\n```js\nng serve --port 4201 --configuration=en\nng serve --port 4202 --configuration=es\n```\n\n---\n\n## Consume the Angular Element\n\n```js\n// /dist/index.html\n\u003cscript src=\"main.js\"\u003e\u003c/script\u003e\n\u003capp-hello-world name=\"World\"\u003e\u003c/app-hello-world\u003e\n```\n\n```js\n// install http-server global\nnpm install -g http-server\n\n// on project root\nhttp-server dist/my-angular-element\n```\n\n## Reverse Proxy\n\n```js\n// install express + http-proxy-middleware\nnpm install express http-proxy-middleware --save\n```\n\n```js\nconst express = require('express');\nconst { createProxyMiddleware } = require('http-proxy-middleware');\n\nconst app = express();\n\napp.use('/services', createProxyMiddleware({ \n  target: 'https://services.lttwdev.slcom-tws.com/services', \n  changeOrigin: true \n}));\n\napp.listen(3000);\n```\n\n```js\n// package.json\n\"reverse-proxy\": \"node server.js\"\n```\n\n\u003e now your app is running on: localhost:8080, so we have to update your request to: http://localhost:8080/services/login-service/v1/login\n\n## Servers up and running\n\nRun `node server.js` for Express server at `:8080` (which has the reverse proxy setup)\nRun `ng serve` for Angular application at `:4200`\n\n\n---\n\n## POC Extra ++\n\n### 1) CommonModule\n\n\u003e We don't need the `CommonModule` if you are using a standalone components.\n\nThis offers several benefits, including improved `tree-shaking`. For example, we can now import only the specific directives and pipes that we need, such as `NgIf` and `NgFor`, eliminating the need to import the entire CommonModule along with other unnecessary pipes and directives (this will reduce the size of the final bundle by including only the code that is actually used in the application)\n\n```js\n// CommonModule\nBrowserModule\n  BrowserModule \n      NgClass\n      NgStyle\n      NgIf  \u003c-- JIC we have an `if` in a Component\n      NgForOf\n      NgSwitch\n      NgSwitchCase\n      NgSwitchDefault\n      NgPlural\n      NgLocalization\n      NgTemplateOutlet\n      NgContent\n      Pipes\n\n      DatePipe\n      DecimalPipe\n      PercentPipe\n      UppercasePipe\n      LowercasePipe\n      TitleCasePipe\n      JsonPipe\n      SlicePipe\n      AsyncPipe \u003c-- JIC we have an `| async pipe`\n\n  FormsModule\n  HttpClientModule\n  RouterModule\n  UpgradeModule (used to support legacy applications that were created using AngularJS)\n\nDomSanitizerModule\n  DomSanitizer\n\nFormsModule (Directives and pipes used to create, validate, and manipulate template-driven forms)\n  FormsModule\n    NgModel\n    NgModelGroup\n    FormBuilder\n    FormGroup\n    FormControl\n    FormArray\n    Validators\n    AsyncValidators\n  ReactiveFormsModule (Directives and pipes are used to create, validate, and manipulate reactive forms)\n    FormBuilder\n    FormGroup\n    FormControl\n    FormArray\n    Validators\n    AsyncValidators\n\nInputModule ( if you are creating a component that does not have any input elements, then you do not need to import the InputModule)\n  InputModule\n\nNgModule\n  NgModule\n\nNgZoneModule (provides a way to manage the Angular ZoneJS)\n  NgZone (OnPush-Change-Detection strategy 'do not' required NgZoneModule. Because Angular does not need to 'track asynchronous events' to perform Change Detection)\n\nTitleModule\n  TitleModule\n\nUrlSerializerModule (serialize and deserialize URL parameters)\n  UrlSerializer\n```\n\n\n### 2) lazy-load routes\n\n\u003e Routes will `lazy-load` their `standalone web components` by using the `loadComponent` function (internally loadComponent function returns a promise. This promise resolves to the loaded component when it is ready)\n\n\u003e Angular 16+ also uses: `Lazy-loading feature modules` allows us to `lazy-load entire modules, rather than just individual components`\n\n```js\n//  lazy loading for children based on routes\nimport { Routes } from \"@angular/router\";\nimport { AppComponent } from \"./app.component\";\n\nexport const APP_ROUTES: Routes = [\n    {\n        path : '',\n        children : [\n            {\n                path: 'dogs',\n                loadComponent: () =\u003e import('./dogs/dogs.component')\n                                           .then(c =\u003e c.DogsComponent)\n\n            },\n            {\n                path: 'cats',\n                loadChildren: () =\u003e import('./cars/cats.routes')\n                                            .then(r =\u003e r.CatsComponent)\n\n            },\n            {\n                // also we can use the loadChildren function to lazy-load the feature module\n                loader: 'async',\n                path: './my-feature/my-feature.module',\n            },\n            \n\n        ]\n    }\n]\n```\n\nNow, we can trigger: `ng build` to view the `Initial Chunk files` + `Lazy Chunk Files` separated:\n\n```js\n// AOT and Lazy-Loading activated\n✔ Browser application bundle generation complete.\n✔ Copying assets complete.\n✔ Index html generation complete.\n\nInitial Chunk Files | Names                     |  Raw Size | Estimated Transfer Size\nmain.js             | main                      | 296.42 kB |                79.97 kB\npolyfills.js        | polyfills                 |  33.03 kB |                10.61 kB\nruntime.js          | runtime                   |   2.64 kB |                 1.23 kB\nstyles.css          | styles                    |   0 bytes |                       -\n\n                    | Initial Total             | 332.09 kB |                91.80 kB\n\nLazy Chunk Files    | Names                     |  Raw Size | Estimated Transfer Size\n946.js              | display-display-component |   1.54 kB |               733 bytes\n360.js              | c1-c1-component           | 630 bytes |               389 bytes\n561.js              | c2-c2-component           | 538 bytes |               329 bytes\n```\n\n### `Preloading Strategies` with standalone applications \n\n#### Preloading component data\n\n\u003e Preloading improves UX by loading parts of your application in the background. You can preload modules, standalone components or component data.\n\n\u003e We preload component data, using a resolver. Resolvers can improve UX by blocking the page load until all necessary data is available to fully display the page, BUT all the necesary content will be require on `initial page`, so \"it may no be suttable for all solutions\"\n\n---\n\n### Before leaving!\n\nOnce you have defined your Angular Element, you can use it like any other custom HTML element. This is one of the primary advantages of Angular Elements, as they can be used in any web environment that supports Web Components, not just Angular.\n\n---\n\n## Running unit tests\n\nRun `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).\n\n## Running end-to-end tests\n\nRun `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.\n\n## Further help\n\nTo get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.\n\n---\n### :100: \u003ci\u003eThanks!\u003c/i\u003e\n#### Now, don't be an stranger. Let's stay in touch!\n\n\u003ca href=\"https://github.com/leolanese\" target=\"_blank\" rel=\"noopener noreferrer\"\u003e\n  \u003cimg src=\"https://scastiel.dev/api/image/leolanese?dark\u0026removeLink\" alt=\"leolanese’s GitHub image\" width=\"600\" height=\"314\" /\u003e\n\u003c/a\u003e\n\n##### :radio_button: Linkedin: \u003ca href=\"https://www.linkedin.com/in/leolanese/\" target=\"_blank\"\u003eLeoLanese\u003c/a\u003e\n##### :radio_button: Twitter: \u003ca href=\"https://twitter.com/LeoLanese\" target=\"_blank\"\u003e@LeoLanese\u003c/a\u003e\n##### :radio_button: Portfolio: \u003ca href=\"https://www.leolanese.com\" target=\"_blank\"\u003ewww.leolanese.com\u003c/a\u003e\n##### :radio_button: DEV.to: \u003ca href=\"https://www.dev.to/leolanese\" target=\"_blank\"\u003edev.to/leolanese\u003c/a\u003e\n##### :radio_button: Blog: \u003ca href=\"https://www.leolanese.com/blog\" target=\"_blank\"\u003eleolanese.com/blog\u003c/a\u003e\n##### :radio_button: Questions / Suggestion / Recommendation: developer@leolanese.com\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleolanese%2Fangular-web-elements","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fleolanese%2Fangular-web-elements","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fleolanese%2Fangular-web-elements/lists"}