From e5ccb60708f31dc5b8b6a5f190596522fc8ee44f Mon Sep 17 00:00:00 2001 From: Ricardo Chavarria Date: Mon, 7 Jul 2025 23:39:34 -0600 Subject: [PATCH] feat: update origin to Angular v20.0.6 Switch to GitHub Actions for better Bazel support on the free tier. CircleCI's free plan lacks support for the required Bazel resource class and fails when running Bazel inside Docker. GitHub Actions offers more flexibility and compatibility for our current CI needs. --- .circleci/config.yml | 110 +- .github/workflows/adev-staging-deploy.yml | 49 + .github/workflows/ci.yml | 27 + .nvmrc | 2 +- adev-es/src/app/core/constants/links.ts | 3 +- .../layout/footer/footer.component.en.html | 120 ++ .../core/layout/footer/footer.component.html | 135 +- .../navigation/navigation.component.en.html | 501 +++++++ .../navigation/navigation.component.html | 2 +- .../home-animation.component.html | 154 ++ .../src/app/features/home/home.component.html | 162 +- adev-es/src/app/sub-navigation-data.en.ts | 1335 +++++++++++++++++ adev-es/src/app/sub-navigation-data.ts | 2 +- adev-es/src/content/ai/develop-with-ai.md | 36 + adev-es/src/content/ai/overview.md | 147 ++ adev-es/src/content/best-practices/a11y.md | 48 +- .../content/best-practices/error-handling.md | 42 + .../profiling-with-chrome-devtools.md | 100 ++ .../runtime-performance/skipping-subtrees.md | 61 +- .../runtime-performance/zone-pollution.md | 20 +- .../src/content/best-practices/style-guide.md | 1033 +++---------- adev-es/src/content/best-practices/update.md | 6 +- adev-es/src/content/cli/index.md | 136 ++ .../ecosystem/custom-build-pipeline.md | 33 + .../ecosystem/rxjs-interop/output-interop.md | 50 + .../ecosystem/rxjs-interop/signals-interop.md | 104 ++ .../ecosystem/service-workers/app-shell.md | 37 +- .../ecosystem/service-workers/config.md | 37 +- .../ecosystem/service-workers/devops.md | 2 +- .../service-workers/getting-started.md | 21 +- .../service-workers/push-notifications.md | 2 +- adev-es/src/content/examples/i18n/readme.md | 27 + .../src/app/readme.md | 9 + .../guide/animations/complex-sequences.md | 9 +- adev-es/src/content/guide/animations/css.md | 178 +++ .../src/content/guide/animations/migration.md | 264 ++++ .../src/content/guide/animations/overview.md | 2 + .../guide/animations/reusable-animations.md | 2 + .../guide/animations/route-animations.md | 207 +-- .../animations/transition-and-triggers.md | 2 + .../components/advanced-configuration.md | 10 +- .../guide/components/anatomy-of-components.md | 57 +- .../guide/components/content-projection.md | 79 +- .../src/content/guide/components/dom-apis.md | 24 +- .../content/guide/components/host-elements.md | 19 +- .../content/guide/components/inheritance.md | 17 +- .../src/content/guide/components/inputs.md | 295 +++- .../src/content/guide/components/lifecycle.md | 137 +- .../src/content/guide/components/outputs.md | 110 +- .../components/programmatic-rendering.md | 46 +- .../src/content/guide/components/queries.md | 338 +++-- .../src/content/guide/components/selectors.md | 28 +- .../src/content/guide/components/styling.md | 29 +- .../guide/di/creating-injectable-service.md | 36 +- .../guide/di/dependency-injection-context.md | 2 +- .../di/dependency-injection-providers.md | 21 +- .../content/guide/di/dependency-injection.md | 50 +- adev-es/src/content/guide/di/di-in-action.md | 6 +- .../di/hierarchical-dependency-injection.md | 203 +-- adev-es/src/content/guide/di/overview.md | 2 +- .../guide/directives/attribute-directives.md | 12 +- .../directives/directive-composition-api.md | 14 +- .../src/content/guide/directives/overview.md | 253 +--- .../guide/directives/structural-directives.md | 37 +- adev-es/src/content/guide/drag-drop.md | 343 +++++ adev-es/src/content/guide/elements.md | 15 +- .../src/content/guide/forms/dynamic-forms.md | 6 +- .../content/guide/forms/form-validation.md | 15 +- adev-es/src/content/guide/forms/overview.md | 60 +- .../src/content/guide/forms/reactive-forms.md | 68 +- .../guide/forms/template-driven-forms.md | 160 +- .../src/content/guide/forms/typed-forms.md | 20 +- .../src/content/guide/http/http-resource.md | 124 ++ .../src/content/guide/http/interceptors.md | 10 +- .../src/content/guide/http/making-requests.md | 37 +- adev-es/src/content/guide/http/security.md | 4 +- adev-es/src/content/guide/http/setup.md | 9 +- adev-es/src/content/guide/http/testing.md | 78 +- adev-es/src/content/guide/hydration.md | 80 +- adev-es/src/content/guide/i18n/add-package.md | 6 +- adev-es/src/content/guide/i18n/deploy.md | 14 +- .../content/guide/i18n/format-data-locale.md | 2 +- adev-es/src/content/guide/i18n/locale-id.md | 13 +- .../content/guide/i18n/manage-marked-text.md | 6 +- adev-es/src/content/guide/i18n/merge.md | 2 +- adev-es/src/content/guide/i18n/prepare.md | 103 +- .../src/content/guide/image-optimization.md | 143 +- .../content/guide/incremental-hydration.md | 210 +++ .../src/content/guide/ngmodules/overview.md | 209 ++- .../src/content/guide/performance/overview.md | 11 + .../guide/routing/common-router-tasks.md | 599 +------- .../content/guide/routing/define-routes.md | 401 +++++ .../guide/routing/navigate-to-routes.md | 171 +++ adev-es/src/content/guide/routing/overview.md | 69 +- .../content/guide/routing/read-route-state.md | 288 ++++ .../guide/routing/redirecting-routes.md | 144 ++ .../src/content/guide/routing/route-guards.md | 208 +++ .../content/guide/routing/router-reference.md | 245 ++- .../content/guide/routing/router-tutorial.md | 4 +- .../guide/routing/show-routes-with-outlets.md | 170 +++ adev-es/src/content/guide/security.md | 27 +- .../content/guide/signals/linked-signal.md | 131 ++ adev-es/src/content/guide/signals/overview.md | 26 +- adev-es/src/content/guide/signals/resource.md | 117 ++ adev-es/src/content/guide/ssr.md | 394 ++++- .../guide/templates/attribute-binding.md | 53 - .../src/content/guide/templates/binding.md | 244 ++- .../content/guide/templates/class-binding.md | 93 -- .../content/guide/templates/control-flow.md | 155 +- adev-es/src/content/guide/templates/defer.md | 340 +++++ .../content/guide/templates/event-binding.md | 83 - .../guide/templates/event-listeners.md | 120 ++ .../guide/templates/expression-syntax.md | 118 ++ .../content/guide/templates/interpolation.md | 24 - .../content/guide/templates/ng-container.md | 115 ++ .../src/content/guide/templates/ng-content.md | 37 + .../content/guide/templates/ng-template.md | 283 ++++ .../src/content/guide/templates/overview.md | 66 +- adev-es/src/content/guide/templates/pipes.md | 290 ++++ .../property-binding-best-practices.md | 63 - .../guide/templates/property-binding.md | 95 -- .../guide/templates/reference-variables.md | 114 -- .../guide/templates/svg-in-templates.md | 17 - .../guide/templates/template-statements.md | 56 - .../guide/templates/two-way-binding.md | 184 ++- .../src/content/guide/templates/variables.md | 188 +++ .../src/content/guide/templates/whitespace.md | 64 + .../content/guide/testing/code-coverage.md | 2 +- .../testing/component-harnesses-overview.md | 31 + ...omponent-harnesses-testing-environments.md | 62 + .../guide/testing/components-basics.md | 64 +- .../guide/testing/components-scenarios.md | 242 +-- .../testing/creating-component-harnesses.md | 277 ++++ .../guide/testing/experimental-unit-test.md | 65 + adev-es/src/content/guide/testing/pipes.md | 2 +- adev-es/src/content/guide/testing/services.md | 2 +- .../testing/using-component-harnesses.md | 214 +++ .../src/content/guide/testing/utility-apis.md | 4 +- adev-es/src/content/guide/zoneless.md | 88 +- .../introduction/essentials/components.md | 152 +- .../essentials/dependency-injection.md | 60 + .../introduction/essentials/next-steps.md | 6 +- .../introduction/essentials/overview.md | 17 +- .../introduction/essentials/signals.md | 80 + .../introduction/essentials/templates.md | 148 ++ .../src/content/introduction/installation.md | 119 ++ .../introduction/what-is-angular.en.md | 2 +- adev-es/src/content/kitchen-sink.md | 20 +- adev-es/src/content/reference/cli.md | 2 +- .../configs/angular-compiler-options.md | 4 + .../reference/configs/file-structure.md | 4 +- .../reference/configs/workspace-config.md | 34 +- .../src/content/reference/errors/NG0100.md | 2 +- .../src/content/reference/errors/NG01203.md | 2 +- .../src/content/reference/errors/NG0203.md | 4 +- .../src/content/reference/errors/NG02800.md | 6 +- .../src/content/reference/errors/NG0300.md | 30 + .../src/content/reference/errors/NG0302.md | 5 +- .../src/content/reference/errors/NG0403.md | 2 +- .../src/content/reference/errors/NG0500.md | 1 - .../src/content/reference/errors/NG0503.md | 2 - .../src/content/reference/errors/NG0504.md | 3 - .../src/content/reference/errors/NG0506.md | 78 +- .../src/content/reference/errors/NG05104.md | 4 +- .../src/content/reference/errors/NG0602.md | 8 +- .../src/content/reference/errors/NG0750.md | 6 + .../src/content/reference/errors/NG0751.md | 13 + .../src/content/reference/errors/NG0910.md | 8 +- .../src/content/reference/errors/NG0913.md | 33 + .../src/content/reference/errors/NG0956.md | 8 +- .../src/content/reference/errors/NG8002.md | 2 +- .../src/content/reference/errors/overview.md | 3 +- .../reference/extended-diagnostics/NG8101.md | 4 +- .../reference/extended-diagnostics/NG8103.md | 3 - .../reference/extended-diagnostics/NG8106.md | 2 +- .../reference/extended-diagnostics/NG8111.md | 65 + .../reference/extended-diagnostics/NG8113.md | 48 + .../reference/extended-diagnostics/NG8114.md | 52 + .../reference/extended-diagnostics/NG8115.md | 61 + .../reference/extended-diagnostics/NG8116.md | 61 + .../extended-diagnostics/overview.md | 43 +- adev-es/src/content/reference/license.md | 2 +- .../migrations/cleanup-unused-imports.md | 38 + .../reference/migrations/control-flow.md | 5 +- .../reference/migrations/inject-function.md | 137 ++ .../content/reference/migrations/outputs.md | 96 ++ .../content/reference/migrations/overview.md | 33 +- .../migrations/route-lazy-loading.md | 75 + .../reference/migrations/self-closing-tags.md | 26 + .../reference/migrations/signal-inputs.md | 116 ++ .../reference/migrations/signal-queries.md | 115 ++ .../reference/migrations/standalone.md | 23 +- adev-es/src/content/reference/press-kit.md | 2 +- adev-es/src/content/reference/releases.md | 25 +- adev-es/src/content/reference/roadmap.md | 146 +- adev-es/src/content/reference/versions.md | 36 +- adev-es/src/content/tools/cli/aot-compiler.md | 22 +- .../tools/cli/build-system-migration.md | 337 ++++- adev-es/src/content/tools/cli/build.md | 27 +- adev-es/src/content/tools/cli/deployment.md | 4 +- adev-es/src/content/tools/cli/end-to-end.md | 7 +- adev-es/src/content/tools/cli/environments.md | 2 +- adev-es/src/content/tools/cli/overview.md | 6 + .../tools/cli/schematics-for-libraries.md | 4 +- adev-es/src/content/tools/cli/schematics.md | 7 +- adev-es/src/content/tools/cli/serve.md | 2 +- adev-es/src/content/tools/cli/setup-local.md | 60 +- .../content/tools/cli/template-typecheck.md | 19 +- adev-es/src/content/tools/devtools.md | 6 +- adev-es/src/content/tools/language-service.md | 47 + .../tools/libraries/angular-package-format.md | 49 +- .../tools/libraries/creating-libraries.md | 13 +- .../src/content/tools/libraries/overview.md | 2 +- .../tools/libraries/using-libraries.md | 8 +- adev-es/src/content/tutorials/README.md | 108 ++ .../deferrable-views/intro/README.md | 11 + .../1-what-are-deferrable-views/README.md | 38 + .../2-loading-error-placeholder/README.md | 106 ++ .../steps/3-defer-triggers/README.md | 106 ++ .../tutorials/first-app/intro/README.md | 86 ++ .../first-app/steps/01-hello-world/README.md | 120 ++ .../first-app/steps/02-Home/README.md | 128 ++ .../steps/03-HousingLocation/README.md | 79 + .../first-app/steps/04-interfaces/README.md | 99 ++ .../first-app/steps/05-inputs/README.md | 51 + .../steps/06-property-binding/README.md | 49 + .../07-dynamic-template-values/README.md | 60 + .../first-app/steps/08-ngFor/README.md | 66 + .../first-app/steps/09-services/README.md | 110 ++ .../first-app/steps/10-routing/README.md | 87 ++ .../first-app/steps/11-details-page/README.md | 133 ++ .../first-app/steps/12-forms/README.md | 106 ++ .../first-app/steps/13-search/README.md | 85 ++ .../first-app/steps/14-http/README.md | 207 +++ adev-es/src/content/tutorials/home.md | 15 + .../tutorials/learn-angular/intro/README.md | 17 + .../steps/1-components-in-angular/README.md | 47 + .../steps/10-deferrable-views/README.md | 114 ++ .../steps/11-optimizing-images/README.md | 105 ++ .../steps/12-enable-routing/README.md | 77 + .../steps/13-define-a-route/README.md | 67 + .../steps/14-routerLink/README.md | 54 + .../learn-angular/steps/15-forms/README.md | 70 + .../steps/16-form-control-values/README.md | 66 + .../steps/17-reactive-forms/README.md | 124 ++ .../steps/18-forms-validation/README.md | 56 + .../README.md | 45 + .../2-updating-the-component-class/README.md | 51 + .../steps/20-inject-based-di/README.md | 54 + .../learn-angular/steps/22-pipes/README.md | 64 + .../steps/23-pipes-format-data/README.md | 74 + .../steps/24-create-a-pipe/README.md | 81 + .../steps/25-next-steps/README.md | 18 + .../steps/3-composing-components/README.md | 40 + .../steps/4-control-flow-if/README.md | 65 + .../steps/5-control-flow-for/README.md | 60 + .../steps/6-property-binding/README.md | 48 + .../steps/7-event-handling/README.md | 53 + .../learn-angular/steps/8-input/README.md | 57 + .../learn-angular/steps/9-output/README.md | 72 + origin | 2 +- package.json | 1 - tools/build.mjs | 7 +- .../replace-build-for-everyone.patch | 13 - tools/lib/common.mjs | 4 +- tools/update-origin.mjs | 9 +- 266 files changed, 16922 insertions(+), 4886 deletions(-) create mode 100644 .github/workflows/adev-staging-deploy.yml create mode 100644 .github/workflows/ci.yml create mode 100644 adev-es/src/app/core/layout/footer/footer.component.en.html create mode 100644 adev-es/src/app/core/layout/navigation/navigation.component.en.html create mode 100644 adev-es/src/app/features/home/components/home-animation/home-animation.component.html create mode 100644 adev-es/src/app/sub-navigation-data.en.ts create mode 100644 adev-es/src/content/ai/develop-with-ai.md create mode 100644 adev-es/src/content/ai/overview.md create mode 100644 adev-es/src/content/best-practices/error-handling.md create mode 100644 adev-es/src/content/best-practices/runtime-performance/profiling-with-chrome-devtools.md create mode 100644 adev-es/src/content/cli/index.md create mode 100644 adev-es/src/content/ecosystem/custom-build-pipeline.md create mode 100644 adev-es/src/content/ecosystem/rxjs-interop/output-interop.md create mode 100644 adev-es/src/content/ecosystem/rxjs-interop/signals-interop.md create mode 100644 adev-es/src/content/examples/i18n/readme.md create mode 100644 adev-es/src/content/examples/service-worker-getting-started/src/app/readme.md create mode 100644 adev-es/src/content/guide/animations/css.md create mode 100644 adev-es/src/content/guide/animations/migration.md create mode 100644 adev-es/src/content/guide/drag-drop.md create mode 100644 adev-es/src/content/guide/http/http-resource.md create mode 100644 adev-es/src/content/guide/incremental-hydration.md create mode 100644 adev-es/src/content/guide/performance/overview.md create mode 100644 adev-es/src/content/guide/routing/define-routes.md create mode 100644 adev-es/src/content/guide/routing/navigate-to-routes.md create mode 100644 adev-es/src/content/guide/routing/read-route-state.md create mode 100644 adev-es/src/content/guide/routing/redirecting-routes.md create mode 100644 adev-es/src/content/guide/routing/route-guards.md create mode 100644 adev-es/src/content/guide/routing/show-routes-with-outlets.md create mode 100644 adev-es/src/content/guide/signals/linked-signal.md create mode 100644 adev-es/src/content/guide/signals/resource.md delete mode 100644 adev-es/src/content/guide/templates/attribute-binding.md delete mode 100644 adev-es/src/content/guide/templates/class-binding.md create mode 100644 adev-es/src/content/guide/templates/defer.md delete mode 100644 adev-es/src/content/guide/templates/event-binding.md create mode 100644 adev-es/src/content/guide/templates/event-listeners.md create mode 100644 adev-es/src/content/guide/templates/expression-syntax.md delete mode 100644 adev-es/src/content/guide/templates/interpolation.md create mode 100644 adev-es/src/content/guide/templates/ng-container.md create mode 100644 adev-es/src/content/guide/templates/ng-content.md create mode 100644 adev-es/src/content/guide/templates/ng-template.md create mode 100644 adev-es/src/content/guide/templates/pipes.md delete mode 100644 adev-es/src/content/guide/templates/property-binding-best-practices.md delete mode 100644 adev-es/src/content/guide/templates/property-binding.md delete mode 100644 adev-es/src/content/guide/templates/reference-variables.md delete mode 100644 adev-es/src/content/guide/templates/svg-in-templates.md delete mode 100644 adev-es/src/content/guide/templates/template-statements.md create mode 100644 adev-es/src/content/guide/templates/variables.md create mode 100644 adev-es/src/content/guide/templates/whitespace.md create mode 100644 adev-es/src/content/guide/testing/component-harnesses-overview.md create mode 100644 adev-es/src/content/guide/testing/component-harnesses-testing-environments.md create mode 100644 adev-es/src/content/guide/testing/creating-component-harnesses.md create mode 100644 adev-es/src/content/guide/testing/experimental-unit-test.md create mode 100644 adev-es/src/content/guide/testing/using-component-harnesses.md create mode 100644 adev-es/src/content/introduction/essentials/dependency-injection.md create mode 100644 adev-es/src/content/introduction/essentials/signals.md create mode 100644 adev-es/src/content/introduction/essentials/templates.md create mode 100644 adev-es/src/content/introduction/installation.md create mode 100644 adev-es/src/content/reference/errors/NG0750.md create mode 100644 adev-es/src/content/reference/errors/NG0751.md create mode 100644 adev-es/src/content/reference/errors/NG0913.md create mode 100644 adev-es/src/content/reference/extended-diagnostics/NG8111.md create mode 100644 adev-es/src/content/reference/extended-diagnostics/NG8113.md create mode 100644 adev-es/src/content/reference/extended-diagnostics/NG8114.md create mode 100644 adev-es/src/content/reference/extended-diagnostics/NG8115.md create mode 100644 adev-es/src/content/reference/extended-diagnostics/NG8116.md create mode 100644 adev-es/src/content/reference/migrations/cleanup-unused-imports.md create mode 100644 adev-es/src/content/reference/migrations/inject-function.md create mode 100644 adev-es/src/content/reference/migrations/outputs.md create mode 100644 adev-es/src/content/reference/migrations/route-lazy-loading.md create mode 100644 adev-es/src/content/reference/migrations/self-closing-tags.md create mode 100644 adev-es/src/content/reference/migrations/signal-inputs.md create mode 100644 adev-es/src/content/reference/migrations/signal-queries.md create mode 100644 adev-es/src/content/tutorials/README.md create mode 100644 adev-es/src/content/tutorials/deferrable-views/intro/README.md create mode 100644 adev-es/src/content/tutorials/deferrable-views/steps/1-what-are-deferrable-views/README.md create mode 100644 adev-es/src/content/tutorials/deferrable-views/steps/2-loading-error-placeholder/README.md create mode 100644 adev-es/src/content/tutorials/deferrable-views/steps/3-defer-triggers/README.md create mode 100644 adev-es/src/content/tutorials/first-app/intro/README.md create mode 100644 adev-es/src/content/tutorials/first-app/steps/01-hello-world/README.md create mode 100644 adev-es/src/content/tutorials/first-app/steps/02-Home/README.md create mode 100644 adev-es/src/content/tutorials/first-app/steps/03-HousingLocation/README.md create mode 100644 adev-es/src/content/tutorials/first-app/steps/04-interfaces/README.md create mode 100644 adev-es/src/content/tutorials/first-app/steps/05-inputs/README.md create mode 100644 adev-es/src/content/tutorials/first-app/steps/06-property-binding/README.md create mode 100644 adev-es/src/content/tutorials/first-app/steps/07-dynamic-template-values/README.md create mode 100644 adev-es/src/content/tutorials/first-app/steps/08-ngFor/README.md create mode 100644 adev-es/src/content/tutorials/first-app/steps/09-services/README.md create mode 100644 adev-es/src/content/tutorials/first-app/steps/10-routing/README.md create mode 100644 adev-es/src/content/tutorials/first-app/steps/11-details-page/README.md create mode 100644 adev-es/src/content/tutorials/first-app/steps/12-forms/README.md create mode 100644 adev-es/src/content/tutorials/first-app/steps/13-search/README.md create mode 100644 adev-es/src/content/tutorials/first-app/steps/14-http/README.md create mode 100644 adev-es/src/content/tutorials/home.md create mode 100644 adev-es/src/content/tutorials/learn-angular/intro/README.md create mode 100644 adev-es/src/content/tutorials/learn-angular/steps/1-components-in-angular/README.md create mode 100644 adev-es/src/content/tutorials/learn-angular/steps/10-deferrable-views/README.md create mode 100644 adev-es/src/content/tutorials/learn-angular/steps/11-optimizing-images/README.md create mode 100644 adev-es/src/content/tutorials/learn-angular/steps/12-enable-routing/README.md create mode 100644 adev-es/src/content/tutorials/learn-angular/steps/13-define-a-route/README.md create mode 100644 adev-es/src/content/tutorials/learn-angular/steps/14-routerLink/README.md create mode 100644 adev-es/src/content/tutorials/learn-angular/steps/15-forms/README.md create mode 100644 adev-es/src/content/tutorials/learn-angular/steps/16-form-control-values/README.md create mode 100644 adev-es/src/content/tutorials/learn-angular/steps/17-reactive-forms/README.md create mode 100644 adev-es/src/content/tutorials/learn-angular/steps/18-forms-validation/README.md create mode 100644 adev-es/src/content/tutorials/learn-angular/steps/19-creating-an-injectable-service/README.md create mode 100644 adev-es/src/content/tutorials/learn-angular/steps/2-updating-the-component-class/README.md create mode 100644 adev-es/src/content/tutorials/learn-angular/steps/20-inject-based-di/README.md create mode 100644 adev-es/src/content/tutorials/learn-angular/steps/22-pipes/README.md create mode 100644 adev-es/src/content/tutorials/learn-angular/steps/23-pipes-format-data/README.md create mode 100644 adev-es/src/content/tutorials/learn-angular/steps/24-create-a-pipe/README.md create mode 100644 adev-es/src/content/tutorials/learn-angular/steps/25-next-steps/README.md create mode 100644 adev-es/src/content/tutorials/learn-angular/steps/3-composing-components/README.md create mode 100644 adev-es/src/content/tutorials/learn-angular/steps/4-control-flow-if/README.md create mode 100644 adev-es/src/content/tutorials/learn-angular/steps/5-control-flow-for/README.md create mode 100644 adev-es/src/content/tutorials/learn-angular/steps/6-property-binding/README.md create mode 100644 adev-es/src/content/tutorials/learn-angular/steps/7-event-handling/README.md create mode 100644 adev-es/src/content/tutorials/learn-angular/steps/8-input/README.md create mode 100644 adev-es/src/content/tutorials/learn-angular/steps/9-output/README.md delete mode 100644 tools/git-patches/replace-build-for-everyone.patch diff --git a/.circleci/config.yml b/.circleci/config.yml index 200221a..74ef38e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,111 +1,7 @@ -# Use the latest 2.1 version of CircleCI pipeline process engine. -# See: https://circleci.com/docs/configuration-reference version: 2.1 - -# Settings common to each job -job_defaults: &job_defaults - working_directory: ~/angular-docs-es - docker: - - image: cimg/node:lts-browsers - -orbs: - node: circleci/node@5.2.0 - build-tools: circleci/build-tools@3.0.0 - browser-tools: circleci/browser-tools@1.4.8 - -commands: - # Command for checking out the source code from GitHub. This also ensures that the source code - # can be merged to the main branch without conflicts. - checkout_and_rebase: - description: Checkout and verify clean merge with main - steps: - - checkout - - run: - name: Set git user.name and user.email for rebase. - # User is required for rebase. - command: | - git config user.name "Admin" - git config user.email "admin@angular.lat" - - build-tools/merge-with-parent: - parent: main - setup: - description: 'Set up executor' - steps: - - attach_workspace: - at: ~/ - setup_firebase_auth: - description: 'Set up Firebase authentication' - steps: - - run: - name: Create a google_service_account.json - command: | - echo $GSA_KEY > $GOOGLE_APPLICATION_CREDENTIALS - -# ---------------------------------- -# Job definitions. -# ---------------------------------- - jobs: - # ---------------------------------- - # initialize job - # ---------------------------------- - initialize: - <<: *job_defaults - steps: - - checkout_and_rebase - - node/install-packages - - persist_to_workspace: - root: ~/ - paths: - - angular-docs-es - # ----------------------------------- - # Build job. - # ----------------------------------- build: - <<: *job_defaults - steps: - - setup - - checkout - - run: - name: Build project in CI mode (pre build) - command: npm run build:ci - - run: - name: Installing packages - working_directory: ./build - command: yarn install - - run: - name: Build adev-es docs - working_directory: ./build - command: yarn bazel build //adev:build --fast_adev --local_ram_resources="HOST_RAM*.90" --jobs=1 - - persist_to_workspace: - root: ~/ - paths: - - angular-docs-es/build/dist/bin/adev/build/browser - - # ----------------------------------- - # Firebase deploy to staging job. - # ----------------------------------- - firebase-deploy-staging: - <<: *job_defaults + docker: + - image: circleci/node:12-browsers steps: - - setup - - setup_firebase_auth - - run: - name: 'Deploy Main Branch to Firebase Hosting' - command: | - npm run deploy:staging - -workflows: - build-workflow: - jobs: - - initialize - - build: - requires: - - initialize - - firebase-deploy-staging: - filters: - branches: - only: - - main - requires: - - build \ No newline at end of file + - run: echo 'No checks.' diff --git a/.github/workflows/adev-staging-deploy.yml b/.github/workflows/adev-staging-deploy.yml new file mode 100644 index 0000000..8f00694 --- /dev/null +++ b/.github/workflows/adev-staging-deploy.yml @@ -0,0 +1,49 @@ +name: Build adev and deploy to staging + +on: + push: + branches: + - main + +env: + BAZEL_REPO_CACHE_PATH: '~/.cache/bazel_repo_cache' + +jobs: + adev-build: + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + submodules: true + - name: Setup Node JS + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + - name: Setup Bazel + uses: bazel-contrib/setup-bazel@4fd964a13a440a8aeb0be47350db2fc640f19ca8 # 0.15.0 + with: + bazelisk-cache: true + disk-cache: true + repository-cache: true + bazelrc: | + # Print all the options that apply to the build. + # This helps us diagnose which options override others + # (e.g. /etc/bazel.bazelrc vs. tools/bazel.rc) + build --announce_rc + + # More details on failures + build --verbose_failures=true + + # CI supports colors but Bazel does not detect it. + common --color=yes + - name: Install Dependencies + run: npm ci + - name: Build Docs + run: npm run build + - name: Deploy to Firebase Hosting + uses: FirebaseExtended/action-hosting-deploy@v0 + with: + repoToken: "${{ secrets.ANGULAR_DOCS_ES_TOKEN }}" + firebaseServiceAccount: "${{ secrets.FIREBASE_SERVICE_ACCOUNT }}" + projectId: angular-hispano-staging + channelId: live + target: staging \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..4150f81 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,27 @@ +name: CI (Pull Request) + +on: [pull_request] + +permissions: + contents: read + +jobs: + build-adev: + runs-on: ubuntu-latest + steps: + - name: Checkout Repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + submodules: true + - name: Setup Node JS + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + - name: Setup Bazel + uses: bazel-contrib/setup-bazel@4fd964a13a440a8aeb0be47350db2fc640f19ca8 # 0.15.0 + with: + bazelisk-cache: true + disk-cache: true + repository-cache: true + - name: Install Dependencies + run: npm ci + - name: Build Docs + run: npm run build \ No newline at end of file diff --git a/.nvmrc b/.nvmrc index 87ec884..ba33190 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -18.18.2 +20.19.2 diff --git a/adev-es/src/app/core/constants/links.ts b/adev-es/src/app/core/constants/links.ts index 0dd19ee..6c731e5 100644 --- a/adev-es/src/app/core/constants/links.ts +++ b/adev-es/src/app/core/constants/links.ts @@ -2,4 +2,5 @@ export const GITHUB = 'https://github.com/angular-hispano/angular-docs-es'; export const X = 'https://x.com/AngularHispana'; export const DISCORD = 'https://discord.com/invite/4cWb6SKUcb'; export const MEDIUM = ''; -export const YOUTUBE = ''; \ No newline at end of file +export const YOUTUBE = ''; +export const BLUESKY = ''; \ No newline at end of file diff --git a/adev-es/src/app/core/layout/footer/footer.component.en.html b/adev-es/src/app/core/layout/footer/footer.component.en.html new file mode 100644 index 0000000..b72fd77 --- /dev/null +++ b/adev-es/src/app/core/layout/footer/footer.component.en.html @@ -0,0 +1,120 @@ + diff --git a/adev-es/src/app/core/layout/footer/footer.component.html b/adev-es/src/app/core/layout/footer/footer.component.html index 1fa7444..376b4c2 100644 --- a/adev-es/src/app/core/layout/footer/footer.component.html +++ b/adev-es/src/app/core/layout/footer/footer.component.html @@ -1,71 +1,70 @@ } - + \ No newline at end of file diff --git a/adev-es/src/app/features/home/components/home-animation/home-animation.component.html b/adev-es/src/app/features/home/components/home-animation/home-animation.component.html new file mode 100644 index 0000000..18366f7 --- /dev/null +++ b/adev-es/src/app/features/home/components/home-animation/home-animation.component.html @@ -0,0 +1,154 @@ +
+ + + + @if (!isUwu()) { + +
+ + + + + + + + + + + + + + + +
+ } @else { + +
+ Angular logo +
+ } + + +
+

