{"id":13661165,"url":"https://github.com/sparkles-dev/ng-aot-guide","last_synced_at":"2025-04-24T23:31:54.906Z","repository":{"id":94795949,"uuid":"95243045","full_name":"sparkles-dev/ng-aot-guide","owner":"sparkles-dev","description":"Dev guidelines for AoT-friendly Angular apps.","archived":true,"fork":false,"pushed_at":"2018-05-03T23:52:02.000Z","size":1166,"stargazers_count":8,"open_issues_count":6,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-11-10T15:45:34.746Z","etag":null,"topics":["angular","angular-aot","angular-demo","angular2","angular4","typescript"],"latest_commit_sha":null,"homepage":"https://medium.com/spektrakel-blog/angular-writing-aot-friendly-applications-7b64c8afbe3f","language":"TypeScript","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sparkles-dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2017-06-23T17:47:26.000Z","updated_at":"2023-01-28T20:44:38.000Z","dependencies_parsed_at":"2023-05-03T05:31:34.832Z","dependency_job_id":null,"html_url":"https://github.com/sparkles-dev/ng-aot-guide","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sparkles-dev%2Fng-aot-guide","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sparkles-dev%2Fng-aot-guide/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sparkles-dev%2Fng-aot-guide/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sparkles-dev%2Fng-aot-guide/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sparkles-dev","download_url":"https://codeload.github.com/sparkles-dev/ng-aot-guide/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250727707,"owners_count":21477353,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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","angular-aot","angular-demo","angular2","angular4","typescript"],"created_at":"2024-08-02T05:01:30.562Z","updated_at":"2025-04-24T23:31:49.891Z","avatar_url":"https://github.com/sparkles-dev.png","language":"TypeScript","readme":"Angular: writing AoT-friendly apps\n==================================\n\n\u003e [Read the full story on medium.com](https://medium.com/spektrakel-blog/angular-writing-aot-friendly-applications-7b64c8afbe3f)\n\n[![CircleCI](https://circleci.com/gh/spektrakel-blog/ng-aot-guide/tree/master.svg?style=svg)](https://circleci.com/gh/spektrakel-blog/ng-aot-guide/tree/master)\n[![Renovate enabled](https://img.shields.io/badge/renovate-enabled-brightgreen.svg)](https://renovateapp.com/)\n\n\nThis article is published under my [Spektrakel Blog on Medium.com](https://medium.com/spektrakel-blog/angular-writing-aot-friendly-applications-7b64c8afbe3f).\nIt describes development guidelines and instructions how to write AoT-compatible apps with the Angular CLI and talks through this topic by an example app.\nYou find the code for the example app in this repository.\n\n[![Angular: Writing AoT-friendly applications](./screenshots/chicken-app.png)](https://medium.com/spektrakel-blog/angular-writing-aot-friendly-applications-7b64c8afbe3f)\n\nHere is a short walk-through for the example:\n\n## Kick-start a project\n\nFirst, install the Angular CLI and bootstrap a new project:\n\n```bash\n$ npm install -g @angular/cli\n$ ng set --global packageManager yarn\n$ ng new ng-aot-guide\n$ cd ng-aot-guide\n$ yarn add spectre.css\n```\n\nGenerate some code for the demo application, an `NgModule`, a `Component`, and a `Service`.\n\n```bash\n$ ng generate module bttf\n$ ng generate component bttf\n$ ng generate service bttf/bttf\n```\n\nA first glance at the app:\n\n```bash\n$ ng serve\n```\n\nThen open [http://localhost:4200](http://localhost:4200) and you see a screen yelling \"_bttf works!_\"\n\n\n**NOTE**: _bttf_ is an abbreviation for _back to the future_.\nYes, it's some play of words... _ahead of time_, _back to the future_, well, err...\n\n\n#### Serving and Building – w/ AoT – w/o AoT\n\nBuild the application (by default w/o AoT):\n\n```bash\n$ ng build\n```\n\n![ng build](./screenshots/ng-build.png)\n\n\nThe Angular CLI outputs the build artefacts in the `dist` folder which now looks like this:\n\n![dist folder generated by ng build](./screenshots/ng-build-dist.png)\n\nBuild for production w/ AoT:\n\n```bash\n$ ng build --prod\n```\n\n\n## A chicken application\n\nCheckout the Git tag `baseline` or [take a look at that commit](./releases/tag/baseline).\n\nIn JiT compilation (`ng build --dev`), the application works fine.\nHowever, with AoT compilation (`ng build --prod`), we encounter several errors.\nWe will talk through this errors and look how to fix and void them.\n\n\n#### Common Mis-Take #1: Factory functions must be exported, named functions\n\nThe first error message is:\n\n```\nError encountered resolving symbol values statically. Function calls are not supported.\nConsider replacing the function or lambda with a reference to an exported function\n```\n\nIt is caused in `bttf.module.ts` by the factory provider:\n\n```ts\n{\n  provide: BttfService,\n  useFactory: () =\u003e new BttfService()\n}\n```\n\nWith AoT compilation, lambda expressions (arrow functions in TypeScript jargon) are not supported for writing factories!\nWe have to replace the factory with a plain-old function:\n\n```ts\nexport function myServiceFactory() {\n  return new BttfService();\n}\n\n@NgModule({\n  providers: [\n    {\n      provide: BttfService,\n      useFactory: myServiceFactory\n    }\n  ]\n})\nexport class BttfModule {}\n```\n\nYou can see the solution in the Git tag `fix-1`.\n[Take a look at it](releases/tag/fix-1)!\n\n***WARNING***: the Tour-of-Heroes on [angular.io](https://angular.io/) gives a non-working example for factory providers.\nThe document is [outdated](https://github.com/angular/angular/issues/17042) and [needs to be updated](https://github.com/angular/angular/pull/17081)!\n\n\n#### Common Mis-Take #2: bound properties must be public\n\nNow, when running `ng build --prod` again, another error shows up:\n\n```\nProperty '{xyz}' is private and only accessible within class '{component}'\n```\n\nThe error is caused by two places in `bttf.component.ts` and `bttf.component.html`.\nIn the HTML template, we have a text interpolation binding for the headline and an event binding for the form:\n\n```html\n\u003ch2\u003e{{ message }}\u003c/h2\u003e\n\n\u003cform #f=\"ngForm\" (ngSubmit)=\"onSubmit(f.controls.question.value)\"\u003e\n   ...\n\u003c/form\u003e\n```\n\nIn the component class, the corresponding code snippet is:\n\n```ts\n@Component({ .. })\nexport class BttfComponent {\n \n  private message: string = `Back to the future, again!`;\n\n  private onSubmit(value: string) {\n    /* .. */\n  }\n}\n```\n\nBoth the property and the method need to be public members!\nBy simply removing the `private` keyword, both will be public by default.\nSo, the fix for this error is:\n\n```ts\n@Component({ .. })\nexport class BttfComponent {\n \n  message: string = `Back to the future, again!`;\n\n  onSubmit(value: string) {\n    /* .. */\n  }\n}\n```\n\nIf you like to be even more explicit, you can declare a `public message: string` as well as a `public onSubmit()` method.\nThe solution is shown in Git tag `fix-2` [whose commit you find here](releases/tag/fix-2)!\n\n\n\n#### Common Mis-Take #3: call signatures for event bindings must match\n\nThere is one more issue with the application:\n\n```\nSupplied parameters do not match any signature of call target.\n```\n\nAgain, this error is caused by `BttfComponent` and its template.\nThe code part in the template is:\n\n```html\n\u003cbutton (click)=\"onAdd($event)\"\u003eAdd more {{ items }}\u003c/button\u003e\n```\n\nAnd its counter-part in the component class:\n\n```ts\n@Component({ .. })\nexport class BttfComponent {\n\n  onAdd() {\n    this.count += 1;\n  }\n\n}\n```\n\nNotice that `onAdd()` does not declare a method parameter.\nHowever, in the template, we try to pass the `$event` variable to the method.\nThis causes AoT compilation to fail and has two possible solutions.\nFirst, change the method implementation in the class to accept a paramter or remove the paramter from the event binding in the template.\nWe choose to not pass `$event` to the method since it is not needed anyway.\nThe fixed template code is:\n\n```html\n\u003cbutton (click)=\"onAdd()\"\u003eAdd more {{ items }}\u003c/button\u003e\n```\n\nTo see the solution in Git tag `fix-3` [you can view at this commit](releases/tag/fix-3)!\n\n\n#### Building with AoT\n\nFinally, compile the application for production with AoT enabled.\n\n```bash\n$ ng build --prod\n```\n\n![ng build --prod](./screenshots/ng-build-prod.png)\n\nThere are noticable differences in the file sizes!\nWith the development build, `vendor.bundle.js` was 1.88MB in size and `main.bundle.js` was 7.51kB.\nIn the production build, `vendor.bundle.js` is **reduced to 1.07MB** and `main.bundle.js` has **grown to 23.6kB**.\n\nThis happens because of several effects:\n\n - First, in production build, the JavaScript files are minified.\n - Second, with AoT compilation, the [Angular compiler is no longer included in the vendor bundle](https://angular.io/guide/aot-compiler#inspect-the-bundle).\nThe package `@angular/compiler` accounts for roughly 999kB in unmified, plain ES5 code!\n   - As [Minko Gechev points out](http://blog.mgechev.com/2016/08/14/ahead-of-time-compilation-angular-offline-precompilation/#what-we-get-from-aot), ommitting the compiler from the vendor bundle saves us quite a few KWh of energy on the planet!\n   - On top, [AoT applications will run much faster](https://blog.nrwl.io/angular-is-aot-worth-it-8fa02eaf64d4) in the web browser!\n\nThese performance improvements are achieved by a trade-off.\nWith AoT compilation, so-called **factory code for components is generated**.\nThe generated code lives in intermediate `*.ngfactory.ts` files.\nThe Angular CLI generates these files under the hood and does not make them visible to the user.\nSince that code needs to be included in the application, the `main.bundle.js` **is increased in file size** (from 7.51kB to 23.6kB, ~3 times even though minificaiton is applied) and the build **takes longer to execute** (from ~26sec to ~34 sec) in the above example.\n\n\n## A look inside the generated factory code\n\nThe compiled version of `BttfComponent`:\n\n![BttfComponent compiled in JiT](./screenshots/bttf-component-jit.png)\n\nThe generated factory code for the component reflects view definitions:\n\n![BttfComponent Factory in AoT](./screenshots/bttf-component-aot.png)\n\nNotice some well-known code patterns from the past:\nthe call to `co.onAdd()` is invoked for the event binding.\nIt checks for `if(('click' == en))`, then checks the return value `_co.onAdd() !== false`, stores it in a local `pd_0` variable, and returns a boolean from evaluating `(pd_0 \u0026\u0026 ad)`.\n\nIn the past, prior to Angular and data binding frameworks, plain-old DOM manipulating JavaScript code looked like:\n\n```js\nelem.addEventListener(function (evt) {\n  if (evt.type === 'click') {\n    /* do things... */\n    return true; // cancel the event\n  }\n});\n```\n\nWith Angular's data binding, this code is written in the template with the following expression:\n\n```html\n\u003cbutton (click)=\"onAdd($event)\"\u003eAdd more chicken\u003c/button\u003e\n```\n\n**Notice**:\nthe auto-generated **factory code reflect the component's template**, albei in a slightly different way.\nThe transformation from HTML markup to JavaScript instructions is performed by Angular AoT compiler.\n\nIt also means that the compiler performs type checking from code expressions in a component's HTML template.\nThe template is transformed into TypeScript factory code.\nThe factory code is compiled and depends on the component class.\nNow it becomes clear why certain errors occured:\nhow could a factory invoke the `private onAdd()` method with the parameter `$event`?\nIt simply cannot and throws a type error.\nThese are exactly the kind of errors we have to fix to make AoT work!\n\n","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsparkles-dev%2Fng-aot-guide","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsparkles-dev%2Fng-aot-guide","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsparkles-dev%2Fng-aot-guide/lists"}