Trabaja a cualquier escala

+

+ Angular te permite empezar poco a poco en un camino bien iluminado y + te apoya a medida que tu equipo y tus aplicaciones crecen. +

+
+ + + @if (meteorFieldData(); as meteorFieldData) { +
+
+ @for (type of meteors(); track $index) { +
+ } +
+
+ } + + +
+

Amaado por millones

+

+ Únete a los millones de desarrolladores de todo el mundo que + construyen con Angular en una comunidad próspera y amigable. +

+
+ + +
+

Construye para todos

+

+ Confía en la hidración, internacionalización, seguridad y accesibilidad + incorporadas de Angular para construir para todos en todo el mundo. +

+
+
+ \ No newline at end of file diff --git a/adev-es/src/app/features/home/home.component.html b/adev-es/src/app/features/home/home.component.html index a71f8c8..77e4458 100644 --- a/adev-es/src/app/features/home/home.component.html +++ b/adev-es/src/app/features/home/home.component.html @@ -1,153 +1,17 @@ -
-
-
- -
-
-
- -
-
-
-
-

Trabaja a cualquier escala

-

- Angular te permite empezar poco a poco en un camino bien iluminado y - te apoya a medida que tu equipo y tus aplicaciones crecen. -

-
-
-
-
-
-
-
-
-

Amado por millones

-

- Únete a los millones de desarrolladores de todo el mundo que - construyen con Angular en una comunidad próspera y amigable. -

-
-
-
-
-
-
-
-
-

- Construir para todos - -

-

- Confía en el soporte integrado de hidratación, internacionalización, - seguridad y accesibilidad de Angular para construir para todos en - todo el mundo. -

-
-
-
- -
-
-
-
- @defer (on viewport(buildForEveryone); prefetch on - viewport(lovedByMillions)) { + + + +@if (!animationReady()) { +
+} + +
+
+ @defer (when showEditor(); prefetch when prefetchEditor()) { - } @loading { + } @loading { Code editor - } -
+ }
-
- - -
diff --git a/adev-es/src/app/sub-navigation-data.en.ts b/adev-es/src/app/sub-navigation-data.en.ts new file mode 100644 index 0000000..fe20e9e --- /dev/null +++ b/adev-es/src/app/sub-navigation-data.en.ts @@ -0,0 +1,1335 @@ +/*! + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import {NavigationItem} from '@angular/docs'; + +// These 2 imports are expected to be red because they are generated a build time +import FIRST_APP_TUTORIAL_NAV_DATA from '../../src/assets/tutorials/first-app/routes.json'; +import LEARN_ANGULAR_TUTORIAL_NAV_DATA from '../../src/assets/tutorials/learn-angular/routes.json'; +import DEFERRABLE_VIEWS_TUTORIAL_NAV_DATA from '../../src/assets/tutorials/deferrable-views/routes.json'; +import ERRORS_NAV_DATA from '../../src/assets/content/reference/errors/routes.json'; +import EXT_DIAGNOSTICS_NAV_DATA from '../../src/assets/content/reference/extended-diagnostics/routes.json'; + +import {DefaultPage} from './core/enums/pages'; +import {getApiNavigationItems} from './features/references/helpers/manifest.helper'; + +interface SubNavigationData { + docs: NavigationItem[]; + reference: NavigationItem[]; + tutorials: NavigationItem[]; + footer: NavigationItem[]; +} + +const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ + { + label: 'Introduction', + children: [ + { + label: 'What is Angular?', + path: 'overview', + contentPath: 'introduction/what-is-angular', + }, + { + label: 'Installation', + path: 'installation', + contentPath: 'introduction/installation', + }, + { + label: 'Essentials', + children: [ + { + label: 'Overview', + path: 'essentials', + contentPath: 'introduction/essentials/overview', + }, + { + label: 'Composition with components', + path: 'essentials/components', + contentPath: 'introduction/essentials/components', + }, + { + label: 'Reactivity with signals', + path: 'essentials/signals', + contentPath: 'introduction/essentials/signals', + }, + { + label: 'Dynamic interfaces with templates', + path: 'essentials/templates', + contentPath: 'introduction/essentials/templates', + }, + { + label: 'Modular design with dependency injection', + path: 'essentials/dependency-injection', + contentPath: 'introduction/essentials/dependency-injection', + }, + { + label: 'Next Steps', + path: 'essentials/next-steps', + contentPath: 'introduction/essentials/next-steps', + }, + ], + }, + { + label: 'Start coding! 🚀', + path: 'tutorials/learn-angular', + }, + ], + }, + { + label: 'In-depth Guides', + children: [ + { + label: 'Signals', + children: [ + { + label: 'Overview', + path: 'guide/signals', + contentPath: 'guide/signals/overview', + }, + { + label: 'Dependent state with linkedSignal', + path: 'guide/signals/linked-signal', + contentPath: 'guide/signals/linked-signal', + }, + { + label: 'Async reactivity with resources', + path: 'guide/signals/resource', + contentPath: 'guide/signals/resource', + }, + ], + }, + { + label: 'Components', + children: [ + { + label: 'Anatomy of components', + path: 'guide/components', + contentPath: 'guide/components/anatomy-of-components', + }, + { + label: 'Selectors', + path: 'guide/components/selectors', + contentPath: 'guide/components/selectors', + }, + { + label: 'Styling', + path: 'guide/components/styling', + contentPath: 'guide/components/styling', + }, + { + label: 'Accepting data with input properties', + path: 'guide/components/inputs', + contentPath: 'guide/components/inputs', + }, + { + label: 'Custom events with outputs', + path: 'guide/components/outputs', + contentPath: 'guide/components/outputs', + }, + { + label: 'Content projection with ng-content', + path: 'guide/components/content-projection', + contentPath: 'guide/components/content-projection', + }, + { + label: 'Host elements', + path: 'guide/components/host-elements', + contentPath: 'guide/components/host-elements', + }, + { + label: 'Lifecycle', + path: 'guide/components/lifecycle', + contentPath: 'guide/components/lifecycle', + }, + { + label: 'Referencing component children with queries', + path: 'guide/components/queries', + contentPath: 'guide/components/queries', + }, + { + label: 'Using DOM APIs', + path: 'guide/components/dom-apis', + contentPath: 'guide/components/dom-apis', + }, + { + label: 'Inheritance', + path: 'guide/components/inheritance', + contentPath: 'guide/components/inheritance', + }, + { + label: 'Programmatically rendering components', + path: 'guide/components/programmatic-rendering', + contentPath: 'guide/components/programmatic-rendering', + }, + { + label: 'Advanced configuration', + path: 'guide/components/advanced-configuration', + contentPath: 'guide/components/advanced-configuration', + }, + { + label: 'Custom Elements', + path: 'guide/elements', + contentPath: 'guide/elements', + }, + ], + }, + { + label: 'Templates', + children: [ + { + label: 'Overview', + path: 'guide/templates', + contentPath: 'guide/templates/overview', + }, + { + label: 'Binding dynamic text, properties and attributes', + path: 'guide/templates/binding', + contentPath: 'guide/templates/binding', + }, + { + label: 'Adding event listeners', + path: 'guide/templates/event-listeners', + contentPath: 'guide/templates/event-listeners', + }, + { + label: 'Two-way binding', + path: 'guide/templates/two-way-binding', + contentPath: 'guide/templates/two-way-binding', + }, + { + label: 'Control flow', + path: 'guide/templates/control-flow', + contentPath: 'guide/templates/control-flow', + }, + { + label: 'Pipes', + path: 'guide/templates/pipes', + contentPath: 'guide/templates/pipes', + }, + { + label: 'Slotting child content with ng-content', + path: 'guide/templates/ng-content', + contentPath: 'guide/templates/ng-content', + }, + { + label: 'Create template fragments with ng-template', + path: 'guide/templates/ng-template', + contentPath: 'guide/templates/ng-template', + }, + { + label: 'Grouping elements with ng-container', + path: 'guide/templates/ng-container', + contentPath: 'guide/templates/ng-container', + }, + { + label: 'Variables in templates', + path: 'guide/templates/variables', + contentPath: 'guide/templates/variables', + }, + { + label: 'Deferred loading with @defer', + path: 'guide/templates/defer', + contentPath: 'guide/templates/defer', + }, + { + label: 'Expression syntax', + path: 'guide/templates/expression-syntax', + contentPath: 'guide/templates/expression-syntax', + }, + { + label: 'Whitespace in templates', + path: 'guide/templates/whitespace', + contentPath: 'guide/templates/whitespace', + }, + ], + }, + { + label: 'Directives', + children: [ + { + label: 'Overview', + path: 'guide/directives', + contentPath: 'guide/directives/overview', + }, + { + label: 'Attribute directives', + path: 'guide/directives/attribute-directives', + contentPath: 'guide/directives/attribute-directives', + }, + { + label: 'Structural directives', + path: 'guide/directives/structural-directives', + contentPath: 'guide/directives/structural-directives', + }, + { + label: 'Directive composition API', + path: 'guide/directives/directive-composition-api', + contentPath: 'guide/directives/directive-composition-api', + }, + { + label: 'Optimizing images with NgOptimizedImage', + path: 'guide/image-optimization', + contentPath: 'guide/image-optimization', + }, + ], + }, + { + label: 'Dependency Injection', + children: [ + { + label: 'Overview', + path: 'guide/di', + contentPath: 'guide/di/overview', + }, + { + label: 'Understanding dependency injection', + path: 'guide/di/dependency-injection', + contentPath: 'guide/di/dependency-injection', + }, + { + label: 'Creating an injectable service', + path: 'guide/di/creating-injectable-service', + contentPath: 'guide/di/creating-injectable-service', + }, + { + label: 'Defining dependency providers', + path: 'guide/di/dependency-injection-providers', + contentPath: 'guide/di/dependency-injection-providers', + }, + { + label: 'Injection context', + path: 'guide/di/dependency-injection-context', + contentPath: 'guide/di/dependency-injection-context', + }, + { + label: 'Hierarchical injectors', + path: 'guide/di/hierarchical-dependency-injection', + contentPath: 'guide/di/hierarchical-dependency-injection', + }, + { + label: 'Optimizing injection tokens', + path: 'guide/di/lightweight-injection-tokens', + contentPath: 'guide/di/lightweight-injection-tokens', + }, + { + label: 'DI in action', + path: 'guide/di/di-in-action', + contentPath: 'guide/di/di-in-action', + }, + ], + }, + { + label: 'Routing', + children: [ + { + label: 'Overview', + path: 'guide/routing', + contentPath: 'guide/routing/overview', + }, + { + label: 'Define routes', + path: 'guide/routing/define-routes', + contentPath: 'guide/routing/define-routes', + }, + { + label: 'Show routes with Outlets', + path: 'guide/routing/show-routes-with-outlets', + contentPath: 'guide/routing/show-routes-with-outlets', + }, + { + label: 'Navigate to routes', + path: 'guide/routing/navigate-to-routes', + contentPath: 'guide/routing/navigate-to-routes', + }, + { + label: 'Read route state', + path: 'guide/routing/read-route-state', + contentPath: 'guide/routing/read-route-state', + }, + { + label: 'Redirecting routes', + path: 'guide/routing/redirecting-routes', + contentPath: 'guide/routing/redirecting-routes', + }, + { + label: 'Control route access with guards', + path: 'guide/routing/route-guards', + contentPath: 'guide/routing/route-guards', + }, + { + label: 'Other routing tasks', + path: 'guide/routing/common-router-tasks', + contentPath: 'guide/routing/common-router-tasks', + }, + { + label: 'Creating custom route matches', + path: 'guide/routing/routing-with-urlmatcher', + contentPath: 'guide/routing/routing-with-urlmatcher', + }, + { + label: 'Router reference', + path: 'guide/routing/router-reference', + contentPath: 'guide/routing/router-reference', + }, + ], + }, + { + label: 'Forms', + children: [ + { + label: 'Overview', + path: 'guide/forms', + contentPath: 'guide/forms/overview', + }, + { + label: 'Reactive forms', + path: 'guide/forms/reactive-forms', + contentPath: 'guide/forms/reactive-forms', + }, + { + label: 'Strictly typed reactive forms', + path: 'guide/forms/typed-forms', + contentPath: 'guide/forms/typed-forms', + }, + { + label: 'Template-driven forms', + path: 'guide/forms/template-driven-forms', + contentPath: 'guide/forms/template-driven-forms', + }, + { + label: 'Validate form input', + path: 'guide/forms/form-validation', + contentPath: 'guide/forms/form-validation', + }, + { + label: 'Building dynamic forms', + path: 'guide/forms/dynamic-forms', + contentPath: 'guide/forms/dynamic-forms', + }, + ], + }, + { + label: 'HTTP Client', + children: [ + { + label: 'Overview', + path: 'guide/http', + contentPath: 'guide/http/overview', + }, + { + label: 'Setting up HttpClient', + path: 'guide/http/setup', + contentPath: 'guide/http/setup', + }, + { + label: 'Making requests', + path: 'guide/http/making-requests', + contentPath: 'guide/http/making-requests', + }, + { + label: 'Reactive data fetching with httpResource', + path: 'guide/http/http-resource', + contentPath: 'guide/http/http-resource', + }, + { + label: 'Intercepting requests and responses', + path: 'guide/http/interceptors', + contentPath: 'guide/http/interceptors', + }, + { + label: 'Testing', + path: 'guide/http/testing', + contentPath: 'guide/http/testing', + }, + ], + }, + { + label: 'Server-side & hybrid-rendering', + children: [ + { + label: 'Overview', + path: 'guide/performance', + contentPath: 'guide/performance/overview', + }, + { + label: 'Server-side and hybrid-rendering', + path: 'guide/ssr', + contentPath: 'guide/ssr', + }, + { + label: 'Hydration', + path: 'guide/hydration', + contentPath: 'guide/hydration', + }, + { + label: 'Incremental Hydration', + path: 'guide/incremental-hydration', + contentPath: 'guide/incremental-hydration', + }, + ], + }, + { + label: 'Testing', + children: [ + { + label: 'Overview', + path: 'guide/testing', + contentPath: 'guide/testing/overview', + }, + { + label: 'Code coverage', + path: 'guide/testing/code-coverage', + contentPath: 'guide/testing/code-coverage', + }, + { + label: 'Testing services', + path: 'guide/testing/services', + contentPath: 'guide/testing/services', + }, + { + label: 'Basics of testing components', + path: 'guide/testing/components-basics', + contentPath: 'guide/testing/components-basics', + }, + { + label: 'Component testing scenarios', + path: 'guide/testing/components-scenarios', + contentPath: 'guide/testing/components-scenarios', + }, + { + label: 'Testing attribute directives', + path: 'guide/testing/attribute-directives', + contentPath: 'guide/testing/attribute-directives', + }, + { + label: 'Testing pipes', + path: 'guide/testing/pipes', + contentPath: 'guide/testing/pipes', + }, + { + label: 'Debugging tests', + path: 'guide/testing/debugging', + contentPath: 'guide/testing/debugging', + }, + { + label: 'Testing utility APIs', + path: 'guide/testing/utility-apis', + contentPath: 'guide/testing/utility-apis', + }, + { + label: 'Experimental unit testing integration', + path: 'guide/testing/unit-tests', + contentPath: 'guide/testing/experimental-unit-test', + }, + { + label: 'Component harnesses overview', + path: 'guide/testing/component-harnesses-overview', + contentPath: 'guide/testing/component-harnesses-overview', + }, + { + label: 'Using component harnesses in tests', + path: 'guide/testing/using-component-harnesses', + contentPath: 'guide/testing/using-component-harnesses', + }, + { + label: 'Creating harnesses for your components', + path: 'guide/testing/creating-component-harnesses', + contentPath: 'guide/testing/creating-component-harnesses', + }, + { + label: 'Adding harness support for additional testing environments', + path: 'guide/testing/component-harnesses-testing-environments', + contentPath: 'guide/testing/component-harnesses-testing-environments', + }, + ], + }, + { + label: 'Internationalization', + children: [ + { + label: 'Overview', + path: 'guide/i18n', + contentPath: 'guide/i18n/overview', + }, + { + label: 'Add the localize package', + path: 'guide/i18n/add-package', + contentPath: 'guide/i18n/add-package', + }, + { + label: 'Refer to locales by ID', + path: 'guide/i18n/locale-id', + contentPath: 'guide/i18n/locale-id', + }, + { + label: 'Format data based on locale', + path: 'guide/i18n/format-data-locale', + contentPath: 'guide/i18n/format-data-locale', + }, + { + label: 'Prepare component for translation', + path: 'guide/i18n/prepare', + contentPath: 'guide/i18n/prepare', + }, + { + label: 'Work with translation files', + path: 'guide/i18n/translation-files', + contentPath: 'guide/i18n/translation-files', + }, + { + label: 'Merge translations into the app', + path: 'guide/i18n/merge', + contentPath: 'guide/i18n/merge', + }, + { + label: 'Deploy multiple locales', + path: 'guide/i18n/deploy', + contentPath: 'guide/i18n/deploy', + }, + { + label: 'Import global variants of the locale data', + path: 'guide/i18n/import-global-variants', + contentPath: 'guide/i18n/import-global-variants', + }, + { + label: 'Manage marked text with custom IDs', + path: 'guide/i18n/manage-marked-text', + contentPath: 'guide/i18n/manage-marked-text', + }, + { + label: 'Example Angular application', + path: 'guide/i18n/example', + contentPath: 'guide/i18n/example', + }, + ], + }, + { + label: 'Animations', + children: [ + { + label: 'Animating your content', + path: 'guide/animations/css', + contentPath: 'guide/animations/css', + }, + { + label: 'Route transition animations', + path: 'guide/animations/route-animations', + contentPath: 'guide/animations/route-animations', + }, + ], + }, + { + label: 'Drag and drop', + path: 'guide/drag-drop', + contentPath: 'guide/drag-drop', + }, + ], + }, + { + label: 'Build with AI', + children: [ + { + label: 'Get Started', + path: 'ai', + contentPath: 'ai/overview', + }, + { + label: 'LLM prompts and AI IDE setup', + path: 'ai/develop-with-ai', + contentPath: 'ai/develop-with-ai', + }, + ], + }, + { + label: 'Developer Tools', + children: [ + { + label: 'Angular CLI', + children: [ + { + label: 'Overview', + path: 'tools/cli', + contentPath: 'tools/cli/overview', + }, + { + label: 'Local set-up', + path: 'tools/cli/setup-local', + contentPath: 'tools/cli/setup-local', + }, + { + label: 'Building Angular apps', + path: 'tools/cli/build', + contentPath: 'tools/cli/build', + }, + { + label: 'Serving Angular apps for development', + path: 'tools/cli/serve', + contentPath: 'tools/cli/serve', + }, + { + label: 'Deployment', + path: 'tools/cli/deployment', + contentPath: 'tools/cli/deployment', + }, + { + label: 'End-to-End Testing', + path: 'tools/cli/end-to-end', + contentPath: 'tools/cli/end-to-end', + }, + { + label: 'Migrating to new build system', + path: 'tools/cli/build-system-migration', + contentPath: 'tools/cli/build-system-migration', + }, + { + label: 'Build environments', + path: 'tools/cli/environments', + contentPath: 'tools/cli/environments', + }, + { + label: 'Angular CLI builders', + path: 'tools/cli/cli-builder', + contentPath: 'tools/cli/cli-builder', + }, + { + label: 'Generating code using schematics', + path: 'tools/cli/schematics', + contentPath: 'tools/cli/schematics', + }, + { + label: 'Authoring schematics', + path: 'tools/cli/schematics-authoring', + contentPath: 'tools/cli/schematics-authoring', + }, + { + label: 'Schematics for libraries', + path: 'tools/cli/schematics-for-libraries', + contentPath: 'tools/cli/schematics-for-libraries', + }, + { + label: 'Template type checking', + path: 'tools/cli/template-typecheck', + contentPath: 'tools/cli/template-typecheck', + }, + { + label: 'Ahead-of-time (AOT) compilation', + path: 'tools/cli/aot-compiler', + contentPath: 'tools/cli/aot-compiler', + }, + { + label: 'AOT metadata errors', + path: 'tools/cli/aot-metadata-errors', + contentPath: 'tools/cli/aot-metadata-errors', + }, + ], + }, + { + label: 'Libraries', + children: [ + { + label: 'Overview', + path: 'tools/libraries', + contentPath: 'tools/libraries/overview', + }, + { + label: 'Creating Libraries', + path: 'tools/libraries/creating-libraries', + contentPath: 'tools/libraries/creating-libraries', + }, + { + label: 'Using Libraries', + path: 'tools/libraries/using-libraries', + contentPath: 'tools/libraries/using-libraries', + }, + { + label: 'Angular Package Format', + path: 'tools/libraries/angular-package-format', + contentPath: 'tools/libraries/angular-package-format', + }, + ], + }, + { + label: 'DevTools', + path: 'tools/devtools', + contentPath: 'tools/devtools', + }, + { + label: 'Language Service', + path: 'tools/language-service', + contentPath: 'tools/language-service', + }, + ], + }, + { + label: 'Best Practices', + children: [ + { + label: 'Style Guide', + path: 'style-guide', + contentPath: 'best-practices/style-guide', + }, + { + label: 'Security', + path: 'best-practices/security', + contentPath: 'guide/security', // Have not refactored due to build issues + }, + { + label: 'Accessibility', + path: 'best-practices/a11y', + contentPath: 'best-practices/a11y', + }, + { + label: 'Unhandled errors in Angular', + path: 'best-practices/error-handling', + contentPath: 'best-practices/error-handling', + }, + { + label: 'Performance', + children: [ + { + label: 'Overview', + path: 'best-practices/runtime-performance', + contentPath: 'best-practices/runtime-performance/overview', + }, + { + label: 'Zone pollution', + path: 'best-practices/zone-pollution', + contentPath: 'best-practices/runtime-performance/zone-pollution', + }, + { + label: 'Slow computations', + path: 'best-practices/slow-computations', + contentPath: 'best-practices/runtime-performance/slow-computations', + }, + { + label: 'Skipping component subtrees', + path: 'best-practices/skipping-subtrees', + contentPath: 'best-practices/runtime-performance/skipping-subtrees', + }, + { + label: 'Profiling with the Chrome DevTools', + path: 'best-practices/profiling-with-chrome-devtools', + contentPath: 'best-practices/runtime-performance/profiling-with-chrome-devtools', + }, + {label: 'Zoneless', path: 'guide/zoneless', contentPath: 'guide/zoneless'}, + ], + }, + { + label: 'Keeping up-to-date', + path: 'update', + contentPath: 'best-practices/update', + }, + ], + }, + { + label: 'Extended Ecosystem', + children: [ + { + label: 'NgModules', + path: 'guide/ngmodules/overview', + contentPath: 'guide/ngmodules/overview', + }, + { + label: 'Animations', + children: [ + { + label: 'Overview', + path: 'guide/animations', + contentPath: 'guide/animations/overview', + }, + { + label: 'Transition and Triggers', + path: 'guide/animations/transition-and-triggers', + contentPath: 'guide/animations/transition-and-triggers', + }, + { + label: 'Complex Sequences', + path: 'guide/animations/complex-sequences', + contentPath: 'guide/animations/complex-sequences', + }, + { + label: 'Reusable Animations', + path: 'guide/animations/reusable-animations', + contentPath: 'guide/animations/reusable-animations', + }, + { + label: 'Migrating to Native CSS Animations', + path: 'guide/animations/migration', + contentPath: 'guide/animations/migration', + }, + ], + }, + { + label: 'Using RxJS with Angular', + children: [ + { + label: 'Signals interop', + path: 'ecosystem/rxjs-interop', + contentPath: 'ecosystem/rxjs-interop/signals-interop', + }, + { + label: 'Component output interop', + path: 'ecosystem/rxjs-interop/output-interop', + contentPath: 'ecosystem/rxjs-interop/output-interop', + }, + ], + }, + { + label: 'Service Workers & PWAs', + children: [ + { + label: 'Overview', + path: 'ecosystem/service-workers', + contentPath: 'ecosystem/service-workers/overview', + }, + { + label: 'Getting started', + path: 'ecosystem/service-workers/getting-started', + contentPath: 'ecosystem/service-workers/getting-started', + }, + { + label: 'Configuration file', + path: 'ecosystem/service-workers/config', + contentPath: 'ecosystem/service-workers/config', + }, + { + label: 'Communicating with the service worker', + path: 'ecosystem/service-workers/communications', + contentPath: 'ecosystem/service-workers/communications', + }, + { + label: 'Push notifications', + path: 'ecosystem/service-workers/push-notifications', + contentPath: 'ecosystem/service-workers/push-notifications', + }, + { + label: 'Service worker devops', + path: 'ecosystem/service-workers/devops', + contentPath: 'ecosystem/service-workers/devops', + }, + { + label: 'App shell pattern', + path: 'ecosystem/service-workers/app-shell', + contentPath: 'ecosystem/service-workers/app-shell', + }, + ], + }, + { + label: 'Web workers', + path: 'ecosystem/web-workers', + contentPath: 'ecosystem/web-workers', + }, + { + label: 'Custom build pipeline', + path: 'ecosystem/custom-build-pipeline', + contentPath: 'ecosystem/custom-build-pipeline', + }, + { + label: 'Angular Fire', + path: 'https://github.com/angular/angularfire#readme', + }, + { + label: 'Google Maps', + path: 'https://github.com/angular/components/tree/main/src/google-maps#readme', + }, + { + label: 'Google Pay', + path: 'https://github.com/google-pay/google-pay-button#angular', + }, + { + label: 'YouTube player', + path: 'https://github.com/angular/components/blob/main/src/youtube-player/README.md', + }, + { + label: 'Angular CDK', + path: 'https://material.angular.dev/cdk/categories', + }, + { + label: 'Angular Material', + path: 'https://material.angular.dev/', + }, + ], + }, +]; + +export const TUTORIALS_SUB_NAVIGATION_DATA: NavigationItem[] = [ + FIRST_APP_TUTORIAL_NAV_DATA, + LEARN_ANGULAR_TUTORIAL_NAV_DATA, + DEFERRABLE_VIEWS_TUTORIAL_NAV_DATA, + { + path: DefaultPage.TUTORIALS, + contentPath: 'tutorials/home', + label: 'Tutorials', + }, +]; + +const REFERENCE_SUB_NAVIGATION_DATA: NavigationItem[] = [ + { + label: 'Roadmap', + path: 'roadmap', + contentPath: 'reference/roadmap', + }, + { + label: 'Get involved', + path: 'https://github.com/angular/angular/blob/main/CONTRIBUTING.md', + }, + { + label: 'API Reference', + children: [ + { + label: 'Overview', + path: 'api', + }, + ...getApiNavigationItems(), + ], + }, + { + label: 'CLI Reference', + children: [ + { + label: 'Overview', + path: 'cli', + contentPath: 'reference/cli', + }, + { + label: 'ng add', + path: 'cli/add', + }, + { + label: 'ng analytics', + children: [ + { + label: 'Overview', + path: 'cli/analytics', + }, + { + label: 'disable', + path: 'cli/analytics/disable', + }, + { + label: 'enable', + path: 'cli/analytics/enable', + }, + { + label: 'info', + path: 'cli/analytics/info', + }, + { + label: 'prompt', + path: 'cli/analytics/prompt', + }, + ], + }, + { + label: 'ng build', + path: 'cli/build', + }, + { + label: 'ng cache', + children: [ + { + label: 'Overview', + path: 'cli/cache', + }, + { + label: 'clean', + path: 'cli/cache/clean', + }, + { + label: 'disable', + path: 'cli/cache/disable', + }, + { + label: 'enable', + path: 'cli/cache/enable', + }, + { + label: 'info', + path: 'cli/cache/info', + }, + ], + }, + { + label: 'ng completion', + children: [ + { + label: 'Overview', + path: 'cli/completion', + }, + { + label: 'script', + path: 'cli/completion/script', + }, + ], + }, + { + label: 'ng config', + path: 'cli/config', + }, + { + label: 'ng deploy', + path: 'cli/deploy', + }, + { + label: 'ng e2e', + path: 'cli/e2e', + }, + { + label: 'ng extract-i18n', + path: 'cli/extract-i18n', + }, + { + label: 'ng generate', + children: [ + { + label: 'Overview', + path: 'cli/generate', + }, + { + label: 'app-shell', + path: 'cli/generate/app-shell', + }, + { + label: 'application', + path: 'cli/generate/application', + }, + { + label: 'class', + path: 'cli/generate/class', + }, + { + label: 'component', + path: 'cli/generate/component', + }, + { + label: 'config', + path: 'cli/generate/config', + }, + { + label: 'directive', + path: 'cli/generate/directive', + }, + { + label: 'enum', + path: 'cli/generate/enum', + }, + { + label: 'environments', + path: 'cli/generate/environments', + }, + { + label: 'guard', + path: 'cli/generate/guard', + }, + { + label: 'interceptor', + path: 'cli/generate/interceptor', + }, + { + label: 'interface', + path: 'cli/generate/interface', + }, + { + label: 'library', + path: 'cli/generate/library', + }, + { + label: 'module', + path: 'cli/generate/module', + }, + { + label: 'pipe', + path: 'cli/generate/pipe', + }, + { + label: 'resolver', + path: 'cli/generate/resolver', + }, + { + label: 'service-worker', + path: 'cli/generate/service-worker', + }, + { + label: 'service', + path: 'cli/generate/service', + }, + { + label: 'web-worker', + path: 'cli/generate/web-worker', + }, + ], + }, + { + label: 'ng lint', + path: 'cli/lint', + }, + { + label: 'ng new', + path: 'cli/new', + }, + { + label: 'ng run', + path: 'cli/run', + }, + { + label: 'ng serve', + path: 'cli/serve', + }, + { + label: 'ng test', + path: 'cli/test', + }, + { + label: 'ng update', + path: 'cli/update', + }, + { + label: 'ng version', + path: 'cli/version', + }, + ], + }, + { + label: 'Error Encyclopedia', + children: [ + { + label: 'Overview', + path: 'errors', + contentPath: 'reference/errors/overview', + }, + ...ERRORS_NAV_DATA, + ], + }, + { + label: 'Extended Diagnostics', + children: [ + { + label: 'Overview', + path: 'extended-diagnostics', + contentPath: 'reference/extended-diagnostics/overview', + }, + ...EXT_DIAGNOSTICS_NAV_DATA, + ], + }, + { + label: 'Versioning and releases', + path: 'reference/releases', + contentPath: 'reference/releases', + }, + { + label: 'Version compatibility', + path: 'reference/versions', + contentPath: 'reference/versions', + }, + { + label: 'Update guide', + path: 'update-guide', + }, + { + label: 'Configurations', + children: [ + { + label: 'File structure', + path: 'reference/configs/file-structure', + contentPath: 'reference/configs/file-structure', + }, + { + label: 'Workspace configuration', + path: 'reference/configs/workspace-config', + contentPath: 'reference/configs/workspace-config', + }, + { + label: 'Angular compiler options', + path: 'reference/configs/angular-compiler-options', + contentPath: 'reference/configs/angular-compiler-options', + }, + { + label: 'npm dependencies', + path: 'reference/configs/npm-packages', + contentPath: 'reference/configs/npm-packages', + }, + ], + }, + { + label: 'Migrations', + children: [ + { + label: 'Overview', + path: 'reference/migrations', + contentPath: 'reference/migrations/overview', + }, + { + label: 'Standalone', + path: 'reference/migrations/standalone', + contentPath: 'reference/migrations/standalone', + }, + { + label: 'Control Flow Syntax', + path: 'reference/migrations/control-flow', + contentPath: 'reference/migrations/control-flow', + }, + { + label: 'inject() Function', + path: 'reference/migrations/inject-function', + contentPath: 'reference/migrations/inject-function', + }, + { + label: 'Lazy-loaded routes', + path: 'reference/migrations/route-lazy-loading', + contentPath: 'reference/migrations/route-lazy-loading', + }, + { + label: 'Signal inputs', + path: 'reference/migrations/signal-inputs', + contentPath: 'reference/migrations/signal-inputs', + }, + { + label: 'Outputs', + path: 'reference/migrations/outputs', + contentPath: 'reference/migrations/outputs', + }, + { + label: 'Signal queries', + path: 'reference/migrations/signal-queries', + contentPath: 'reference/migrations/signal-queries', + }, + { + label: 'Clean up unused imports', + path: 'reference/migrations/cleanup-unused-imports', + contentPath: 'reference/migrations/cleanup-unused-imports', + }, + { + label: 'Self-closing tags', + path: 'reference/migrations/self-closing-tags', + contentPath: 'reference/migrations/self-closing-tags', + }, + ], + }, +]; + +const FOOTER_NAVIGATION_DATA: NavigationItem[] = [ + { + label: 'Press Kit', + path: 'press-kit', + contentPath: 'reference/press-kit', + }, + { + label: 'License', + path: 'license', + contentPath: 'reference/license', + }, +]; + +// Docs navigation data structure, it's used to display structure in +// navigation-list component And build the routing table for content pages. +export const SUB_NAVIGATION_DATA: SubNavigationData = { + docs: DOCS_SUB_NAVIGATION_DATA, + reference: REFERENCE_SUB_NAVIGATION_DATA, + tutorials: TUTORIALS_SUB_NAVIGATION_DATA, + footer: FOOTER_NAVIGATION_DATA, +}; diff --git a/adev-es/src/app/sub-navigation-data.ts b/adev-es/src/app/sub-navigation-data.ts index eff0d00..80fbc22 100644 --- a/adev-es/src/app/sub-navigation-data.ts +++ b/adev-es/src/app/sub-navigation-data.ts @@ -1547,4 +1547,4 @@ export const SUB_NAVIGATION_DATA: SubNavigationData = { reference: REFERENCE_SUB_NAVIGATION_DATA, tutorials: TUTORIALS_SUB_NAVIGATION_DATA, footer: FOOTER_NAVIGATION_DATA, -}; +}; \ No newline at end of file diff --git a/adev-es/src/content/ai/develop-with-ai.md b/adev-es/src/content/ai/develop-with-ai.md new file mode 100644 index 0000000..8c75f8e --- /dev/null +++ b/adev-es/src/content/ai/develop-with-ai.md @@ -0,0 +1,36 @@ +# LLM prompts and AI IDE setup +Generating code with large language models (LLMs) is a rapidly growing area of interest for developers. While LLMs are often capable of generating working code it can be a challenge to generate code for consistently evolving frameworks like Angular. + +Advanced instructions and prompting are an emerging standard for supporting modern code generation with domain specific details. This section contains curated content and resources to support more accurate code generation for Angular and LLMs. + +## Custom Prompts and System Instructions +Improve your experience generating code with LLMs by using one of the following custom, domain specific files. + +NOTE: These files will be updated on a regular basis staying up to date with Angular's conventions. + +Here is a set of instructions to help LLMs generate correct code that follows Angular best practices. This file can be included as system instructions to your AI tooling or included along with your prompt as context. + + + +Click here to download the best-practices.md file. + +## Rules Files +Several editors, such as Firebase Studio have rules files useful for providing critical context to LLMs. + +| Environment/IDE | Rules File | Installation Instructions | +|:----------------|:----------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------| +| Firebase Studio | airules.md | Configure `airules.md` | +| Copilot powered IDEs | copilot-instructions.md | Configure `.github/copilot-instructions.md` | +| Cursor | cursor.md | Configure `cursorrules.md` | +| JetBrains IDEs | guidelines.md | Configure `guidelines.md` | +| VS Code | .instructions.md | Configure `.instructions.md` | +| Windsurf | guidelines.md | Configure `guidelines.md` | + +## Providing Context with `llms.txt` +`llms.txt` is a proposed standard for websites designed to help LLMs better understand and process their content. The Angular team has developed two versions of this file to help LLMs and tools that use LLMs for code generation to create better modern Angular code. + + +* llms.txt - an index file providing links to key files and resources. +* llms-full.txt - a more robust compiled set of resources describing how Angular works and how to build Angular applications. + +Be sure [to check out the overview page](/ai) for more information on how to integrate AI into your Angular applications. diff --git a/adev-es/src/content/ai/overview.md b/adev-es/src/content/ai/overview.md new file mode 100644 index 0000000..d816855 --- /dev/null +++ b/adev-es/src/content/ai/overview.md @@ -0,0 +1,147 @@ + + +Build AI-powered apps. Develop faster with AI. + + +HELPFUL: Looking to get started with building in your favorite AI powered IDE?
Check out our [prompt rules and best practices](/ai/develop-with-ai). + +Generative AI (GenAI) with large language models (LLMs) enables the creation of sophisticated and engaging application experiences, including personalized content, intelligent recommendations, media generation and comprehension, information summarization, and dynamic functionality. + +Developing features like these would have previously required deep domain expertise and significant engineering effort. However, new products and SDKs are lowering the barrier to entry. Angular is well-suited for integrating AI into your web application as a result of: + +* Angular's robust templating APIs enable the creation of dynamic, cleanly composed UIs made from generated content +* Strong, signal-based architecture designed to dynamically manage data and state +* Angular integrates seamlessly with AI SDKs and APIs + +This guide demonstrates how you can use [Genkit](/ai#build-ai-powered-applications-with-genkit-and-angular), [Firebase AI Logic](/ai#build-ai-powered-applications-with-firebase-ai-logic-and-angular), and the [Gemini API](/ai#build-ai-powered-applications-with-gemini-api-and-angular) to infuse your Angular apps with AI today. This guide will jumpstart your AI-powered web app development journey by explaining how to begin integrating AI into Angular apps. This guide also shares resources, such as starter kits, example code, and recipes for common workflows, you can use to get up to speed quickly. + +To get started, you should have a basic understanding of Angular. New to Angular? Try our [essentials guide](/essentials) or our [getting started tutorials](/tutorials). + +NOTE: While this page features integrations and examples with Google AI products, tools like Genkit are model agnostic and allow you to choose your own model. In many cases, the examples and code samples are applicable to other third-party solutions. + +## Getting Started +Building AI-powered applications is a new and rapidly developing field. It can be challenging to decide where to start and which technologies to choose. The following section provides three options to choose from: + +1. *Genkit* gives you the choice of [supported model and interface with a unified API](https://genkit.dev) for building full-stack applications. Ideal for applications requiring sophisticated back-end AI logic, such as personalized recommendations. + +1. *Firebase AI Logic* provides a secure client-side API for Google's models to build client-side only applications or mobile apps. Best for interactive AI features directly in the browser, such as real-time text analysis or basic chatbots. + +1. *Gemini API* enables you to build an application that uses the methods and functionality exposed through the API surface directly, best for full-stack applications. Suitable for applications needing direct control over AI models, like custom image generation or deep data processing. + +### Build AI-powered applications with Genkit and Angular +[Genkit](https://genkit.dev) is an open-source toolkit designed to help you build AI-powered features in web and mobile apps. It offers a unified interface for integrating AI models from Google, OpenAI, Anthropic, Ollama, and more, so you can explore and choose the best models for your needs. As a server-side solution, your web apps need a supported server environment, such as a node-based server in order to integrate with Genkit. Building a full-stack app using Angular SSR gives you the starting server-side code, for example. + +Here are examples of how to build with Genkit and Angular: + +* [Agentic Apps with Genkit and Angular starter-kit](https://github.com/angular/examples/tree/main/genkit-angular-starter-kit)— New to building with AI? Start here with a basic app that features an agentic workflow. Perfect place to start for your first AI building experience. + +* [Use Genkit in an Angular app](https://genkit.dev/docs/angular/)— Build a basic application that uses Genkit Flows, Angular and Gemini 2.0 Flash. This step-by-step walkthrough guides you through creating a full-stack Angular application with AI features. + +* [Dynamic Story Generator app](https://github.com/angular/examples/tree/main/genkit-angular-story-generator)— Learn to build an agentic Angular app powered by Genkit, Gemini and Imagen 3 to dynamically generate a story based on user interaction featuring beautiful image panels to accompany the events that take place. Start here if you'd like to experiment with a more advanced use-case. + + This example also has an in-depth video walkthrough of the functionality: + * [Watch "Building Agentic Apps with Angular and Genkit live!"](https://youtube.com/live/mx7yZoIa2n4?feature=share) + * [Watch "Building Agentic Apps with Angular and Genkit live! PT 2"](https://youtube.com/live/YR6LN5_o3B0?feature=share) + +* [Building Agentic apps with Firebase and Google Cloud (Barista Example)](https://developers.google.com/solutions/learn/agentic-barista) - Learn how to build an agentic coffee ordering app with Firebase and Google Cloud. This example uses both Firebase AI Logic and Genkit. + +### Build AI-powered applications with Firebase AI Logic and Angular +[Firebase AI Logic](https://firebase.google.com/products/vertex-ai-in-firebase) provides a secure way to interact with Vertex AI Gemini API or Imagen API directly from your web and mobile apps. This is compelling for Angular developers since apps can be either full-stack or client-side only. If you are developing a client-side only application, Firebase AI Logic is a good fit for incorporating AI into your web apps. + +Here is an example of how to build with Firebase AI Logic and Angular: +* [Firebase AI Logic x Angular Starter Kit](https://github.com/angular/examples/tree/main/vertex-ai-firebase-angular-example) - Use this starter-kit to build an e-commerce application with a chat agent that can perform tasks. Start here if you do not have experience building with Firebase AI Logic and Angular. + + This example includes an [in-depth video walkthrough explaining the functionality and demonstrates how to add new features](https://youtube.com/live/4vfDz2al_BI). + +### Build AI-powered applications with Gemini API and Angular +The [Gemini API](https://ai.google.dev/gemini-api/docs) provides access to state-of-the-art models from Google that supports audio, images, video, and text input. The models that are optimized for specific use cases, [learn more on the Gemini API documentation site](https://ai.google.dev/gemini-api/docs/models). + +* [AI Text Editor Angular app template](https://github.com/FirebaseExtended/firebase-framework-tools/tree/main/starters/angular/ai-text-editor) - Use this template to start with a fully functioning text editor with AI-powered features like refining text, expanding text and formalizing text. This is a good starting point to gain experience with calling the Gemini API via HTTP. + +* [AI Chatbot app template](https://github.com/FirebaseExtended/firebase-framework-tools/tree/main/starters/angular/ai-chatbot) - This template starts with a chatbot user interface that communicates with the Gemini API via HTTP. + +## AI patterns in action: Streaming chat responses +Having text appear as the response is received from the model is a common UI pattern for web apps using AI. You can achieve this asynchronous task with Angular's `resource` API. The `stream` property of `resource` accepts an asynchronous function you can use to apply updates to a signal value over time. The signal being updated represents the data being streamed. + +```ts +characters = resource({ + stream: async () => { + const data = signal<{ value: string } | { error: unknown }>({ + value: "", + }); + + fetch(this.url).then(async (response) => { + if (!response.body) return; + + for await (const chunk of response.body) { + const chunkText = this.decoder.decode(chunk); + data.update((prev) => { + if ("value" in prev) { + return { value: `${prev.value} ${chunkText}` }; + } else { + return { error: chunkText }; + } + }); + } + }); + + return data; + }, + }); + +``` + +The `characters` member is updated asynchronously and can be displayed in the template. + +```html +

{{ characters.value() }}

+``` + +On the server side, in `server.ts` for example, the defined endpoint sends the data to be streamed to the client. The following code uses the Gemini API but this technique is applicable to other tools and frameworks that support streaming responses from LLMs: + +```ts + app.get("/api/stream-response", async (req, res) => { + ai.models.generateContentStream({ + model: "gemini-2.0-flash", + contents: "Explain how AI works", + }).then(async (response) => { + for await (const chunk of response) { + res.write(chunk.text); + } + }); + }); + +``` +This example connects to the Gemini API but other APIs that support streaming responses can be used here as well. [You can find the complete example on the Angular Github](https://github.com/angular/examples/tree/main/streaming-example). + +## Best Practices +### Connecting to model providers and keeping your API Credentials Secure +When connecting to model providers, it is important to keep your API secrets safe. *Never put your API key in a file that ships to the client, such as `environments.ts`*. + +Your application's architecture determines which AI APIs and tools to choose. Specifically, choose based on whether or not your application is client-side or server-side. Tools such as Firebase AI Logic provide a secure connection to the model APIs for client-side code. If you want to use a different API than Firerbase AI Logic or prefer to use a different model provider, consider creating a proxy-server or even [Cloud Functions for Firebase](https://firebase.google.com/docs/functions) to serve as a proxy and not expose your API keys. + +For an example of connecting using a client-side app, see the code: [Firebase AI Logic Angular example repository](https://github.com/angular/examples/tree/main/vertex-ai-firebase-angular-example). + +For server-side connections to model APIs that require API keys, prefer using a secrets manager or environment variable, not `environments.ts`. You should follow standard best practices for securing API keys and credentials. Firebase now provides a new secrets manager with the latest updates from Firebase App Hosting. To learn more, [check out the official documentation](https://firebase.google.com/docs/app-hosting/configure). + +For a server-side connection example in a full-stack application, see the code: [Angular AI Example (Genkit and Angular Story Generator) repository](https://github.com/angular/examples/tree/main/genkit-angular-story-generator). + +### Use Tool Calling to enhance apps +If you want to build agentic workflows, where agents are able to act and use tools to solve problems based on prompts use "tool calling". Tool calling, also known as function calling, is a way to provide LLMs the ability to make requests back to the application that called it. As a developer, you define which tools are available and you are in control of how or when the tools are called. + +Tool calling further enhances your web apps by expanding your AI integration further than a question and answer style chat bot. In fact, you can empower your model to request function calls using the function calling API of your model provider. The available tools can be used to perform more complex actions within the context of your application. + +In the [e-commerce example](https://github.com/angular/examples/blob/main/vertex-ai-firebase-angular-example/src/app/ai.service.ts#L88) of the [Angular examples repository](https://github.com/angular/examples), the LLM requests to make calls to functions for inventory in order to gain the necessary context to perform more complex tasks such as calculating how much a group of items in the store will cost. The scope of the available API is up to you as a developer just as is whether or not to call a function requested by the LLM. You remain in control of the flow of execution. You can expose specific functions of a service for example but not all functions of that service. + +### Handling non-deterministic responses +Because models can return non-deterministic results, your applications should be designed with that in mind. Here are a few strategies that you can use in your application implementation: +* Adjust prompts and model parameters (such as [temperature](https://ai.google.dev/gemini-api/docs/prompting-strategies)) for more or less deterministic responses. You can [find out more in the prompting strategies section](https://ai.google.dev/gemini-api/docs/prompting-strategies) of [ai.google.dev](https://ai.google.dev/). +* Use the "human in the loop" strategy where a human verifies outputs before proceeding in a workflow. Build your application workflows to allow operators (humans or other models) to verify outputs and confirm key decisions. +* Employ tool (or function) calling and schema constraints to guide and restrict model responses to predefined formats, increasing response predictability. + +Even considering these strategies and techniques, sensible fallbacks should be incorporated in your application design. Follow existing standards of application resiliency. For example, it is not acceptable for an application to crash if a resource or API is not available. In that scenario, an error message is displayed to the user and, if applicable, options for next steps are also displayed. Building AI-powered applications requires the same consideration. Confirm that the response is aligned with the expected output and provide a "safe landing" in case it is not aligned by way of [graceful degradation](https://developer.mozilla.org/en-US/docs/Glossary/Graceful_degradation). This also applies to API outages for LLM providers. + +Consider this example: The LLM provider is not responding. A potential strategy to handle the outage is: +* Save the response from the user to used in a retry scenario (now or at a later time) +* Alert the user to the outage with an appropriate message that doesn't reveal sensitive information +* Resume the conversation at a later time once the services are available again. diff --git a/adev-es/src/content/best-practices/a11y.md b/adev-es/src/content/best-practices/a11y.md index ac68f5d..7df1b3b 100644 --- a/adev-es/src/content/best-practices/a11y.md +++ b/adev-es/src/content/best-practices/a11y.md @@ -21,7 +21,7 @@ When binding to ARIA attributes in Angular, you must use the `attr.` prefix. The
-Note: This syntax is only necessary for attribute *bindings*. +NOTE: This syntax is only necessary for attribute *bindings*. Static ARIA attributes require no extra syntax. @@ -36,8 +36,8 @@ See the [Binding syntax guide](guide/templates) for more background on the diffe ## Angular UI components -The [Angular Material](https://material.angular.io) library, which is maintained by the Angular team, is a suite of reusable UI components that aims to be fully accessible. -The [Component Development Kit (CDK)](https://material.angular.io/cdk/categories) includes the `a11y` package that provides tools to support various areas of accessibility. +The [Angular Material](https://material.angular.dev) library, which is maintained by the Angular team, is a suite of reusable UI components that aims to be fully accessible. +The [Component Development Kit (CDK)](https://material.angular.dev/cdk/categories) includes the `a11y` package that provides tools to support various areas of accessibility. For example: * `LiveAnnouncer` is used to announce messages for screen-reader users using an `aria-live` region. @@ -46,7 +46,7 @@ For example: * The `cdkTrapFocus` directive traps Tab-key focus within an element. Use it to create accessible experience for components such as modal dialogs, where focus must be constrained. -For full details of these and other tools, see the [Angular CDK accessibility overview](https://material.angular.io/cdk/a11y/overview). +For full details of these and other tools, see the [Angular CDK accessibility overview](https://material.angular.dev/cdk/a11y/overview). ### Augmenting native elements @@ -66,7 +66,7 @@ For example, the native `` element cannot have children, so any custom te By just including `` in your custom component's template, it's impossible for your component's users to set arbitrary properties and attributes to the `` element. Instead, create a container component that uses content projection to include the native control in the component's API. -You can see [`MatFormField`](https://material.angular.io/components/form-field/overview) as an example of this pattern. +You can see [`MatFormField`](https://material.angular.dev/components/form-field/overview) as an example of this pattern. ## Case study: Building a custom progress bar @@ -121,30 +121,30 @@ You should avoid situations where focus returns to the `body` element after a ro CSS classes applied to active `RouterLink` elements, such as `RouterLinkActive`, provide a visual cue to identify the active link. Unfortunately, a visual cue doesn't help blind or visually impaired users. Applying the `aria-current` attribute to the element can help identify the active link. -For more information, see [Mozilla Developer Network \(MDN\) aria-current](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-current)). +For more information, see [Mozilla Developer Network \(MDN\) aria-current](https://developer.mozilla.org/docs/Web/Accessibility/ARIA/Attributes/aria-current)). The `RouterLinkActive` directive provides the `ariaCurrentWhenActive` input which sets the `aria-current` to a specified value when the link becomes active. The following example shows how to apply the `active-page` class to active links as well as setting their `aria-current` attribute to `"page"` when they are active: -```html - +```angular-html + ``` diff --git a/adev-es/src/content/best-practices/error-handling.md b/adev-es/src/content/best-practices/error-handling.md new file mode 100644 index 0000000..36dbb05 --- /dev/null +++ b/adev-es/src/content/best-practices/error-handling.md @@ -0,0 +1,42 @@ +# Unhandled errors in Angular + +As your Angular application runs, some of your code may throw an error. If left unhandled, these errors can lead to unexpected behavior and a nonresponsive UI. This guide covers how Angular deals with errors that are not explicitly caught by your application code. For guidance on writing your own error handling logic within your application, consult best practices for error handling in JavaScript and Angular. + +A fundamental principle in Angular's error handling strategy is that errors should be surfaced to developers at the callsite whenever possible. This approach ensures that the code which initiated an operation has the context necessary to understand the error, handle it appropriately, and decide what the appropriate application state should be. By making errors visible at their origin, developers can implement error handling that is specific to the failed operation and has access to relevant information for recovery or providing informative feedback to the end-user. This also helps to avoid the "Overly general error" smell, where errors are reported without sufficient context to understand their cause. + +For example, consider a component that fetches user data from an API. The code responsible for making the API call should include error handling (e.g., using a `try...catch` block or the `catchError` operator in RxJS) to manage potential network issues or errors returned by the API. This allows the component to display a user-friendly error message or retry the request, rather than letting the error propagate unhandled. + +## Unhandled errors are reported to the `ErrorHandler` + +Angular reports unhandled errors to the application's root [ErrorHandler](api/core/ErrorHandler). When providing a custom `ErrorHandler`, provide it in your `ApplicationConfig` as part of calling `bootstrapApplication`. + +When building an Angular application, often you write code that is called automatically _by_ the framework. For example, Angular is responsible for calling a component's constructor and lifecycle methods when that component appears in a template. When the framework runs your code, there's nowhere you could reasonably add a `try` block to gracefully handle errors. In situations like this, Angular catches errors and sends them to the `ErrorHandler`. + +Angular does _not_ catch errors inside of APIs that are called directly by your code. For example, if you have a service with a method that throws an error and you call that method in your component, Angular will not automatically catch that error. You are responsible for handling it using mechanisms like `try...catch`. + +Angular catches _asynchronous_ errors from user promises or observables only when: + +* There is an explicit contract for Angular to wait for and use the result of the asynchronous operation, and +* When errors are not presented in the return value or state. + +For example, `AsyncPipe` and `PendingTasks.run` forward errors to the `ErrorHandler`, whereas `resource` presents the error in the `status` and `error` properties. + +Errors that Angular reports to the `ErrorHandler` are _unexpected_ errors. These errors may be unrecoverable or an indication that the state of the application is corrupted. Applications should provide error handling using `try` blocks or appropriate error handling operators (like `catchError` in RxJS) where the error occurs whenever possible rather than relying on the `ErrorHandler`, which is most frequently and appropriately used only as a mechanism to report potentially fatal errors to the error tracking and logging infrastructure. + +### `TestBed` rethrows errors by default + +In many cases, `ErrorHandler` may only log errors and otherwise allow the application to continue running. In tests, however, you almost always want to surface these errors. Angular's `TestBed` rethrows unexpected errors to ensure that errors caught by the framework cannot be unintentionally missed or ignored. In rare circumstances, a test may specifically attempt to ensure errors do not cause the application to be unresponsive or crash. In these situations, you can [configure `TestBed` to _not_ rethrow application errors](api/core/testing/TestModuleMetadata#rethrowApplicationErrors) with `TestBed.configureTestingModule({rethrowApplicationErrors: false})`. + +## Global error listeners + +Errors that are caught neither by the application code nor by the framework's application instance may reach the global scope. Errors reaching the global scope can have unintended consequences if not accounted for. In non-browser environments, they may cause the process to crash. In the browser, these errors may go unreported and site visitors may see the errors in the browser console. Angular provides global listeners for both environments to account for these issues. + +### Client-side rendering + +Adding [`provideBrowserGlobalErrorListeners()`](/api/core/provideBrowserGlobalErrorListeners) to the [ApplicationConfig](guide/di/dependency-injection#at-the-application-root-level-using-applicationconfig) adds the `'error'` and `'unhandledrejection'` listeners to the browser window and forwards those errors to `ErrorHandler`. The Angular CLI generates new applications with this provider by default. The Angular team recommends handling these global errors for most applications, either with the framework's built-in listeners or with your own custom listeners. If you provide custom listeners, you can remove `provideBrowserGlobalErrorListeners`. + +### Server-side and hybrid rendering + +When using [Angular with SSR](guide/ssr), Angular automatically adds the `'unhandledRejection'` and `'uncaughtException'` listeners to the server process. These handlers prevent the server from crashing and instead log captured errors to the console. + +IMPORTANT: If the application is using Zone.js, only the `'unhandledRejection'` handler is added. When Zone.js is present, errors inside the Application's Zone are already forwarded to the application `ErrorHandler` and do not reach the server process. diff --git a/adev-es/src/content/best-practices/runtime-performance/profiling-with-chrome-devtools.md b/adev-es/src/content/best-practices/runtime-performance/profiling-with-chrome-devtools.md new file mode 100644 index 0000000..c78d6c2 --- /dev/null +++ b/adev-es/src/content/best-practices/runtime-performance/profiling-with-chrome-devtools.md @@ -0,0 +1,100 @@ +# Profiling with the Chrome DevTools + +Angular integrates with the [Chrome DevTools extensibility API](https://developer.chrome.com/docs/devtools/performance/extension) to present framework-specific data and insights directly in the [Chrome DevTools performance panel](https://developer.chrome.com/docs/devtools/performance/overview). + +With the integration enabled, you can [record a performance profile](https://developer.chrome.com/docs/devtools/performance#record) containing two sets of data: + +- Standard performance entries based on Chrome's understanding of your code executing in a browser, and +- Angular-specific entries contributed by the framework's runtime. + +Both sets of data are presented together on the same tab, but on separate tracks: + +Angular custom track in Chrome DevTools profiler + +Angular-specific data are expressed in terms of framework concepts (components, change detection, lifecycle hooks, etc.) alongside lower-level function and method calls captured by a browser. These two data sets are correlated, and you can switch between the different views and level of details. + +You can use the Angular track to better understand how your code runs in the browser, including: + +- Determining whether a given code block is part of the Angular application, or whether it belongs to another script running on the same page. +- Identifying performance bottlenecks and attribute those to specific components or services. +- Gaining deeper insight into Angular's inner working with a visual breakdown of each change detection cycle. + +## Recording a profile + +### Enable integration + +You can enable Angular profiling in one of two ways: + +1. Run `ng.enableProfiling()` in Chrome's console panel, or +1. Include a call to `enableProfiling()` in your application startup code (imported from `@angular/core`). + +NOTE: +Angular profiling works exclusively in development mode. + +Here is an example of how you can enable the integration in the application bootstrap to capture all possible events: + +```ts +import { enableProfiling } from '@angular/core'; +import { bootstrapApplication } from '@angular/platform-browser'; +import { MyApp } from './my-app'; + +// Turn on profiling *before* bootstrapping your application +// in order to capture all of the code run on start-up. +enableProfiling(); +bootstrapApplication(MyApp); +``` + +### Record a profile + +Use the **Record** button in the Chrome DevTools performance panel: + +Recording a profile + +See the [Chrome DevTools documentation](https://developer.chrome.com/docs/devtools/performance#record) for more details on recording profiles. + +## Interpreting a recorded profile + +You can use the "Angular" custom track to quickly identify and diagnose performance issues. The following sections describe some common profiling scenarios. + +### Differentiating between your Angular application and other tasks on the same page + +As Angular and Chrome data are presented on the separate but correlated tracks, you can see when Angular's application code is executed as opposed to some other browser processing (typically layout and paint) or other scripts running on the same page (in this case the custom Angular track does not have any data): + +Profile data: Angular vs. 3rd party scripts execution + +This allows you to determine whether further investigations should focus on the Angular application code or on other parts of your codebase or dependencies. + +### Color-coding + +Angular uses colors in the flame chart graph to distinguish tasks types: + +- 🟦 Blue represents TypeScript code written by the application developer (for example: services, component constructors and lifecycle hooks, etc.). +- 🟪 Purple represents templates code written by the application developer and transformed by the Angular compiler. +- 🟩 Green represents entry points to the application code and identifies _reasons_ for executing code. + +The following examples illustrate the described color-coding in various, real-life recordings. + +#### Example: Application bootstrapping + +The application bootstrap process usually consists of: + +- Triggers marked in blue, such as the call to the `bootstrapApplication`, instantiation of the root component, and initial change detection +- Various DI services instantiated during bootstrap, marked in green. + +Profile data: bootstrap application + +#### Example: Component execution + +One component processing is typically represented as an entry point (blue) followed by its template execution (purple). A template might, in turn, trigger instantiation of directives and execution of lifecycle hooks (green): + +Profile data: component processing + +#### Example: Change detection + +A change detection cycle usually consists of one or more data synchronization passes (blue), where each pass traverses a subset of components. + +Profile data: change detection + +With this data visualization, it is possible to immediately identify components that were involved in the change detection and which were skipped (typically the `OnPush` components that were not marked dirty). + +Additionally, you can inspect the number of synchronization passes for one change detection. Having more than one synchronization pass suggest that state is updated during change detection. You should avoid this, as it slows down page updates and can even result in infinite loops in the worst cases. diff --git a/adev-es/src/content/best-practices/runtime-performance/skipping-subtrees.md b/adev-es/src/content/best-practices/runtime-performance/skipping-subtrees.md index 86e608f..64e984d 100644 --- a/adev-es/src/content/best-practices/runtime-performance/skipping-subtrees.md +++ b/adev-es/src/content/best-practices/runtime-performance/skipping-subtrees.md @@ -31,7 +31,7 @@ This section examines several common change detection scenarios to illustrate An If Angular handles an event within a component without `OnPush` strategy, the framework executes change detection on the entire component tree. Angular will skip descendant component subtrees with roots using `OnPush`, which have not received new inputs. -As an example, if we set the change detection strategy of `MainComponent` to `OnPush` and the user interacts with a component outside the subtree with root `MainComponent`, Angular will check all the green components from the diagram below (`AppComponent`, `HeaderComponent`, `SearchComponent`, `ButtonComponent`) unless `MainComponent` receives new inputs: +As an example, if we set the change detection strategy of `MainComponent` to `OnPush` and the user interacts with a component outside the subtree with root `MainComponent`, Angular will check all the pink components from the diagram below (`AppComponent`, `HeaderComponent`, `SearchComponent`, `ButtonComponent`) unless `MainComponent` receives new inputs: ```mermaid graph TD; @@ -43,14 +43,11 @@ graph TD; main --- details[DetailsComponent]; event>Event] --- search -style main fill:#E4BE74,color:#000 -style login fill:#E4BE74,color:#000 -style details fill:#E4BE74,color:#000 - -style app fill:#C1D5B0,color:#000 -style header fill:#C1D5B0,color:#000 -style button fill:#C1D5B0,color:#000 -style search fill:#C1D5B0,color:#000 +class app checkedNode +class header checkedNode +class button checkedNode +class search checkedNode +class event eventNode ``` ## An event is handled by a component with OnPush @@ -69,14 +66,13 @@ graph TD; main --- details[DetailsComponent]; event>Event] --- main -style login fill:#E4BE74,color:#000 - -style app fill:#C1D5B0,color:#000 -style header fill:#C1D5B0,color:#000 -style button fill:#C1D5B0,color:#000 -style search fill:#C1D5B0,color:#000 -style main fill:#C1D5B0,color:#000 -style details fill:#C1D5B0,color:#000 +class app checkedNode +class header checkedNode +class button checkedNode +class search checkedNode +class main checkedNode +class details checkedNode +class event eventNode ``` ## An event is handled by a descendant of a component with OnPush @@ -95,13 +91,14 @@ graph TD; main --- details[DetailsComponent]; event>Event] --- login -style app fill:#C1D5B0,color:#000 -style header fill:#C1D5B0,color:#000 -style button fill:#C1D5B0,color:#000 -style search fill:#C1D5B0,color:#000 -style login fill:#C1D5B0,color:#000 -style main fill:#C1D5B0,color:#000 -style details fill:#C1D5B0,color:#000 +class app checkedNode +class header checkedNode +class button checkedNode +class search checkedNode +class login checkedNode +class main checkedNode +class details checkedNode +class event eventNode ``` ## New inputs to component with OnPush @@ -120,15 +117,13 @@ graph TD; main --- details[DetailsComponent]; event>Parent passes new input to MainComponent] -style login fill:#E4BE74,color:#000 - -linkStyle 1 stroke:green -style app fill:#C1D5B0,color:#000 -style header fill:#C1D5B0,color:#000 -style button fill:#C1D5B0,color:#000 -style search fill:#C1D5B0,color:#000 -style main fill:#C1D5B0,color:#000 -style details fill:#C1D5B0,color:#000 +class app checkedNode +class header checkedNode +class button checkedNode +class search checkedNode +class main checkedNode +class details checkedNode +class event eventNode ``` ## Edge cases diff --git a/adev-es/src/content/best-practices/runtime-performance/zone-pollution.md b/adev-es/src/content/best-practices/runtime-performance/zone-pollution.md index 27b22fc..e232ec0 100644 --- a/adev-es/src/content/best-practices/runtime-performance/zone-pollution.md +++ b/adev-es/src/content/best-practices/runtime-performance/zone-pollution.md @@ -2,7 +2,7 @@ **Zone.js** is a signaling mechanism that Angular uses to detect when an application state might have changed. It captures asynchronous operations like `setTimeout`, network requests, and event listeners. Angular schedules change detection based on signals from Zone.js. -In some cases scheduled [tasks](https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide#tasks) or [microtasks](https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide#microtasks) don’t make any changes in the data model, which makes running change detection unnecessary. Common examples are: +In some cases scheduled [tasks](https://developer.mozilla.org/docs/Web/API/HTML_DOM_API/Microtask_guide#tasks) or [microtasks](https://developer.mozilla.org/docs/Web/API/HTML_DOM_API/Microtask_guide#microtasks) don’t make any changes in the data model, which makes running change detection unnecessary. Common examples are: * `requestAnimationFrame`, `setTimeout` or `setInterval` * Task or microtask scheduling by third-party libraries @@ -26,7 +26,8 @@ import { Component, NgZone, OnInit } from '@angular/core'; @Component(...) class AppComponent implements OnInit { - constructor(private ngZone: NgZone) {} + private ngZone = inject(NgZone); + ngOnInit() { this.ngZone.runOutsideAngular(() => setInterval(pollForUpdates), 500); } @@ -35,7 +36,7 @@ class AppComponent implements OnInit { The preceding snippet instructs Angular to call `setInterval` outside the Angular Zone and skip running change detection after `pollForUpdates` runs. -Third-party libraries commonly trigger unnecessary change detection cycles when their APIs are invoked within the Angular zone. This phenomenon particularly affects libraries that setup event listeners or initiate other tasks (such as timers, XHR requests, etc.). Avoid these extra cycles by calling library APIs outside the Angular zone: +Third-party libraries commonly trigger unnecessary change detection cycles when their APIs are invoked within the Angular zone. This phenomenon particularly affects libraries that set up event listeners or initiate other tasks (such as timers, XHR requests, etc.). Avoid these extra cycles by calling library APIs outside the Angular zone: import { Component, NgZone, OnInit } from '@angular/core'; @@ -43,8 +44,7 @@ import * as Plotly from 'plotly.js-dist-min'; @Component(...) class AppComponent implements OnInit { - - constructor(private ngZone: NgZone) {} + private ngZone = inject(NgZone); ngOnInit() { this.ngZone.runOutsideAngular(() => { @@ -66,9 +66,9 @@ import * as Plotly from 'plotly.js-dist-min'; @Component(...) class AppComponent implements OnInit { - plotlyClick = output(); + private ngZone = inject(NgZone); - constructor(private ngZone: NgZone) {} + plotlyClick = output(); ngOnInit() { this.ngZone.runOutsideAngular(() => { @@ -98,9 +98,9 @@ import * as Plotly from 'plotly.js-dist-min'; @Component(...) class AppComponent implements OnInit { - plotlyClick = output(); + private ngZone = inject(NgZone); - constructor(private ngZone: NgZone) {} + plotlyClick = output(); ngOnInit() { this.ngZone.runOutsideAngular(() => { @@ -120,4 +120,4 @@ class AppComponent implements OnInit { } -The scenario of dispatching events outside of the Angular zone may also arise. It's important to remember that triggering change detection (for example, manually) may result to the creation/update of views outside of the Angular zone. \ No newline at end of file +The scenario of dispatching events outside of the Angular zone may also arise. It's important to remember that triggering change detection (for example, manually) may result in the creation/update of views outside of the Angular zone. \ No newline at end of file diff --git a/adev-es/src/content/best-practices/style-guide.md b/adev-es/src/content/best-practices/style-guide.md index 28219b3..fe9c834 100644 --- a/adev-es/src/content/best-practices/style-guide.md +++ b/adev-es/src/content/best-practices/style-guide.md @@ -1,893 +1,318 @@ # Angular coding style guide -Looking for an opinionated guide to Angular syntax, conventions, and application structure? -Step right in. -This style guide presents preferred conventions and, as importantly, explains why. +## Introduction -## Style vocabulary +This guide covers a range of style conventions for Angular application code. These recommendations +are not required for Angular to work, but instead establish a set of coding practices that promote +consistency across the Angular ecosystem. A consistent set of practices makes it easier to share +code and move between projects. -Each guideline describes either a good or bad practice, and all have a consistent presentation. +This guide does _not_ cover TypeScript or general coding practices unrelated to Angular. For +TypeScript, check +out [Google's TypeScript style guide](https://google.github.io/styleguide/tsguide.html). -The wording of each guideline indicates how strong the recommendation is. +### When in doubt, prefer consistency -**Do** is one that should always be followed. -*Always* might be a bit too strong of a word. -Guidelines that literally should always be followed are extremely rare. -On the other hand, you need a really unusual case for breaking a *Do* guideline. - -**Consider** guidelines should generally be followed. -If you fully understand the meaning behind the guideline and have a good reason to deviate, then do so. -Aim to be consistent. - -**Avoid** indicates something you should almost never do. -Code examples to *avoid* have an unmistakable red header. - -**Why**?
-Gives reasons for following the previous recommendations. - -## File structure conventions - -Some code examples display a file that has one or more similarly named companion files. -For example, `hero.component.ts` and `hero.component.html`. - -The guideline uses the shortcut `hero.component.ts|html|css|spec` to represent those various files. -Using this shortcut makes this guide's file structures easier to read and more terse. - -## Single responsibility - -Apply the [*single responsibility principle (SRP)*](https://wikipedia.org/wiki/Single_responsibility_principle) to all components, services, and other symbols. -This helps make the application cleaner, easier to read and maintain, and more testable. - -### Rule of One - -#### Style 01-01 - -**Do** define one thing, such as a service or component, per file. - -**Consider** limiting files to 400 lines of code. - -**Why**?
-One component per file makes it far easier to read, maintain, and avoid collisions with teams in source control. - -**Why**?
-One component per file avoids hidden bugs that often arise when combining components in a file where they may share variables, create unwanted closures, or unwanted coupling with dependencies. - -**Why**?
-A single component can be the default export for its file which facilitates lazy loading with the router. - -The key is to make the code more reusable, easier to read, and less mistake-prone. - -The following *negative* example defines the `AppComponent`, bootstraps the app, -defines the `Hero` model object, and loads heroes from the server all in the same file. -*Don't do this*. - - - -It is a better practice to redistribute the component and its -supporting classes into their own, dedicated files. - - - - - - - - - - - -As the application grows, this rule becomes even more important. +Whenever you encounter a situation in which these rules contradict the style of a particular file, +prioritize maintaining consistency within a file. Mixing different style conventions in a single +file creates more confusion than diverging from the recommendations in this guide. ## Naming -Naming conventions are hugely important to maintainability and readability. -This guide recommends naming conventions for the file name and the symbol name. - -### General Naming Guidelines - -#### Style 02-01 - -**Do** use consistent names for all symbols. - -**Do** follow a pattern that describes the symbol's feature then its type. -The recommended pattern is `feature.type.ts`. - -**Why**?
-Naming conventions help provide a consistent way to find content at a glance. -Consistency within the project is vital. -Consistency with a team is important. -Consistency across a company provides tremendous efficiency. - -**Why**?
-The naming conventions should help find desired code faster and make it easier to understand. - -**Why**?
-Names of folders and files should clearly convey their intent. -For example, `app/heroes/hero-list.component.ts` may contain a component that manages a list of heroes. - -### Separate file names with dots and dashes - -#### Style 02-02 - -**Do** use dashes to separate words in the descriptive name. - -**Do** use dots to separate the descriptive name from the type. - -**Do** use consistent type names for all components following a pattern that describes the component's feature then its type. -A recommended pattern is `feature.type.ts`. - -**Do** use conventional type names including `.service`, `.component`, `.pipe`, `.module`, and `.directive`. -Invent additional type names if you must but take care not to create too many. - -**Why**?
-Type names provide a consistent way to quickly identify what is in the file. - -**Why**?
-Type names make it easy to find a specific file type using an editor or IDE's fuzzy search techniques. - -**Why**?
-Unabbreviated type names such as `.service` are descriptive and unambiguous. -Abbreviations such as `.srv`, `.svc`, and `.serv` can be confusing. - -**Why**?
-Type names provide pattern matching for any automated tasks. - -### Symbols and file names - -#### Style 02-03 - -**Do** use consistent names for all assets named after what they represent. - -**Do** use upper camel case for class names. - -**Do** match the name of the symbol to the name of the file. - -**Do** append the symbol name with the conventional suffix \(such as `Component`, `Directive`, `Module`, `Pipe`, or `Service`\) for a thing of that type. - -**Do** give the filename the conventional suffix \(such as `.component.ts`, `.directive.ts`, `.module.ts`, `.pipe.ts`, or `.service.ts`\) for a file of that type. - -**Why**?
-Consistent conventions make it easy to quickly identify and reference assets of different types. - -| Symbol name | File name | -|:--- |:--- | -| @Component({ … })
export class AppComponent { }
| app.component.ts | -| @Component({ … })
export class HeroesComponent { }
| heroes.component.ts | -| @Component({ … })
export class HeroListComponent { }
| hero-list.component.ts | -| @Component({ … })
export class HeroDetailComponent { }
| hero-detail.component.ts | -| @Directive({ … })
export class ValidationDirective { }
| validation.directive.ts | -| @NgModule({ … })
export class AppModule
| app.module.ts | -| @Pipe({ name: 'initCaps' })
export class InitCapsPipe implements PipeTransform { }
| init-caps.pipe.ts | -| @Injectable()
export class UserProfileService { }
| user-profile.service.ts | - -### Service names - -#### Style 02-04 - -**Do** use consistent names for all services named after their feature. - -**Do** suffix a service class name with `Service`. -For example, something that gets data or heroes should be called a `DataService` or a `HeroService`. - -A few terms are unambiguously services. -They typically indicate agency by ending in "-er". -You may prefer to name a service that logs messages `Logger` rather than `LoggerService`. -Decide if this exception is agreeable in your project. -As always, strive for consistency. - -**Why**?
-Provides a consistent way to quickly identify and reference services. - -**Why**?
-Clear service names such as `Logger` do not require a suffix. - -**Why**?
-Service names such as `Credit` are nouns and require a suffix and should be named with a suffix when it is not obvious if it is a service or something else. - -| Symbol name | File name | -|:--- |:--- | -| @Injectable()
export class HeroDataService { }
| hero-data.service.ts | -| @Injectable()
export class CreditService { }
| credit.service.ts | -| @Injectable()
export class Logger { }
| logger.service.ts | - -### Bootstrapping - -#### Style 02-05 - -**Do** put bootstrapping and platform logic for the application in a file named `main.ts`. - -**Do** include error handling in the bootstrapping logic. +### Separate words in file names with hyphens -**Avoid** putting application logic in `main.ts`. -Instead, consider placing it in a component or service. +Separate words within a file name with hyphens (`-`). For example, a component named `UserProfile` +has a file name `user-profile.ts`. -**Why**?
-Follows a consistent convention for the startup logic of an app. +### Use the same name for a file's tests with `.spec` at the end -**Why**?
-Follows a familiar convention from other technology platforms. +For unit tests, end file names with `.spec.ts`. For example, the unit test file for +the `UserProfile` component has the file name `user-profile.spec.ts`. - +### Match file names to the TypeScript identifier within -### Component selectors +File names should generally describe the contents of the code in the file. When the file contains a +TypeScript class, the file name should reflect that class name. For example, a file containing a +component named `UserProfile` has the name `user-profile.ts`. -#### Style 05-02 +If the file contains more than one primary namable identifier, choose a name that describes the +common theme to the code within. If the code in a file does not fit within a common theme or feature +area, consider breaking the code up into different files. Avoid overly generic file names +like `helpers.ts`, `utils.ts`, or `common.ts`. -**Do** use *dashed-case* or *kebab-case* for naming the element selectors of components. +### Use the same file name for a component's TypeScript, template, and styles -**Why**?
-Keeps the element names consistent with the specification for [Custom Elements](https://www.w3.org/TR/custom-elements). +Components typically consist of one TypeScript file, one template file, and one style file. These +files should share the same name with different file extensions. For example, a `UserProfile` +component can have the files `user-profile.ts`, `user-profile.html`, and `user-profile.css`. - +If a component has more than one style file, append the name with additional words that describe the +styles specific to that file. For example, `UserProfile` might have style +files `user-profile-settings.css` and `user-profile-subscription.css`. - - - - +## Project structure -### Component custom prefix +### All the application's code goes in a directory named `src` -#### Style 02-07 +All of your Angular UI code (TypeScript, HTML, and styles) should live inside a directory +named `src`. Code that's not related to UI, such as configuration files or scripts, should live +outside the `src` directory. -**Do** use a hyphenated, lowercase element selector value; for example, `admin-users`. +This keeps the root application directory consistent between different Angular projects and creates +a clear separation between UI code and other code in your project. -**Do** use a prefix that identifies the feature area or the application itself. +### Bootstrap your application in a file named `main.ts` directly inside `src` -**Why**?
-Prevents element name collisions with components in other applications and with native HTML elements. +The code to start up, or **bootstrap**, an Angular application should always live in a file +named `main.ts`. This represents the primary entry point to the application. -**Why**?
-Makes it easier to promote and share the component in other applications. +### Group closely related files together in the same directory -**Why**?
-Components are easy to identify in the DOM. +Angular components consist of a TypeScript file and, optionally, a template and one or more style +files. You should group these together in the same directory. - +Unit tests should live in the same directory as the code-under-test. Avoid collecting unrelated +tests into a single `tests` directory. - +### Organize your project by feature areas - +Organize your project into subdirectories based on the features or your application or common themes +to the code in those directories. For example, the project structure for a movie theater site, +MovieReel, might look like this: - - -### Directive selectors - -#### Style 02-06 - -**Do** Use lower camel case for naming the selectors of directives. - -**Why**?
-Keeps the names of the properties defined in the directives that are bound to the view consistent with the attribute names. - -**Why**?
-The Angular HTML parser is case-sensitive and recognizes lower camel case. - -### Directive custom prefix - -#### Style 02-08 - -**Do** spell non-element selectors in lower camel case unless the selector is meant to match a native HTML attribute. - -**Don't** prefix a directive name with `ng` because that prefix is reserved for Angular and using it could cause bugs that are difficult to diagnose. - -**Why**?
-Prevents name collisions. - -**Why**?
-Directives are easily identified. - - - - - -### Pipe names - -#### Style 02-09 - -**Do** use consistent names for all pipes, named after their feature. -The pipe class name should use `UpperCamelCase` \(the general convention for class names\), and the corresponding `name` string should use *lowerCamelCase*. -The `name` string cannot use hyphens \("dash-case" or "kebab-case"\). - -**Why**?
-Provides a consistent way to quickly identify and reference pipes. - -| Symbol name | File name | -|:--- |:--- | -| @Pipe({ standalone: true, name: 'ellipsis' })
export class EllipsisPipe implements PipeTransform { }
| ellipsis.pipe.ts | -| @Pipe({ standalone: true, name: 'initCaps' })
export class InitCapsPipe implements PipeTransform { }
| init-caps.pipe.ts | - -### Unit test file names - -#### Style 02-10 - -**Do** name test specification files the same as the component they test. - -**Do** name test specification files with a suffix of `.spec`. - -**Why**?
-Provides a consistent way to quickly identify tests. - -**Why**?
-Provides pattern matching for [karma](https://karma-runner.github.io) or other test runners. - -| Test type | File names | -|:--- |:--- | -| Components | heroes.component.spec.ts
hero-list.component.spec.ts
hero-detail.component.spec.ts | -| Services | logger.service.spec.ts
hero.service.spec.ts
filter-text.service.spec.ts | -| Pipes | ellipsis.pipe.spec.ts
init-caps.pipe.spec.ts | - -## Application structure and NgModules - -Have a near-term view of implementation and a long-term vision. -Start small but keep in mind where the application is heading. - -All of the application's code goes in a folder named `src`. -All feature areas are in their own folder. - -All content is one asset per file. -Each component, service, and pipe is in its own file. -All third party vendor scripts are stored in another folder and not in the `src` folder. -Use the naming conventions for files in this guide. - -### Overall structural guidelines - -#### Style 04-06 - -**Do** start small but keep in mind where the application is heading down the road. - -**Do** have a near term view of implementation and a long term vision. - -**Do** put all of the application's code in a folder named `src`. - -**Consider** creating a folder for a component when it has multiple accompanying files \(`.ts`, `.html`, `.css`, and `.spec`\). - -**Why**?
-Helps keep the application structure small and easy to maintain in the early stages, while being easy to evolve as the application grows. - -**Why**?
-Components often have four files \(for example, `*.html`, `*.css`, `*.ts`, and `*.spec.ts`\) and can clutter a folder quickly. - -Here is a compliant folder and file structure: - -```markdown -project root -├── src -│ ├── app -│ │ ├── core -│ │ │ └── exception.service.ts|spec.ts -│ │ │ └── user-profile.service.ts|spec.ts -│ │ ├── heroes -│ │ │ ├── hero -│ │ │ │ └── hero.component.ts|html|css|spec.ts -│ │ │ ├── hero-list -│ │ │ │ └── hero-list.component.ts|html|css|spec.ts -│ │ │ ├── shared -│ │ │ │ └── hero-button.component.ts|html|css|spec.ts -│ │ │ │ └── hero.model.ts -│ │ │ │ └── hero.service.ts|spec.ts -│ │ │ └── heroes.component.ts|html|css|spec.ts -│ │ │ └── heroes.routes.ts -│ │ ├── shared -│ │ │ └── init-caps.pipe.ts|spec.ts -│ │ │ └── filter-text.component.ts|spec.ts -│ │ │ └── filter-text.service.ts|spec.ts -│ │ ├── villains -│ │ │ ├── villain -│ │ │ │ └── … -│ │ │ ├── villain-list -│ │ │ │ └── … -│ │ │ ├── shared -│ │ │ │ └── … -│ │ │ └── villains.component.ts|html|css|spec.ts -│ │ │ └── villains.module.ts -│ │ │ └── villains-routing.module.ts -│ │ └── app.component.ts|html|css|spec.ts -│ │ └── app.routes.ts -│ └── main.ts -│ └── index.html -│ └── … -└── node_modules/… -└── … ``` - -HELPFUL: While components in dedicated folders are widely preferred, another option for small applications is to keep components flat \(not in a dedicated folder\). -This adds up to four files to the existing folder, but also reduces the folder nesting. -Whatever you choose, be consistent. - -### *Folders-by-feature* structure - -#### Style 04-07 - -**Do** create folders named for the feature area they represent. - -**Why**?
-A developer can locate the code and identify what each file represents at a glance. -The structure is as flat as it can be and there are no repetitive or redundant names. - -**Why**?
-Helps reduce the application from becoming cluttered through organizing the content. - -**Why**?
-When there are a lot of files, for example 10+, locating them is easier with a consistent folder structure and more difficult in a flat structure. - -For more information, refer to [this folder and file structure example](#file-tree). - -### App *root module* - -IMPORTANT: The following style guide recommendations are for applications based on `NgModule`. New applications should use standalone components, directives, and pipes instead. - -#### Style 04-08 - -**Do** create an NgModule in the application's root folder, for example, in `/src/app` if creating a `NgModule` based app. - -**Why**?
-Every `NgModule` based application requires at least one root NgModule. - -**Consider** naming the root module `app.module.ts`. - -**Why**?
-Makes it easier to locate and identify the root module. - - - -### Feature modules - -#### Style 04-09 - -**Do** create an NgModule for all distinct features in an application; for example, a `Heroes` feature. - -**Do** place the feature module in the same named folder as the feature area; for example, in `app/heroes`. - -**Do** name the feature module file reflecting the name of the feature area and folder; for example, `app/heroes/heroes.module.ts`. - -**Do** name the feature module symbol reflecting the name of the feature area, folder, and file; for example, `app/heroes/heroes.module.ts` defines `HeroesModule`. - -**Why**?
-A feature module can expose or hide its implementation from other modules. - -**Why**?
-A feature module identifies distinct sets of related components that comprise the feature area. - -**Why**?
-A feature module can easily be routed to both eagerly and lazily. - -**Why**?
-A feature module defines clear boundaries between specific functionality and other application features. - -**Why**?
-A feature module helps clarify and make it easier to assign development responsibilities to different teams. - -**Why**?
-A feature module can easily be isolated for testing. - -### Shared feature module - -#### Style 04-10 - -**Do** create a feature module named `SharedModule` in a `shared` folder; for example, `app/shared/shared.module.ts` defines `SharedModule`. - -**Do** declare components, directives, and pipes in a shared module when those items will be re-used and referenced by the components declared in other feature modules. - -**Consider** using the name SharedModule when the contents of a shared -module are referenced across the entire application. - -**Consider** *not* providing services in shared modules. -Services are usually singletons that are provided once for the entire application or in a particular feature module. -There are exceptions, however. -For example, in the sample code that follows, notice that the `SharedModule` provides `FilterTextService`. -This is acceptable here because the service is stateless;that is, the consumers of the service aren't impacted by new instances. - -**Do** import all modules required by the assets in the `SharedModule`; for example, `CommonModule` and `FormsModule`. - -**Why**?
-`SharedModule` will contain components, directives, and pipes that may need features from another common module; for example, `ngFor` in `CommonModule`. - -**Do** declare all components, directives, and pipes in the `SharedModule`. - -**Do** export all symbols from the `SharedModule` that other feature modules need to use. - -**Why**?
-`SharedModule` exists to make commonly used components, directives, and pipes available for use in the templates of components in many other modules. - -**Avoid** specifying app-wide singleton providers in a `SharedModule`. -Intentional singletons are OK. -Take care. - -**Why**?
-A lazy loaded feature module that imports that shared module will make its own copy of the service and likely have undesirable results. - -**Why**?
-You don't want each module to have its own separate instance of singleton services. -Yet there is a real danger of that happening if the `SharedModule` provides a service. - -```markdown -project root -├──src -├──├──app -├──├──├── shared -├──├──├──└── shared.module.ts -├──├──├──└── init-caps.pipe.ts|spec.ts -├──├──├──└── filter-text.component.ts|spec.ts -├──├──├──└── filter-text.service.ts|spec.ts -├──├──└── app.component.ts|html|css|spec.ts -├──├──└── app.module.ts -├──├──└── app-routing.module.ts -├──└── main.ts -├──└── index.html -└── … +src/ +├─ movie-reel/ +│ ├─ show-times/ +│ │ ├─ film-calendar/ +│ │ ├─ film-details/ +│ ├─ reserve-tickets/ +│ │ ├─ payment-info/ +│ │ ├─ purchase-confirmation/ ``` - - - - - - - - - -### Lazy Loaded folders - -#### Style 04-11 - -A distinct application feature or workflow may be *lazy loaded* or *loaded on demand* rather than when the application starts. - -**Do** put the contents of lazy loaded features in a *lazy loaded folder*. -A typical *lazy loaded folder* contains a *routing component*, its child components, and their related assets. - -**Why**?
-The folder makes it easy to identify and isolate the feature content. - -## Components - -### Components as elements - -#### Style 05-03 - -**Consider** giving components an *element* selector, as opposed to *attribute* or *class* selectors. - -**Why**?
-Components have templates containing HTML and optional Angular template syntax. -They display content. -Developers place components on the page as they would native HTML elements and web components. - -**Why**?
-It is easier to recognize that a symbol is a component by looking at the template's html. - -HELPFUL: There are a few cases where you give a component an attribute, such as when you want to augment a built-in element. -For example, [Material Design](https://material.angular.io/components/button/overview) uses this technique with ` - -## Appendix + + +``` -Useful tools and tips for Angular. +Using meaningful names like this makes it easier to tell what an event does from reading the +template. -### File templates and snippets +For keyboard events, you can use Angular's key event modifiers with specific handler names: -#### Style A-02 +```html +