Compare commits

...

10 Commits

Author SHA1 Message Date
5d8f869913 docs: fix error w/master 2020-11-27 11:25:22 -04:00
2ca7ee8f54 docs: translate guide/guide/libraries 2020-11-27 11:24:00 -04:00
ebee38994f docs: translate tutorial/toh-pt5.md (#281)
- Se agrego la versión en ingles .en.md

Fixes #78
2020-11-27 04:05:57 -05:00
6ebb0246ab docs: fix error in e2e tests in /tutorial 2020-11-27 03:47:58 -05:00
f8c56ec8eb docs: translate guide/guide/libraries 2020-11-25 13:34:15 -04:00
c7a054fa31 fixing github lost repo 2020-11-01 03:35:59 -05:00
6ff6acbb21 docs: translate tutorial/toh-pt1.md (#278)
Closes #74
2020-11-01 03:34:26 -05:00
8a09cb9794 test: fix aio e2e
- check for translated /features page content and title

Fixes #296
2020-10-22 21:12:06 -04:00
eeb59b4f4d fix: master/toh-pt3 (#295)
Co-authored-by: Pato <11162114+devpato@users.noreply.github.com>
Co-authored-by: Michael Prentice <splaktar@gmail.com>
2020-10-22 19:24:06 -04:00
7afbc3a1c1 docs: update PULL_REQUEST_TEMPLATE.md (#240)
Co-authored-by: Michael Prentice <splaktar@gmail.com>
2020-10-22 19:00:26 -04:00
10 changed files with 1227 additions and 375 deletions

View File

@ -1,12 +1,12 @@
## Lista de Verificación del PR
Comprueba si tu PR cumple los siguientes requisitos:
- [ ] El mensaje de commit esta conforme con [nuestras reglas](https://github.com/angular/angular/blob/master/CONTRIBUTING.md#commit)
- [ ] El mensaje de commit esta conforme con [nuestras reglas](https://github.com/angular-hispano/angular/blob/master/CONTRIBUTING.md#-formato-para-el-mensaje-de-los-commits)
- [ ] Probe los cambios que agregué (arreglo de bugs / funcionalidades)
- [ ] Revisé previamente las traducciones o cambios de contenido
- [ ] Consulté el [diccionario de términos](https://github.com/angular-hispano/angular/blob/master/aio/diccionario-de-términos.md) en español
- [ ] He creado dos archivos con la extensión correspondiente(.en.md para el archivo en inglés y .md para el Archivo en español)
- [ ] La PR fue enlazada con el issue correspondiente
- [ ] He enlazado el commit con el issue correspondiente <!-- ejemplo Fixes #X -->
## Tipo de PR

View File

@ -0,0 +1,37 @@
# Overview of Angular libraries
Many applications need to solve the same general problems, such as presenting a unified user interface, presenting data, and allowing data entry.
Developers can create general solutions for particular domains that can be adapted for re-use in different apps.
Such a solution can be built as Angular *libraries* and these libraries can be published and shared as *npm packages*.
An Angular library is an Angular [project](guide/glossary#project) that differs from an app in that it cannot run on its own.
A library must be imported and used in an app.
Libraries extend Angular's base functionality. For example, to add [reactive forms](guide/reactive-forms) to an app, add the library package using `ng add @angular/forms`, then import the `ReactiveFormsModule` from the `@angular/forms` library in your application code.
Similarly, adding the [service worker](guide/service-worker-intro) library to an Angular application is one of the steps for turning an application into a [Progressive Web App](https://developers.google.com/web/progressive-web-apps/) (PWA).
[Angular Material](https://material.angular.io/) is an example of a large, general-purpose library that provides sophisticated, reusable, and adaptable UI components.
Any app developer can use these and other libraries that have been published as npm packages by the Angular team or by third parties. See [Using Published Libraries](guide/using-libraries).
## Creating libraries
If you have developed functionality that is suitable for reuse, you can create your own libraries.
These libraries can be used locally in your workspace, or you can publish them as [npm packages](guide/npm-packages) to share with other projects or other Angular developers.
These packages can be published to the npm registry, a private npm Enterprise registry, or a private package management system that supports npm packages.
See [Creating Libraries](guide/creating-libraries).
Whether you decide to package functionality as a library is an architectural decision, similar to deciding whether a piece of functionality is a component or a service, or deciding on the scope of a component.
Packaging functionality as a library forces the artifacts in the library to be decoupled from the application's business logic.
This can help to avoid various bad practices or architecture mistakes that can make it difficult to decouple and reuse code in the future.
Putting code into a separate library is more complex than simply putting everything in one app.
It requires more of an investment in time and thought for managing, maintaining, and updating the library.
This complexity can pay off, however, when the library is being used in multiple apps.
<div class="alert is-helpful">
Note that libraries are intended to be used by Angular apps.
To add Angular functionality to non-Angular web apps, you can use [Angular custom elements](guide/elements).
</div>

View File

@ -1,37 +1,38 @@
# Overview of Angular libraries
# Vista general de librerías para Angular
Many applications need to solve the same general problems, such as presenting a unified user interface, presenting data, and allowing data entry.
Developers can create general solutions for particular domains that can be adapted for re-use in different apps.
Such a solution can be built as Angular *libraries* and these libraries can be published and shared as *npm packages*.
Muchas aplicaciones necesitan resolver los mismos problemas en general, como presentar una interfaz de usuario unificada, mostrar datos, permitir entrada de datos etc.
Los desarrolladores puede crear soluciones generales para dominios particulares que pueden ser adoptados para re usarse en diferentes aplicaciones.
Tal solución se puede construir como *librerías* para Angular y estas librerías pueden ser publicadas y compartidas como *paquetes de npm*.
An Angular library is an Angular [project](guide/glossary#project) that differs from an app in that it cannot run on its own.
A library must be imported and used in an app.
Una librería de Angular es como un [proyecto](guide/glossary#project) lo que la diferencia de una app es que no puede correr por si sola.
Una librería debe ser importada y usada en una app.
Libraries extend Angular's base functionality. For example, to add [reactive forms](guide/reactive-forms) to an app, add the library package using `ng add @angular/forms`, then import the `ReactiveFormsModule` from the `@angular/forms` library in your application code.
Similarly, adding the [service worker](guide/service-worker-intro) library to an Angular application is one of the steps for turning an application into a [Progressive Web App](https://developers.google.com/web/progressive-web-apps/) (PWA).
[Angular Material](https://material.angular.io/) is an example of a large, general-purpose library that provides sophisticated, reusable, and adaptable UI components.
Las librerías extienden la funcionalidad base de Angular. Por ejemplo, para agregar [formularios reactivos](guide/reactive-forms) en una app, agregamos la librería usando `ng add @angular/forms`, entonces importamos el `ReactiveFormsModule` desde la librería `@angular/forms` el código de la aplicación.
De igual manera, agregar la librería [service worker](guide/service-worker-intro) dentro de una aplicación Angular es el primer paso para convertir una aplicación a una [Progressive Web App](https://developers.google.com/web/progressive-web-apps/) (PWA).
[Angular Material](https://material.angular.io/) es un ejemplo de una gran librería de propósito general que ofrece componentes de UI sofisticados, reutilizables y adaptables.
Any app developer can use these and other libraries that have been published as npm packages by the Angular team or by third parties. See [Using Published Libraries](guide/using-libraries).
Cualquier desarrollador de apps puede usar estas y otras librerías que han sido publicadas como paquetes de npm por el equipo de Angular o por terceros. Mirá [Usando librerías publicadas](guide/using-libraries).
## Creating libraries
## Creando librerías
If you have developed functionality that is suitable for reuse, you can create your own libraries.
These libraries can be used locally in your workspace, or you can publish them as [npm packages](guide/npm-packages) to share with other projects or other Angular developers.
These packages can be published to the npm registry, a private npm Enterprise registry, or a private package management system that supports npm packages.
See [Creating Libraries](guide/creating-libraries).
Si tu tienes funcionalidades desarrolladas que puede ser adecuadas para re usarse tu puedes crear tus propias librerías.
Estas librerías puede ser usadas localmente en tu espacio de trabajo, o puede ser publicadas como [paquetes de npm](guide/npm-packages) para compartir con otros proyectos o otros desarrolladores Angular.
Estos paquetes pueden ser publicados en el registro de npm, en un registro empresarial privado de npm, o en un sistema de gestión de paquetes privado que soporte paquetes de npm.
Mirá [Creando librerías](guide/creating-libraries).
Whether you decide to package functionality as a library is an architectural decision, similar to deciding whether a piece of functionality is a component or a service, or deciding on the scope of a component.
Si tu decides empaquetar una funcionalidad como una librería es una decisión de arquitectura, similar a decidir entre si una pieza de funcionalidad es un componente o un servicio, o decidir el alcance de un componente.
Packaging functionality as a library forces the artifacts in the library to be decoupled from the application's business logic.
This can help to avoid various bad practices or architecture mistakes that can make it difficult to decouple and reuse code in the future.
Empaquetar funcionalidad como una librería fuerza a
que los artefactos en la librería puedan ser desacoplados de la lógica de negocio de la aplicación.
Esto puede ayudar a evitar varias malas practicas o errores de arquitectura que puede hacer difícil desacoplar y re usar código en el futuro.
Putting code into a separate library is more complex than simply putting everything in one app.
It requires more of an investment in time and thought for managing, maintaining, and updating the library.
This complexity can pay off, however, when the library is being used in multiple apps.
Poniendo el código dentro de una librería separada es más complejo que simplemente poner todo en una sola aplicación.
Esto requiere una inversión mayor de tiempo y pensar para administrar, mantener y actualizar la librería.
Sin embargo esta complejidad puede valer la pena cuando la librería esta siendo usada en multiples aplicaciones.
<div class="alert is-helpful">
Note that libraries are intended to be used by Angular apps.
To add Angular functionality to non-Angular web apps, you can use [Angular custom elements](guide/elements).
Note que las librerías están destinadas para ser usadas por aplicaciones Angular.
Para agregar funcionalidad de Angular hacia una aplicación web que no es Angular, puedes usar [Angular custom elements](guide/elements).
</div>

View File

@ -1,13 +1,13 @@
# Contributing to resources.json
# Contribuir a resources.json
## About this list
We maintain a small list of some of the top Angular resources from across the community, stored in `resources.json`. This list is not intended to be comprehensive, but to act as a starting point to connect Angular developers to the rest of the community.
## Acerca de esta lista
Mantenemos una pequeña lista de algunos de los principales recursos de Angular de toda la comunidad, almacenados en `resources.json`. Esta lista no pretende ser completa, sino actuar como un punto de partida para conectar a los desarrolladores de Angular con el resto de la comunidad.
## How do I get listed?
While we can't accept all contributions, qualifying contributions can be submitted via a PR adding yourself to the `resources.json` file. All contributions should be in the appropriate section and must meet the following criteria:
## ¿Cómo me incluyo?
Si bien no podemos aceptar todas las contribuciones, las contribuciones que califiquen se pueden enviar a través de un PR que se agregue al archivo `resources.json`. Todas las contribuciones deben estar en la sección correspondiente y deben cumplir con los siguientes criterios:
1. Your contribution must be valid, and contain a link to a page talking specifically about using Angular
1. Your contribution should have a clear and concise title and description
1. Your resource should follow our brand guidelines (see our [Presskit](presskit))
1. Your resource should have significant benefit to Angular developers
1. Your resource should already have traction and praise from Angular developers
1. Su contribución debe ser válida y contener un enlace a una página que hable específicamente sobre el uso de Angular
1. Tu contribución debe tener un título y una descripción claros y concisos.
1. Su recurso debe seguir nuestras pautas de marca (consulte nuestro [Presskit](presskit))
1. Su recurso debería tener un beneficio significativo para los desarrolladores de Angular
1. Su recurso ya debería tener tracción y elogios de los desarrolladores de Angular

View File

@ -0,0 +1,245 @@
# The hero editor
The application now has a basic title.
Next you will create a new component to display hero information
and place that component in the application shell.
<div class="alert is-helpful">
For the sample app that this page describes, see the <live-example></live-example>.
</div>
## Create the heroes component
Using the Angular CLI, generate a new component named `heroes`.
<code-example language="sh" class="code-shell">
ng generate component heroes
</code-example>
The CLI creates a new folder, `src/app/heroes/`, and generates
the three files of the `HeroesComponent` along with a test file.
The `HeroesComponent` class file is as follows:
<code-example path="toh-pt1/src/app/heroes/heroes.component.ts" region="v1" header="app/heroes/heroes.component.ts (initial version)"></code-example>
You always import the `Component` symbol from the Angular core library
and annotate the component class with `@Component`.
`@Component` is a decorator function that specifies the Angular metadata for the component.
The CLI generated three metadata properties:
1. `selector`&mdash; the component's CSS element selector
1. `templateUrl`&mdash; the location of the component's template file.
1. `styleUrls`&mdash; the location of the component's private CSS styles.
{@a selector}
The [CSS element selector](https://developer.mozilla.org/en-US/docs/Web/CSS/Type_selectors),
`'app-heroes'`, matches the name of the HTML element that identifies this component within a parent component's template.
The `ngOnInit()` is a [lifecycle hook](guide/lifecycle-hooks#oninit).
Angular calls `ngOnInit()` shortly after creating a component.
It's a good place to put initialization logic.
Always `export` the component class so you can `import` it elsewhere ... like in the `AppModule`.
### Add a `hero` property
Add a `hero` property to the `HeroesComponent` for a hero named "Windstorm."
<code-example path="toh-pt1/src/app/heroes/heroes.component.ts" region="add-hero" header="heroes.component.ts (hero property)"></code-example>
### Show the hero
Open the `heroes.component.html` template file.
Delete the default text generated by the Angular CLI and
replace it with a data binding to the new `hero` property.
<code-example path="toh-pt1/src/app/heroes/heroes.component.1.html" header="heroes.component.html" region="show-hero-1"></code-example>
## Show the `HeroesComponent` view
To display the `HeroesComponent`, you must add it to the template of the shell `AppComponent`.
Remember that `app-heroes` is the [element selector](#selector) for the `HeroesComponent`.
So add an `<app-heroes>` element to the `AppComponent` template file, just below the title.
<code-example path="toh-pt1/src/app/app.component.html" header="src/app/app.component.html"></code-example>
Assuming that the CLI `ng serve` command is still running,
the browser should refresh and display both the application title and the hero name.
## Create a Hero interface
A real hero is more than a name.
Create a `Hero` interface in its own file in the `src/app` folder.
Give it `id` and `name` properties.
<code-example path="toh-pt1/src/app/hero.ts" header="src/app/hero.ts"></code-example>
Return to the `HeroesComponent` class and import the `Hero` interface.
Refactor the component's `hero` property to be of type `Hero`.
Initialize it with an `id` of `1` and the name `Windstorm`.
The revised `HeroesComponent` class file should look like this:
<code-example path="toh-pt1/src/app/heroes/heroes.component.ts" header="src/app/heroes/heroes.component.ts"></code-example>
The page no longer displays properly because you changed the hero from a string to an object.
## Show the hero object
Update the binding in the template to announce the hero's name
and show both `id` and `name` in a details layout like this:
<code-example path="toh-pt1/src/app/heroes/heroes.component.1.html" region="show-hero-2" header="heroes.component.html (HeroesComponent's template)"></code-example>
The browser refreshes and displays the hero's information.
## Format with the _UppercasePipe_
Modify the `hero.name` binding like this.
<code-example path="toh-pt1/src/app/heroes/heroes.component.html" header="src/app/heroes/heroes.component.html" region="pipe">
</code-example>
The browser refreshes and now the hero's name is displayed in capital letters.
The word `uppercase` in the interpolation binding,
right after the pipe operator ( | ),
activates the built-in `UppercasePipe`.
[Pipes](guide/pipes) are a good way to format strings, currency amounts, dates and other display data.
Angular ships with several built-in pipes and you can create your own.
## Edit the hero
Users should be able to edit the hero name in an `<input>` textbox.
The textbox should both _display_ the hero's `name` property
and _update_ that property as the user types.
That means data flows from the component class _out to the screen_ and
from the screen _back to the class_.
To automate that data flow, setup a two-way data binding between the `<input>` form element and the `hero.name` property.
### Two-way binding
Refactor the details area in the `HeroesComponent` template so it looks like this:
<code-example path="toh-pt1/src/app/heroes/heroes.component.1.html" region="name-input" header="src/app/heroes/heroes.component.html (HeroesComponent's template)"></code-example>
**[(ngModel)]** is Angular's two-way data binding syntax.
Here it binds the `hero.name` property to the HTML textbox so that data can flow _in both directions:_ from the `hero.name` property to the textbox, and from the textbox back to the `hero.name`.
### The missing _FormsModule_
Notice that the app stopped working when you added `[(ngModel)]`.
To see the error, open the browser development tools and look in the console
for a message like
<code-example language="sh" class="code-shell">
Template parse errors:
Can't bind to 'ngModel' since it isn't a known property of 'input'.
</code-example>
Although `ngModel` is a valid Angular directive, it isn't available by default.
It belongs to the optional `FormsModule` and you must _opt-in_ to using it.
## _AppModule_
Angular needs to know how the pieces of your application fit together
and what other files and libraries the app requires.
This information is called _metadata_.
Some of the metadata is in the `@Component` decorators that you added to your component classes.
Other critical metadata is in [`@NgModule`](guide/ngmodules) decorators.
The most important `@NgModule` decorator annotates the top-level **AppModule** class.
The Angular CLI generated an `AppModule` class in `src/app/app.module.ts` when it created the project.
This is where you _opt-in_ to the `FormsModule`.
### Import _FormsModule_
Open `AppModule` (`app.module.ts`) and import the `FormsModule` symbol from the `@angular/forms` library.
<code-example path="toh-pt1/src/app/app.module.ts" header="app.module.ts (FormsModule symbol import)"
region="formsmodule-js-import">
</code-example>
Then add `FormsModule` to the `@NgModule` metadata's `imports` array, which contains a list of external modules that the app needs.
<code-example path="toh-pt1/src/app/app.module.ts" header="app.module.ts (@NgModule imports)"
region="ng-imports">
</code-example>
When the browser refreshes, the app should work again. You can edit the hero's name and see the changes reflected immediately in the `<h2>` above the textbox.
### Declare `HeroesComponent`
Every component must be declared in _exactly one_ [NgModule](guide/ngmodules).
_You_ didn't declare the `HeroesComponent`.
So why did the application work?
It worked because the Angular CLI declared `HeroesComponent` in the `AppModule` when it generated that component.
Open `src/app/app.module.ts` and find `HeroesComponent` imported near the top.
<code-example path="toh-pt1/src/app/app.module.ts" header="src/app/app.module.ts" region="heroes-import" >
</code-example>
The `HeroesComponent` is declared in the `@NgModule.declarations` array.
<code-example path="toh-pt1/src/app/app.module.ts" header="src/app/app.module.ts" region="declarations">
</code-example>
Note that `AppModule` declares both application components, `AppComponent` and `HeroesComponent`.
## Final code review
Here are the code files discussed on this page.
<code-tabs>
<code-pane header="src/app/heroes/heroes.component.ts" path="toh-pt1/src/app/heroes/heroes.component.ts">
</code-pane>
<code-pane header="src/app/heroes/heroes.component.html" path="toh-pt1/src/app/heroes/heroes.component.html">
</code-pane>
<code-pane header="src/app/app.module.ts"
path="toh-pt1/src/app/app.module.ts">
</code-pane>
<code-pane header="src/app/app.component.ts" path="toh-pt1/src/app/app.component.ts">
</code-pane>
<code-pane header="src/app/app.component.html" path="toh-pt1/src/app/app.component.html">
</code-pane>
<code-pane header="src/app/hero.ts"
path="toh-pt1/src/app/hero.ts">
</code-pane>
</code-tabs>
## Summary
* You used the CLI to create a second `HeroesComponent`.
* You displayed the `HeroesComponent` by adding it to the `AppComponent` shell.
* You applied the `UppercasePipe` to format the name.
* You used two-way data binding with the `ngModel` directive.
* You learned about the `AppModule`.
* You imported the `FormsModule` in the `AppModule` so that Angular would recognize and apply the `ngModel` directive.
* You learned the importance of declaring components in the `AppModule`
and appreciated that the CLI declared it for you.

View File

@ -1,213 +1,213 @@
# The hero editor
# El editor de Héroe
The application now has a basic title.
Next you will create a new component to display hero information
and place that component in the application shell.
Se ha agregado un título básico a la aplicación.
Luego crea un nuevo componente para mostrar la información del héroe,
Coloca el componente en el (app shell) de la aplicación.
<div class="alert is-helpful">
For the sample app that this page describes, see the <live-example></live-example>.
Para ver la aplicación de ejemplo que describe esta página, consulta el <live-example></live-example>.
</div>
## Create the heroes component
## Crear un componente de héroes
Using the Angular CLI, generate a new component named `heroes`.
Usa la CLI angular para generar un nuevo componente llamado `heroes`.
<code-example language="sh" class="code-shell">
ng generate component heroes
</code-example>
The CLI creates a new folder, `src/app/heroes/`, and generates
the three files of the `HeroesComponent` along with a test file.
CLI crea una nueva carpeta llamada `src/app/heroes/`, y
genera tres archivos sobre `HeroesComponent` junto un archivo de prueba.
The `HeroesComponent` class file is as follows:
El archivo de la clase `HeroesComponent` es el siguiente.
<code-example path="toh-pt1/src/app/heroes/heroes.component.ts" region="v1" header="app/heroes/heroes.component.ts (initial version)"></code-example>
You always import the `Component` symbol from the Angular core library
and annotate the component class with `@Component`.
Siempre Importa el símbolo `Component` de la biblioteca pricipal de Angular, y realiza la anotación a la clase del component con `@Component`.
`@Component` is a decorator function that specifies the Angular metadata for the component.
`@Component` es una decoradoro que especifica metadatos Angular para un
componente.
The CLI generated three metadata properties:
La CLI generó 3 propiedades de metadatos:
1. `selector`&mdash; the component's CSS element selector
1. `templateUrl`&mdash; the location of the component's template file.
1. `styleUrls`&mdash; the location of the component's private CSS styles.
1. `selector`&mdash; El selector de elementos CSS para el componente
1. `templateUrl`&mdash; La ubicación del archivo plantilla para el componente
1. `styleUrls`&mdash; La ubicación de los estilos CSS privados del componente.
{@a selector}
The [CSS element selector](https://developer.mozilla.org/en-US/docs/Web/CSS/Type_selectors),
`'app-heroes'`, matches the name of the HTML element that identifies this component within a parent component's template.
El [Selector de elementos CSS](https://developer.mozilla.org/en-US/docs/Web/CSS/Type_selectors)
`'app-heroes'`, coincide con el nombre del elemento HTML que identifica este componente en el componente padre Plantillas.
The `ngOnInit()` is a [lifecycle hook](guide/lifecycle-hooks#oninit).
Angular calls `ngOnInit()` shortly after creating a component.
It's a good place to put initialization logic.
El `ngOnInit()` es un [gancho de ciclo de vida](guide/lifecycle-hooks#oninit) ("lifecycle hook") . Angular llama a `ngOnInit()` inmediatamente después de crear el componente.
Adecuado para poner la lógica de inicialización.
Always `export` the component class so you can `import` it elsewhere ... like in the `AppModule`.
Siempre `exporta` la clase de componente, por lo que siempre puede `importarla` en otro lugar, como un `AppModule`.
### Add a `hero` property
### Agrega la propiedad `hero`
Add a `hero` property to the `HeroesComponent` for a hero named "Windstorm."
Agrega una propiedad `hero` al `HeroesComponent` para un héroe llamado "Windstorm".
<code-example path="toh-pt1/src/app/heroes/heroes.component.ts" region="add-hero" header="heroes.component.ts (hero property)"></code-example>
### Show the hero
### Mostrar el héroe
Open the `heroes.component.html` template file.
Delete the default text generated by the Angular CLI and
replace it with a data binding to the new `hero` property.
Abre el archivo de plantilla `heroes.component.html`.
Elimina el texto predeterminado generado por CLI angular,
Reemplaza con un enlace de datos a la nueva propiedad `hero`.
<code-example path="toh-pt1/src/app/heroes/heroes.component.1.html" header="heroes.component.html" region="show-hero-1"></code-example>
## Show the `HeroesComponent` view
## Mostrar la vista `HeroesComponent`
To display the `HeroesComponent`, you must add it to the template of the shell `AppComponent`.
Para ver el `HeroesComponent`, debe agregarlo a las Plantillas en el `AppComponent` del shell de tu aplicación.
Remember that `app-heroes` is the [element selector](#selector) for the `HeroesComponent`.
So add an `<app-heroes>` element to the `AppComponent` template file, just below the title.
Recuerda que `app-heroes` es el [selector de elemento](#selector) del `HeroesComponent`.
Entonces, en el archivo Plantillas de `AppComponent`, agrega el elemento `<app-heroes>` directamente debajo del título.
<code-example path="toh-pt1/src/app/app.component.html" header="src/app/app.component.html"></code-example>
Assuming that the CLI `ng serve` command is still running,
the browser should refresh and display both the application title and the hero name.
Si el comando CLI `ng serve` todavía se está ejecutando,
El navegador se actualiza para mostrar el título de la aplicación y el nombre del héroe.
## Create a Hero interface
## Crear interfaz de héroe
A real hero is more than a name.
Un héroe es más que un nombre.
Create a `Hero` interface in its own file in the `src/app` folder.
Give it `id` and `name` properties.
Crea una interfaz `Hero` en su propio archivo en la carpeta `src/app`.
Dale una propiedad `id` y una propiedad `name`.
<code-example path="toh-pt1/src/app/hero.ts" header="src/app/hero.ts"></code-example>
Return to the `HeroesComponent` class and import the `Hero` interface.
Regresa a la clase `HeroesComponent` e importe la interfaz `Hero`.
Refactor the component's `hero` property to be of type `Hero`.
Initialize it with an `id` of `1` and the name `Windstorm`.
Refactoriza la propiedad de héroe del componente para que sea del tipo 'Héroe'.
Inicialízalo con un `id` de `1` y un nombre de `Windstorm`.
The revised `HeroesComponent` class file should look like this:
El archivo de clase revisado `HeroesComponent` se ve así:
<code-example path="toh-pt1/src/app/heroes/heroes.component.ts" header="src/app/heroes/heroes.component.ts"></code-example>
The page no longer displays properly because you changed the hero from a string to an object.
Cambió el héroe de texto a un objeto, lo que provocó que la página se mostrara incorrectamente.
## Show the hero object
## Mostrar objeto de héroe
Update the binding in the template to announce the hero's name
and show both `id` and `name` in a details layout like this:
Actualiza los enlaces de Plantillas para anunciar el nombre del héroe,
Muestra tanto el `id` como el `name` con un diseño detallado como este:
<code-example path="toh-pt1/src/app/heroes/heroes.component.1.html" region="show-hero-2" header="heroes.component.html (HeroesComponent's template)"></code-example>
The browser refreshes and displays the hero's information.
El navegador se actualiza para mostrar la información del héroe.
## Format with the _UppercasePipe_
## Formatea con _UppercasePipe_
Modify the `hero.name` binding like this.
Modifica el enlace para `hero.name` de esta manera:
<code-example path="toh-pt1/src/app/heroes/heroes.component.html" header="src/app/heroes/heroes.component.html" region="pipe">
</code-example>
The browser refreshes and now the hero's name is displayed in capital letters.
El navegador se actualizará para mostrar el nombre del héroe en mayúsculas.
The word `uppercase` in the interpolation binding,
right after the pipe operator ( | ),
activates the built-in `UppercasePipe`.
En el enlace de interpolación, la palabra `mayúscula` inmediatamente después del operador pipe (|) es
Inicie el 'UppercasePipe' incorporado.
[Pipes](guide/pipes) are a good way to format strings, currency amounts, dates and other display data.
Angular ships with several built-in pipes and you can create your own.
[pipe](guide/pipes) ("pipe") Es adecuado para formatear cadenas, importes monetarios, fechas y otros datos de visualización.
Angular viene con múltiples pipes incorporadas, y puede crear las suyas propias.
## Edit the hero
## Editar el héroe
Users should be able to edit the hero name in an `<input>` textbox.
El usuario debe poder editar el nombre del héroe en el cuadro de texto `<input>`.
The textbox should both _display_ the hero's `name` property
and _update_ that property as the user types.
That means data flows from the component class _out to the screen_ and
from the screen _back to the class_.
En el cuadro de texto, la propiedad `name` del héroe se muestra _,
La propiedad se actualiza según los tipos de usuario.
Esto es de la clase de componente a _screen_,
Y significa el flujo de datos desde la pantalla a la clase de componente.
To automate that data flow, setup a two-way data binding between the `<input>` form element and the `hero.name` property.
Para automatizar ese flujo de datos, configure un enlace de datos bidireccional entre el elemento de formulario `<input>` y la propiedad `hero.name`.
### Two-way binding
### Enlace de datos bidireccional
Refactor the details area in the `HeroesComponent` template so it looks like this:
Refactorizando el área de detalle de las Plantas `HeroesComponent` se ve así:
<code-example path="toh-pt1/src/app/heroes/heroes.component.1.html" region="name-input" header="src/app/heroes/heroes.component.html (HeroesComponent's template)"></code-example>
**[(ngModel)]** is Angular's two-way data binding syntax.
**[(ngModel)]** Es la sintaxis de enlace de datos bidireccional de Angular.
Here it binds the `hero.name` property to the HTML textbox so that data can flow _in both directions:_ from the `hero.name` property to the textbox, and from the textbox back to the `hero.name`.
Esto vinculará la propiedad `hero.name` al cuadro de texto HTML, por lo que
Puede pasar datos _en ambas direcciones_ desde la propiedad `hero.name` al cuadro de texto y desde el cuadro de texto a la propiedad `hero.name`.
### The missing _FormsModule_
### _FormsModule_ No encontrado
Notice that the app stopped working when you added `[(ngModel)]`.
Observa que la aplicación dejó de funcionar cuando agregué el `[(ngModel)]`.
To see the error, open the browser development tools and look in the console
for a message like
Para ver el error, abre las herramientas de desarrollo de su navegador,
Busca mensajes como el siguiente en la consola,
<code-example language="sh" class="code-shell">
Template parse errors:
Can't bind to 'ngModel' since it isn't a known property of 'input'.
Errores de análisis de plantilla:
No se puede vincular a 'nGModelo' ya que no es una propiedad conocida de 'entrada'.
</code-example>
Although `ngModel` is a valid Angular directive, it isn't available by default.
`ngModel` Es una directiva angular válida pero no está disponible por defecto.
It belongs to the optional `FormsModule` and you must _opt-in_ to using it.
Pertenece al `FormsModule` opcional y debe optar por ese módulo para usarlo.
## _AppModule_
Angular needs to know how the pieces of your application fit together
and what other files and libraries the app requires.
This information is called _metadata_.
En Angular, cómo encajan las partes de la aplicación,
Necesita saber qué otros archivos y bibliotecas necesita su aplicación.
Esta información se llama _metadata_.
Some of the metadata is in the `@Component` decorators that you added to your component classes.
Other critical metadata is in [`@NgModule`](guide/ngmodules) decorators.
Algunos de los metadatos se encuentran en el decorador `@Component` que agregó a su clase de componentes.
Otros metadatos importantes son[`@NgModule`](guide/ngmodules)Está en el decorador.
The most important `@NgModule` decorator annotates the top-level **AppModule** class.
El decorador más importante `@NgModule` anota la clase **AppModule** de nivel superior.
The Angular CLI generated an `AppModule` class in `src/app/app.module.ts` when it created the project.
This is where you _opt-in_ to the `FormsModule`.
Angular CLI creó la clase `AppModule` en `src/app/app.module.ts` al crear el proyecto.
Ahora opta por el `FormsModule`.
### Import _FormsModule_
### Importar _FormsModule_
Open `AppModule` (`app.module.ts`) and import the `FormsModule` symbol from the `@angular/forms` library.
Abre `AppModule` (`app.module.ts`) e importe el símbolo `FormsModule` desde la biblioteca `@angular/forms`.
<code-example path="toh-pt1/src/app/app.module.ts" header="app.module.ts (FormsModule symbol import)"
<code-example path="toh-pt1/src/app/app.module.ts" header="app.module.ts (@NgModule imports)"
region="formsmodule-js-import">
</code-example>
Then add `FormsModule` to the `@NgModule` metadata's `imports` array, which contains a list of external modules that the app needs.
A continuación, agregue el `FormsModule` a el arreglo `imports` de los metadatos `@ NgModule`.
Esta matriz contiene una lista de módulos externos que requiere su aplicación.
<code-example path="toh-pt1/src/app/app.module.ts" header="app.module.ts (@NgModule imports)"
<code-example path="toh-pt1/src/app/app.module.ts" header="app.module.ts ( @NgModule imports)"
region="ng-imports">
</code-example>
When the browser refreshes, the app should work again. You can edit the hero's name and see the changes reflected immediately in the `<h2>` above the textbox.
La aplicación debería funcionar nuevamente cuando se actualice el navegador. Puedes editar el nombre del héroe y ver los cambios reflejados inmediatamente en el `<h2>` arriba del cuadro de texto.
### Declare `HeroesComponent`
### Declarar `HeroesComponent`
Every component must be declared in _exactly one_ [NgModule](guide/ngmodules).
Todos los componentes deben declararse con _exactamente uno_ [NgModule](guide/ngmodules).
_You_ didn't declare the `HeroesComponent`.
So why did the application work?
_No has declarado_ HeroesComponent`.
Entonces, ¿por qué funcionó la aplicación?
It worked because the Angular CLI declared `HeroesComponent` in the `AppModule` when it generated that component.
La aplicación funcionó porque Angular CLI declaró el componente en el `AppModule` cuando generó el `HeroesComponent`.
Abra `src/app/app.module.ts` y encuentre el `HeroesComponent` importado cerca de la parte superior.
Open `src/app/app.module.ts` and find `HeroesComponent` imported near the top.
<code-example path="toh-pt1/src/app/app.module.ts" header="src/app/app.module.ts" region="heroes-import" >
</code-example>
The `HeroesComponent` is declared in the `@NgModule.declarations` array.
`HeroesComponent` se declara en la matriz`@NgModule.declarations`.
<code-example path="toh-pt1/src/app/app.module.ts" header="src/app/app.module.ts" region="declarations">
</code-example>
Note that `AppModule` declares both application components, `AppComponent` and `HeroesComponent`.
`AppModule` declara los componentes de aplicación `AppComponent` y `HeroesComponent`.
## Revisión del código final
## Final code review
Here are the code files discussed on this page.
Los archivos de código descritos en esta página son:
<code-tabs>
@ -217,7 +217,7 @@ Here are the code files discussed on this page.
<code-pane header="src/app/heroes/heroes.component.html" path="toh-pt1/src/app/heroes/heroes.component.html">
</code-pane>
<code-pane header="src/app/app.module.ts"
<code-pane header="src/app/app.module.ts"
path="toh-pt1/src/app/app.module.ts">
</code-pane>
@ -232,14 +232,12 @@ Here are the code files discussed on this page.
</code-pane>
</code-tabs>
## Resumen
## Summary
* You used the CLI to create a second `HeroesComponent`.
* You displayed the `HeroesComponent` by adding it to the `AppComponent` shell.
* You applied the `UppercasePipe` to format the name.
* You used two-way data binding with the `ngModel` directive.
* You learned about the `AppModule`.
* You imported the `FormsModule` in the `AppModule` so that Angular would recognize and apply the `ngModel` directive.
* You learned the importance of declaring components in the `AppModule`
and appreciated that the CLI declared it for you.
* Creo un segundo `HeroesComponent` usando el CLI.
* Agregó `HeroesComponent` al shell de `AppComponent` y lo mostró.
* Aplico 'UppercasePipe' para formatear el nombre.
* Utilizo el enlace de datos bidireccional en la directiva `ngModel`.
* Aprendío sobre `AppModule`.
* Importó `FormsModule` en `AppModule` para reconocer y aplicar la directiva Angular `ngModel`.
* Aprendío la importancia de declarar un componente en un `AppModule` y me di cuenta de que la CLI está haciendo esa declaración por usted.

View File

@ -59,7 +59,7 @@ Abre el archivo de clase `HeroDetailComponent` e importe el símbolo `Hero`.
region="import-hero" header="src/app/hero-detail/hero-detail.component.ts (import Hero)">
</code-example>
La propiedad `hero` debe ser una [_propiedad de entrada_](guide/template-syntax#inputs-outputs " Input and Output properties"), anotada con el decorador `@Input()` porque el `HeroesComponent` _externo_ [se vinculará de esta manera.](#heroes-component-template)
La propiedad `hero` debe ser una [_propiedad de entrada_](guide/inputs-outputs "Input and Output properties") -->, anotada con el decorador `@Input()` porque el `HeroesComponent` _externo_ [se vinculará de esta manera.](#heroes-component-template)
<code-example path="toh-pt3/src/app/heroes/heroes.component.html" region="hero-detail-binding">
</code-example>
@ -102,7 +102,7 @@ Vincula `HeroesComponent.selectedHero` a la propiedad `hero` de este elemento de
</code-example>
`[hero]="selectedHero"` es el [enlace de propiedad](guide/template-syntax#property-binding). de Angular
`[hero]="selectedHero"` es el [enlace de propiedad](guide/property-binding) de Angular.
Este es un enlace de datos unidireccional de la propiedad `selectedHero` `HeroesComponent` a la propiedad `hero` del elemento objetivo.
Aquí se asigna la propiedad `hero` de `HeroDetailComponent`.
@ -156,6 +156,6 @@ Los archivos de código descritos en esta página son:
* Creaste un `HeroDetailComponent` independiente y reutilizable.
* Usaste el [enlace de propiedad](guide/template-syntax#property-binding) para que el padre `HeroesComponent` pueda controlar al hijo `HeroDetailComponent`.
* Usaste el [enlace de propiedad](guide/property-binding) para que el padre `HeroesComponent` pueda controlar al hijo `HeroDetailComponent`.
* Usaste el [`decorador @Input`](guide/template-syntax#inputs-outputs) para hacer que la propiedad del heroe esté disponible para ser vinculada por el componente `HeroesComponent` externamente.
* Usaste el [`decorador @Input`](guide/inputs-outputs) para hacer que la propiedad del héroe esté disponible para ser vinculada por el componente `HeroesComponent` externamente.

View File

@ -0,0 +1,573 @@
# Add in-app navigation with routing
There are new requirements for the Tour of Heroes app:
* Add a *Dashboard* view.
* Add the ability to navigate between the *Heroes* and *Dashboard* views.
* When users click a hero name in either view, navigate to a detail view of the selected hero.
* When users click a *deep link* in an email, open the detail view for a particular hero.
<div class="alert is-helpful">
For the sample app that this page describes, see the <live-example></live-example>.
</div>
When youre done, users will be able to navigate the app like this:
<div class="lightbox">
<img src='generated/images/guide/toh/nav-diagram.png' alt="View navigations">
</div>
## Add the `AppRoutingModule`
In Angular, the best practice is to load and configure the router in a separate, top-level module
that is dedicated to routing and imported by the root `AppModule`.
By convention, the module class name is `AppRoutingModule` and it belongs in the `app-routing.module.ts` in the `src/app` folder.
Use the CLI to generate it.
<code-example language="sh" class="code-shell">
ng generate module app-routing --flat --module=app
</code-example>
<div class="alert is-helpful">
`--flat` puts the file in `src/app` instead of its own folder.<br>
`--module=app` tells the CLI to register it in the `imports` array of the `AppModule`.
</div>
The generated file looks like this:
<code-example path="toh-pt5/src/app/app-routing.module.0.ts" header="src/app/app-routing.module.ts (generated)">
</code-example>
Replace it with the following:
<code-example path="toh-pt5/src/app/app-routing.module.1.ts" header="src/app/app-routing.module.ts (updated)">
</code-example>
First, `AppRoutingModule` imports `RouterModule` and `Routes` so the app can have routing functionality. The next import, `HeroesComponent`, will give the Router somewhere to go once you configure the routes.
Notice that the `CommonModule` references and `declarations` array are unnecessary, so are no
longer part of `AppRoutingModule`. The following sections explain the rest of the `AppRoutingModule` in more detail.
### Routes
The next part of the file is where you configure your routes.
*Routes* tell the Router which view to display when a user clicks a link or
pastes a URL into the browser address bar.
Since `AppRoutingModule` already imports `HeroesComponent`, you can use it in the `routes` array:
<code-example path="toh-pt5/src/app/app-routing.module.ts" header="src/app/app-routing.module.ts"
region="heroes-route">
</code-example>
A typical Angular `Route` has two properties:
* `path`: a string that matches the URL in the browser address bar.
* `component`: the component that the router should create when navigating to this route.
This tells the router to match that URL to `path: 'heroes'`
and display the `HeroesComponent` when the URL is something like `localhost:4200/heroes`.
### `RouterModule.forRoot()`
The `@NgModule` metadata initializes the router and starts it listening for browser location changes.
The following line adds the `RouterModule` to the `AppRoutingModule` `imports` array and
configures it with the `routes` in one step by calling
`RouterModule.forRoot()`:
<code-example path="toh-pt5/src/app/app-routing.module.ts" header="src/app/app-routing.module.ts" region="ngmodule-imports">
</code-example>
<div class="alert is-helpful">
The method is called `forRoot()` because you configure the router at the application's root level.
The `forRoot()` method supplies the service providers and directives needed for routing,
and performs the initial navigation based on the current browser URL.
</div>
Next, `AppRoutingModule` exports `RouterModule` so it will be available throughout the app.
<code-example path="toh-pt5/src/app/app-routing.module.ts" header="src/app/app-routing.module.ts (exports array)" region="export-routermodule">
</code-example>
## Add `RouterOutlet`
Open the `AppComponent` template and replace the `<app-heroes>` element with a `<router-outlet>` element.
<code-example path="toh-pt5/src/app/app.component.html" region="outlet" header="src/app/app.component.html (router-outlet)">
</code-example>
The `AppComponent` template no longer needs `<app-heroes>` because the app will only display the `HeroesComponent` when the user navigates to it.
The `<router-outlet>` tells the router where to display routed views.
<div class="alert is-helpful">
The `RouterOutlet` is one of the router directives that became available to the `AppComponent`
because `AppModule` imports `AppRoutingModule` which exported `RouterModule`. The `ng generate` command you ran at the start of this tutorial added this import because of the `--module=app` flag. If you manually created `app-routing.module.ts` or used a tool other than the CLI to do so, you'll need to import `AppRoutingModule` into `app.module.ts` and add it to the `imports` array of the `NgModule`.
</div>
#### Try it
You should still be running with this CLI command.
<code-example language="sh" class="code-shell">
ng serve
</code-example>
The browser should refresh and display the app title but not the list of heroes.
Look at the browser's address bar.
The URL ends in `/`.
The route path to `HeroesComponent` is `/heroes`.
Append `/heroes` to the URL in the browser address bar.
You should see the familiar heroes master/detail view.
{@a routerlink}
## Add a navigation link (`routerLink`)
Ideally, users should be able to click a link to navigate rather
than pasting a route URL into the address bar.
Add a `<nav>` element and, within that, an anchor element that, when clicked,
triggers navigation to the `HeroesComponent`.
The revised `AppComponent` template looks like this:
<code-example path="toh-pt5/src/app/app.component.html" region="heroes" header="src/app/app.component.html (heroes RouterLink)">
</code-example>
A [`routerLink` attribute](#routerlink) is set to `"/heroes"`,
the string that the router matches to the route to `HeroesComponent`.
The `routerLink` is the selector for the [`RouterLink` directive](/api/router/RouterLink)
that turns user clicks into router navigations.
It's another of the public directives in the `RouterModule`.
The browser refreshes and displays the app title and heroes link,
but not the heroes list.
Click the link.
The address bar updates to `/heroes` and the list of heroes appears.
<div class="alert is-helpful">
Make this and future navigation links look better by adding private CSS styles to `app.component.css`
as listed in the [final code review](#appcomponent) below.
</div>
## Add a dashboard view
Routing makes more sense when there are multiple views.
So far there's only the heroes view.
Add a `DashboardComponent` using the CLI:
<code-example language="sh" class="code-shell">
ng generate component dashboard
</code-example>
The CLI generates the files for the `DashboardComponent` and declares it in `AppModule`.
Replace the default file content in these three files as follows:
<code-tabs>
<code-pane
header="src/app/dashboard/dashboard.component.html" path="toh-pt5/src/app/dashboard/dashboard.component.1.html">
</code-pane>
<code-pane
header="src/app/dashboard/dashboard.component.ts" path="toh-pt5/src/app/dashboard/dashboard.component.ts">
</code-pane>
<code-pane
header="src/app/dashboard/dashboard.component.css" path="toh-pt5/src/app/dashboard/dashboard.component.css">
</code-pane>
</code-tabs>
The _template_ presents a grid of hero name links.
* The `*ngFor` repeater creates as many links as are in the component's `heroes` array.
* The links are styled as colored blocks by the `dashboard.component.css`.
* The links don't go anywhere yet but [they will shortly](#hero-details).
The _class_ is similar to the `HeroesComponent` class.
* It defines a `heroes` array property.
* The constructor expects Angular to inject the `HeroService` into a private `heroService` property.
* The `ngOnInit()` lifecycle hook calls `getHeroes()`.
This `getHeroes()` returns the sliced list of heroes at positions 1 and 5, returning only four of the Top Heroes (2nd, 3rd, 4th, and 5th).
<code-example path="toh-pt5/src/app/dashboard/dashboard.component.ts" header="src/app/dashboard/dashboard.component.ts" region="getHeroes">
</code-example>
### Add the dashboard route
To navigate to the dashboard, the router needs an appropriate route.
Import the `DashboardComponent` in the `AppRoutingModule`.
<code-example path="toh-pt5/src/app/app-routing.module.ts" region="import-dashboard" header="src/app/app-routing.module.ts (import DashboardComponent)">
</code-example>
Add a route to the `AppRoutingModule.routes` array that matches a path to the `DashboardComponent`.
<code-example path="toh-pt5/src/app/app-routing.module.ts" header="src/app/app-routing.module.ts" region="dashboard-route">
</code-example>
### Add a default route
When the app starts, the browser's address bar points to the web site's root.
That doesn't match any existing route so the router doesn't navigate anywhere.
The space below the `<router-outlet>` is blank.
To make the app navigate to the dashboard automatically, add the following
route to the `AppRoutingModule.Routes` array.
<code-example path="toh-pt5/src/app/app-routing.module.ts" header="src/app/app-routing.module.ts" region="redirect-route">
</code-example>
This route redirects a URL that fully matches the empty path to the route whose path is `'/dashboard'`.
After the browser refreshes, the router loads the `DashboardComponent`
and the browser address bar shows the `/dashboard` URL.
### Add dashboard link to the shell
The user should be able to navigate back and forth between the
`DashboardComponent` and the `HeroesComponent` by clicking links in the
navigation area near the top of the page.
Add a dashboard navigation link to the `AppComponent` shell template, just above the *Heroes* link.
<code-example path="toh-pt5/src/app/app.component.html" header="src/app/app.component.html">
</code-example>
After the browser refreshes you can navigate freely between the two views by clicking the links.
{@a hero-details}
## Navigating to hero details
The `HeroDetailsComponent` displays details of a selected hero.
At the moment the `HeroDetailsComponent` is only visible at the bottom of the `HeroesComponent`
The user should be able to get to these details in three ways.
1. By clicking a hero in the dashboard.
1. By clicking a hero in the heroes list.
1. By pasting a "deep link" URL into the browser address bar that identifies the hero to display.
In this section, you'll enable navigation to the `HeroDetailsComponent`
and liberate it from the `HeroesComponent`.
### Delete _hero details_ from `HeroesComponent`
When the user clicks a hero item in the `HeroesComponent`,
the app should navigate to the `HeroDetailComponent`,
replacing the heroes list view with the hero detail view.
The heroes list view should no longer show hero details as it does now.
Open the `HeroesComponent` template (`heroes/heroes.component.html`) and
delete the `<app-hero-detail>` element from the bottom.
Clicking a hero item now does nothing.
You'll [fix that shortly](#heroes-component-links) after you enable routing to the `HeroDetailComponent`.
### Add a _hero detail_ route
A URL like `~/detail/11` would be a good URL for navigating to the *Hero Detail* view of the hero whose `id` is `11`.
Open `AppRoutingModule` and import `HeroDetailComponent`.
<code-example path="toh-pt5/src/app/app-routing.module.ts" region="import-herodetail" header="src/app/app-routing.module.ts (import HeroDetailComponent)">
</code-example>
Then add a _parameterized_ route to the `AppRoutingModule.routes` array that matches the path pattern to the _hero detail_ view.
<code-example path="toh-pt5/src/app/app-routing.module.ts" header="src/app/app-routing.module.ts" region="detail-route">
</code-example>
The colon (:) in the `path` indicates that `:id` is a placeholder for a specific hero `id`.
At this point, all application routes are in place.
<code-example path="toh-pt5/src/app/app-routing.module.ts" region="routes" header="src/app/app-routing.module.ts (all routes)">
</code-example>
### `DashboardComponent` hero links
The `DashboardComponent` hero links do nothing at the moment.
Now that the router has a route to `HeroDetailComponent`,
fix the dashboard hero links to navigate via the _parameterized_ dashboard route.
<code-example
path="toh-pt5/src/app/dashboard/dashboard.component.html"
region="click"
header="src/app/dashboard/dashboard.component.html (hero links)">
</code-example>
You're using Angular [interpolation binding](guide/interpolation) within the `*ngFor` repeater
to insert the current iteration's `hero.id` into each
[`routerLink`](#routerlink).
{@a heroes-component-links}
### `HeroesComponent` hero links
The hero items in the `HeroesComponent` are `<li>` elements whose click events
are bound to the component's `onSelect()` method.
<code-example path="toh-pt4/src/app/heroes/heroes.component.html" region="list" header="src/app/heroes/heroes.component.html (list with onSelect)">
</code-example>
Strip the `<li>` back to just its `*ngFor`,
wrap the badge and name in an anchor element (`<a>`),
and add a `routerLink` attribute to the anchor that
is the same as in the dashboard template
<code-example path="toh-pt5/src/app/heroes/heroes.component.html" region="list" header="src/app/heroes/heroes.component.html (list with links)">
</code-example>
You'll have to fix the private stylesheet (`heroes.component.css`) to make
the list look as it did before.
Revised styles are in the [final code review](#heroescomponent) at the bottom of this guide.
#### Remove dead code (optional)
While the `HeroesComponent` class still works,
the `onSelect()` method and `selectedHero` property are no longer used.
It's nice to tidy up and you'll be grateful to yourself later.
Here's the class after pruning away the dead code.
<code-example path="toh-pt5/src/app/heroes/heroes.component.ts" region="class" header="src/app/heroes/heroes.component.ts (cleaned up)">
</code-example>
## Routable `HeroDetailComponent`
Previously, the parent `HeroesComponent` set the `HeroDetailComponent.hero`
property and the `HeroDetailComponent` displayed the hero.
`HeroesComponent` doesn't do that anymore.
Now the router creates the `HeroDetailComponent` in response to a URL such as `~/detail/11`.
The `HeroDetailComponent` needs a new way to obtain the hero-to-display.
This section explains the following:
* Get the route that created it
* Extract the `id` from the route
* Acquire the hero with that `id` from the server via the `HeroService`
Add the following imports:
<code-example path="toh-pt5/src/app/hero-detail/hero-detail.component.ts" region="added-imports" header="src/app/hero-detail/hero-detail.component.ts">
</code-example>
{@a hero-detail-ctor}
Inject the `ActivatedRoute`, `HeroService`, and `Location` services
into the constructor, saving their values in private fields:
<code-example path="toh-pt5/src/app/hero-detail/hero-detail.component.ts" header="src/app/hero-detail/hero-detail.component.ts" region="ctor">
</code-example>
The [`ActivatedRoute`](api/router/ActivatedRoute) holds information about the route to this instance of the `HeroDetailComponent`.
This component is interested in the route's parameters extracted from the URL.
The "id" parameter is the `id` of the hero to display.
The [`HeroService`](tutorial/toh-pt4) gets hero data from the remote server
and this component will use it to get the hero-to-display.
The [`location`](api/common/Location) is an Angular service for interacting with the browser.
You'll use it [later](#goback) to navigate back to the view that navigated here.
### Extract the `id` route parameter
In the `ngOnInit()` [lifecycle hook](guide/lifecycle-hooks#oninit)
call `getHero()` and define it as follows.
<code-example path="toh-pt5/src/app/hero-detail/hero-detail.component.ts" header="src/app/hero-detail/hero-detail.component.ts" region="ngOnInit">
</code-example>
The `route.snapshot` is a static image of the route information shortly after the component was created.
The `paramMap` is a dictionary of route parameter values extracted from the URL.
The `"id"` key returns the `id` of the hero to fetch.
Route parameters are always strings.
The JavaScript (+) operator converts the string to a number,
which is what a hero `id` should be.
The browser refreshes and the app crashes with a compiler error.
`HeroService` doesn't have a `getHero()` method.
Add it now.
### Add `HeroService.getHero()`
Open `HeroService` and add the following `getHero()` method with the `id` after the `getHeroes()` method:
<code-example path="toh-pt5/src/app/hero.service.ts" region="getHero" header="src/app/hero.service.ts (getHero)">
</code-example>
<div class="alert is-important">
Note the backticks ( &#96; ) that define a JavaScript
[_template literal_](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) for embedding the `id`.
</div>
Like [`getHeroes()`](tutorial/toh-pt4#observable-heroservice),
`getHero()` has an asynchronous signature.
It returns a _mock hero_ as an `Observable`, using the RxJS `of()` function.
You'll be able to re-implement `getHero()` as a real `Http` request
without having to change the `HeroDetailComponent` that calls it.
#### Try it
The browser refreshes and the app is working again.
You can click a hero in the dashboard or in the heroes list and navigate to that hero's detail view.
If you paste `localhost:4200/detail/11` in the browser address bar,
the router navigates to the detail view for the hero with `id: 11`, "Dr Nice".
{@a goback}
### Find the way back
By clicking the browser's back button,
you can go back to the hero list or dashboard view,
depending upon which sent you to the detail view.
It would be nice to have a button on the `HeroDetail` view that can do that.
Add a *go back* button to the bottom of the component template and bind it
to the component's `goBack()` method.
<code-example path="toh-pt5/src/app/hero-detail/hero-detail.component.html" region="back-button" header="src/app/hero-detail/hero-detail.component.html (back button)">
</code-example>
Add a `goBack()` _method_ to the component class that navigates backward one step
in the browser's history stack
using the `Location` service that you [injected previously](#hero-detail-ctor).
<code-example path="toh-pt5/src/app/hero-detail/hero-detail.component.ts" region="goBack" header="src/app/hero-detail/hero-detail.component.ts (goBack)">
</code-example>
Refresh the browser and start clicking.
Users can navigate around the app, from the dashboard to hero details and back,
from heroes list to the mini detail to the hero details and back to the heroes again.
## Final code review
Here are the code files discussed on this page.
{@a approutingmodule}
{@a appmodule}
#### `AppRoutingModule`, `AppModule`, and `HeroService`
<code-tabs>
<code-pane
header="src/app/app-routing.module.ts"
path="toh-pt5/src/app/app-routing.module.ts">
</code-pane>
<code-pane
header="src/app/app.module.ts"
path="toh-pt5/src/app/app.module.ts">
</code-pane>
<code-pane
header="src/app/hero.service.ts"
path="toh-pt5/src/app/hero.service.ts">
</code-pane>
</code-tabs>
{@a appcomponent}
#### `AppComponent`
<code-tabs>
<code-pane
header="src/app/app.component.html"
path="toh-pt5/src/app/app.component.html">
</code-pane>
<code-pane
header="src/app/app.component.css"
path="toh-pt5/src/app/app.component.css">
</code-pane>
</code-tabs>
{@a dashboardcomponent}
#### `DashboardComponent`
<code-tabs>
<code-pane
header="src/app/dashboard/dashboard.component.html" path="toh-pt5/src/app/dashboard/dashboard.component.html">
</code-pane>
<code-pane
header="src/app/dashboard/dashboard.component.ts" path="toh-pt5/src/app/dashboard/dashboard.component.ts">
</code-pane>
<code-pane
header="src/app/dashboard/dashboard.component.css" path="toh-pt5/src/app/dashboard/dashboard.component.css">
</code-pane>
</code-tabs>
{@a heroescomponent}
#### `HeroesComponent`
<code-tabs>
<code-pane
header="src/app/heroes/heroes.component.html" path="toh-pt5/src/app/heroes/heroes.component.html">
</code-pane>
<code-pane
header="src/app/heroes/heroes.component.ts"
path="toh-pt5/src/app/heroes/heroes.component.ts">
</code-pane>
<code-pane
header="src/app/heroes/heroes.component.css"
path="toh-pt5/src/app/heroes/heroes.component.css">
</code-pane>
</code-tabs>
{@a herodetailcomponent}
#### `HeroDetailComponent`
<code-tabs>
<code-pane
header="src/app/hero-detail/hero-detail.component.html" path="toh-pt5/src/app/hero-detail/hero-detail.component.html">
</code-pane>
<code-pane
header="src/app/hero-detail/hero-detail.component.ts" path="toh-pt5/src/app/hero-detail/hero-detail.component.ts">
</code-pane>
<code-pane
header="src/app/hero-detail/hero-detail.component.css" path="toh-pt5/src/app/hero-detail/hero-detail.component.css">
</code-pane>
</code-tabs>
## Summary
* You added the Angular router to navigate among different components.
* You turned the `AppComponent` into a navigation shell with `<a>` links and a `<router-outlet>`.
* You configured the router in an `AppRoutingModule`
* You defined simple routes, a redirect route, and a parameterized route.
* You used the `routerLink` directive in anchor elements.
* You refactored a tightly-coupled master/detail view into a routed detail view.
* You used router link parameters to navigate to the detail view of a user-selected hero.
* You shared the `HeroService` among multiple components.

View File

@ -1,32 +1,32 @@
# Add in-app navigation with routing
# Agregar navegación en la aplicación con enrutamiento
There are new requirements for the Tour of Heroes app:
Hay nuevos requisitos para la aplicación Tour of Heroes:
* Add a *Dashboard* view.
* Add the ability to navigate between the *Heroes* and *Dashboard* views.
* When users click a hero name in either view, navigate to a detail view of the selected hero.
* When users click a *deep link* in an email, open the detail view for a particular hero.
* Agregar una vista de *Panel de control*.
* Agregue la capacidad de navegar entre las vistas *Heroes* y *Dashboard*.
* Cuando los usuarios hacen clic en el nombre de un héroe en cualquiera de las vistas, navega a una vista detallada del héroe seleccionado.
* Cuando los usuarios hacen clic en un *enlace profundo* en un correo electrónico, abre la vista detallada de un héroe en particular.
<div class="alert is-helpful">
For the sample app that this page describes, see the <live-example></live-example>.
Para ver la aplicación de ejemplo que describe esta página, consulte el<live-example></live-example>.
</div>
When youre done, users will be able to navigate the app like this:
Cuando haya terminado, los usuarios podrán navegar por la aplicación de esta manera:
<div class="lightbox">
<img src='generated/images/guide/toh/nav-diagram.png' alt="View navigations">
</div>
## Add the `AppRoutingModule`
## Agregar el `AppRoutingModule`
In Angular, the best practice is to load and configure the router in a separate, top-level module
that is dedicated to routing and imported by the root `AppModule`.
En Angular, la mejor práctica es cargar y configurar el enrutador en un módulo de nivel superior separado
que está dedicado al enrutamiento e importado por la raíz `AppModule`.
By convention, the module class name is `AppRoutingModule` and it belongs in the `app-routing.module.ts` in the `src/app` folder.
Por convención, el nombre de la clase del módulo es `AppRoutingModule` y pertenece a `app-routing.module.ts` en la carpeta `src/app`.
Use the CLI to generate it.
Utiliza el CLI para generarlo.
<code-example language="sh" class="code-shell">
ng generate module app-routing --flat --module=app
@ -34,52 +34,51 @@ Use the CLI to generate it.
<div class="alert is-helpful">
`--flat` puts the file in `src/app` instead of its own folder.<br>
`--module=app` tells the CLI to register it in the `imports` array of the `AppModule`.
`--flat` coloca el archivo en `src/app` en lugar de en su propia carpeta. <br>
`--module=app` le dice a la CLI que lo registre en la matriz de `importaciones` del `AppModule`.
</div>
The generated file looks like this:
El archivo generado se ve así:
<code-example path="toh-pt5/src/app/app-routing.module.0.ts" header="src/app/app-routing.module.ts (generated)">
</code-example>
Replace it with the following:
Reemplácelo con lo siguiente:
<code-example path="toh-pt5/src/app/app-routing.module.1.ts" header="src/app/app-routing.module.ts (updated)">
</code-example>
First, `AppRoutingModule` imports `RouterModule` and `Routes` so the app can have routing functionality. The next import, `HeroesComponent`, will give the Router somewhere to go once you configure the routes.
Primero, `AppRoutingModule` importa `RouterModule` y `Routes` para que la aplicación pueda tener funcionalidad de enrutamiento. La siguiente importación, `HeroesComponent`, le dará al enrutador un lugar adonde ir una vez que configure las rutas.
Notice that the `CommonModule` references and `declarations` array are unnecessary, so are no
longer part of `AppRoutingModule`. The following sections explain the rest of the `AppRoutingModule` in more detail.
Ten en cuenta que las referencias de CommonModule y la matriz de declaraciones son innecesarias, por lo que no
parte más larga de `AppRoutingModule`. Las siguientes secciones explican el resto del `AppRoutingModule` con más detalle.
### Rutas
### Routes
La siguiente parte del archivo es donde configura sus rutas.
*Rutas* le indican al enrutador qué vista mostrar cuando un usuario hace clic en un enlace o
pega una URL en la barra de direcciones del navegador.
The next part of the file is where you configure your routes.
*Routes* tell the Router which view to display when a user clicks a link or
pastes a URL into the browser address bar.
Since `AppRoutingModule` already imports `HeroesComponent`, you can use it in the `routes` array:
Como `AppRoutingModule` ya importa `HeroesComponent`, puedes usarlo en la matriz de `rutas`:
<code-example path="toh-pt5/src/app/app-routing.module.ts" header="src/app/app-routing.module.ts"
region="heroes-route">
</code-example>
A typical Angular `Route` has two properties:
Una `Ruta`pica de Angular tiene dos propiedades:
* `path`: a string that matches the URL in the browser address bar.
* `component`: the component that the router should create when navigating to this route.
* `path`: una cadena que coincide con la URL en la barra de direcciones del navegador.
* `componet`: el componente que el enrutador debe crear al navegar a esta ruta.
This tells the router to match that URL to `path: 'heroes'`
and display the `HeroesComponent` when the URL is something like `localhost:4200/heroes`.
Esto le dice al enrutador que haga coincidir esa URL con `path: 'héroes'`
y mostrar el `HeroesComponent` cuando la URL sea algo como `localhost:4200/heroes`.
### `RouterModule.forRoot()`
The `@NgModule` metadata initializes the router and starts it listening for browser location changes.
Los metadatos `@NgModule` inicializan el enrutador y lo hacen escuchar los cambios de ubicación del navegador.
The following line adds the `RouterModule` to the `AppRoutingModule` `imports` array and
configures it with the `routes` in one step by calling
La siguiente línea agrega el `RouterModule` a la matriz `AppRoutingModule` `importa` y
lo configura con las `rutas` en un solo paso llamando
`RouterModule.forRoot()`:
<code-example path="toh-pt5/src/app/app-routing.module.ts" header="src/app/app-routing.module.ts" region="ngmodule-imports">
@ -87,100 +86,99 @@ configures it with the `routes` in one step by calling
<div class="alert is-helpful">
The method is called `forRoot()` because you configure the router at the application's root level.
The `forRoot()` method supplies the service providers and directives needed for routing,
and performs the initial navigation based on the current browser URL.
El método se llama `forRoot()` porque configura el enrutador en el nivel raíz de la aplicación.
El método `forRoot()` proporciona los proveedores de servicios y las directivas necesarias para el enrutamiento,
y realiza la navegación inicial basada en la URL del navegador actual.
</div>
Next, `AppRoutingModule` exports `RouterModule` so it will be available throughout the app.
A continuación, `AppRoutingModule` exporta `RouterModule` para que esté disponible en toda la aplicación.
<code-example path="toh-pt5/src/app/app-routing.module.ts" header="src/app/app-routing.module.ts (exports array)" region="export-routermodule">
</code-example>
## Add `RouterOutlet`
## Agregar `RouterOutlet`
Open the `AppComponent` template and replace the `<app-heroes>` element with a `<router-outlet>` element.
Abre la plantilla `AppComponent` y reemplaza el elemento `<app-heroes>` con un elemento `<router-outlet>`.
<code-example path="toh-pt5/src/app/app.component.html" region="outlet" header="src/app/app.component.html (router-outlet)">
</code-example>
The `AppComponent` template no longer needs `<app-heroes>` because the app will only display the `HeroesComponent` when the user navigates to it.
La plantilla `AppComponent` ya no necesita `<app-heroes>` porque la aplicación solo mostrará el `HeroesComponent` cuando el usuario navegue hacia él.
The `<router-outlet>` tells the router where to display routed views.
El `<router-outlet>` le dice al enrutador dónde mostrar las vistas enrutadas.
<div class="alert is-helpful">
The `RouterOutlet` is one of the router directives that became available to the `AppComponent`
because `AppModule` imports `AppRoutingModule` which exported `RouterModule`. The `ng generate` command you ran at the start of this tutorial added this import because of the `--module=app` flag. If you manually created `app-routing.module.ts` or used a tool other than the CLI to do so, you'll need to import `AppRoutingModule` into `app.module.ts` and add it to the `imports` array of the `NgModule`.
El `RouterOutlet` es una de las directivas del enrutador que estuvo disponible para el `AppComponent`
porque `AppModule` importa `AppRoutingModule` que exportó `RouterModule`. El comando `ng generate` que ejecutó al comienzo de este tutorial agregó esta importación debido a la marca `--module=app`. Si creó manualmente `app-routing.module.ts` o usó una herramienta que no sea la CLI para hacerlo, deberá importar `AppRoutingModule` a `app.module.ts` y agregarlo a las `importaciones` matriz del `NgModule`.
</div>
#### Try it
#### Pruébalo
You should still be running with this CLI command.
Debería seguir ejecutando este comando CLI.
<code-example language="sh" class="code-shell">
ng serve
</code-example>
The browser should refresh and display the app title but not the list of heroes.
El navegador debería actualizar y mostrar el título de la aplicación, pero no la lista de héroes.
Look at the browser's address bar.
The URL ends in `/`.
The route path to `HeroesComponent` is `/heroes`.
Mira la barra de direcciones del navegador.
La URL termina en `/`.
La ruta de acceso a `HeroesComponent` es `/heroes`.
Append `/heroes` to the URL in the browser address bar.
You should see the familiar heroes master/detail view.
Agrega `/heroes` a la URL en la barra de direcciones del navegador.
Debería ver la vista maestra / detallada de héroes familiares.
{@a routerlink}
## Add a navigation link (`routerLink`)
## Agregar un enlace de navegación (`routerLink`)
Ideally, users should be able to click a link to navigate rather
than pasting a route URL into the address bar.
Idealmente, los usuarios deberían poder hacer clic en un enlace para navegar en lugar de
que pegar una URL de ruta en la barra de direcciones.
Add a `<nav>` element and, within that, an anchor element that, when clicked,
triggers navigation to the `HeroesComponent`.
The revised `AppComponent` template looks like this:
Agrega un elemento `<nav>` y, dentro de él, un elemento de ancla que, al hacer clic,
activa la navegación al `HeroesComponent`.
La plantilla `AppComponent` revisada se ve así:
<code-example path="toh-pt5/src/app/app.component.html" region="heroes" header="src/app/app.component.html (heroes RouterLink)">
</code-example>
A [`routerLink` attribute](#routerlink) is set to `"/heroes"`,
the string that the router matches to the route to `HeroesComponent`.
The `routerLink` is the selector for the [`RouterLink` directive](/api/router/RouterLink)
that turns user clicks into router navigations.
It's another of the public directives in the `RouterModule`.
Un [atributo `routerLink`](#routerlink) se establece en `"/heroes"`,
la cadena que el enrutador coincide con la ruta a `HeroesComponent`.
El `routerLink` es el selector para la [directiva `RouterLink`](/api/router/RouterLink)
que convierte los clics del usuario en navegaciones del enrutador.
Es otra de las directivas públicas del `RouterModule`.
The browser refreshes and displays the app title and heroes link,
but not the heroes list.
El navegador se actualiza y muestra el título de la aplicación y el enlace de héroes.
pero no la lista de héroes.
Click the link.
The address bar updates to `/heroes` and the list of heroes appears.
Haz clic en el enlace.
La barra de direcciones se actualiza a `/heroes` y aparece la lista de héroes.
<div class="alert is-helpful">
Make this and future navigation links look better by adding private CSS styles to `app.component.css`
as listed in the [final code review](#appcomponent) below.
Hace que este y los enlaces de navegación futuros se vean mejor agregando estilos CSS privados a `app.component.css`
como se indica en la [revisión final del código](#appcomponent) a continuación.
</div>
## Agregar una vista de panel
## Add a dashboard view
El enrutamiento tiene más sentido cuando hay varias vistas.
Hasta ahora solo existe la vista de héroes.
Routing makes more sense when there are multiple views.
So far there's only the heroes view.
Add a `DashboardComponent` using the CLI:
Agrega un `DashboardComponent` usando la CLI:
<code-example language="sh" class="code-shell">
ng generate component dashboard
</code-example>
The CLI generates the files for the `DashboardComponent` and declares it in `AppModule`.
La CLI genera los archivos para el `DashboardComponent` y lo declara en `AppModule`.
Replace the default file content in these three files as follows:
Reemplaza el contenido del archivo predeterminado en estos tres archivos de la siguiente manera:
<code-tabs>
<code-pane
@ -196,121 +194,121 @@ Replace the default file content in these three files as follows:
</code-pane>
</code-tabs>
The _template_ presents a grid of hero name links.
La _plantilla_ presenta una cuadrícula de enlaces de nombres de héroes.
* The `*ngFor` repeater creates as many links as are in the component's `heroes` array.
* The links are styled as colored blocks by the `dashboard.component.css`.
* The links don't go anywhere yet but [they will shortly](#hero-details).
* El repetidor `* ngFor` crea tantos enlaces como hay en en el arreglo `heroes` del componente.
* Los enlaces están diseñados como bloques de colores por el `dashboard.component.css`.
* Los enlaces no van a ninguna parte todavía, pero [lo harán en breve](#hero-details).
The _class_ is similar to the `HeroesComponent` class.
* It defines a `heroes` array property.
* The constructor expects Angular to inject the `HeroService` into a private `heroService` property.
* The `ngOnInit()` lifecycle hook calls `getHeroes()`.
La _clase_ es similar a la clase `HeroesComponent`.
* Define una propiedad de matriz de héroes.
* El constructor espera que Angular inyecte el `HeroService` en una propiedad privada de `heroService`.
* El gancho del ciclo de vida `ngOnInit()` llama a `getHeroes()`.
This `getHeroes()` returns the sliced list of heroes at positions 1 and 5, returning only four of the Top Heroes (2nd, 3rd, 4th, and 5th).
Este `getHeroes()` devuelve la lista dividida de héroes en las posiciones 1 y 5, devolviendo solo cuatro de los mejores héroes (segundo, tercero, cuarto y quinto).
<code-example path="toh-pt5/src/app/dashboard/dashboard.component.ts" header="src/app/dashboard/dashboard.component.ts" region="getHeroes">
</code-example>
### Add the dashboard route
### Agregar la ruta del tablero(dashboard)
To navigate to the dashboard, the router needs an appropriate route.
Para navegar hasta el tablero, el enrutador necesita una ruta adecuada.
Import the `DashboardComponent` in the `AppRoutingModule`.
Importa el `DashboardComponent` en el `AppRoutingModule`.
<code-example path="toh-pt5/src/app/app-routing.module.ts" region="import-dashboard" header="src/app/app-routing.module.ts (import DashboardComponent)">
</code-example>
Add a route to the `AppRoutingModule.routes` array that matches a path to the `DashboardComponent`.
Agrega una ruta a la matriz `AppRoutingModule.routes` que coincida con una ruta al `DashboardComponent`.
<code-example path="toh-pt5/src/app/app-routing.module.ts" header="src/app/app-routing.module.ts" region="dashboard-route">
</code-example>
### Add a default route
### Agregar una ruta predeterminada
When the app starts, the browser's address bar points to the web site's root.
That doesn't match any existing route so the router doesn't navigate anywhere.
The space below the `<router-outlet>` is blank.
Cuando se inicia la aplicación, la barra de direcciones del navegador apunta a la raíz del sitio web.
Eso no coincide con ninguna ruta existente, por lo que el enrutador no navega a ninguna parte.
El espacio debajo de `<router-outlet>` está en blanco.
To make the app navigate to the dashboard automatically, add the following
route to the `AppRoutingModule.Routes` array.
Para que la aplicación navegue al panel de control automáticamente, agreaga la siguiente
ruta a la matriz `AppRoutingModule.Routes`.
<code-example path="toh-pt5/src/app/app-routing.module.ts" header="src/app/app-routing.module.ts" region="redirect-route">
</code-example>
This route redirects a URL that fully matches the empty path to the route whose path is `'/dashboard'`.
Esta ruta redirige una URL que coincide completamente con la ruta vacía a la ruta cuya ruta es `'/dashboard'`.
After the browser refreshes, the router loads the `DashboardComponent`
and the browser address bar shows the `/dashboard` URL.
Después de que el navegador se actualiza, el enrutador carga el `DashboardComponent`
y la barra de direcciones del navegador muestra la URL `/dashboard`.
### Add dashboard link to the shell
### Agregar enlace del tablero al caparazón
The user should be able to navigate back and forth between the
`DashboardComponent` and the `HeroesComponent` by clicking links in the
navigation area near the top of the page.
El usuario debe poder navegar hacia adelante y hacia atrás entre
`DashboardComponent` y `HeroesComponent` haciendo clic en los enlaces en el
área de navegación cerca de la parte superior de la página.
Add a dashboard navigation link to the `AppComponent` shell template, just above the *Heroes* link.
Agrega un enlace de navegación del panel de control a la plantilla de caparazón `AppComponent`, justo encima del enlace *Heroes*.
<code-example path="toh-pt5/src/app/app.component.html" header="src/app/app.component.html">
</code-example>
After the browser refreshes you can navigate freely between the two views by clicking the links.
Después de que se actualice el navegador, puedes navegar libremente entre las dos vistas haciendo clic en los enlaces.
{@a hero-details}
## Navigating to hero details
## Navegando a los detalles del héroe
The `HeroDetailsComponent` displays details of a selected hero.
At the moment the `HeroDetailsComponent` is only visible at the bottom of the `HeroesComponent`
El `HeroDetailsComponent` muestra los detalles de un héroe seleccionado.
Por el momento, el `HeroDetailsComponent` solo es visible en la parte inferior del `HeroesComponent`
The user should be able to get to these details in three ways.
El usuario debería poder acceder a estos detalles de tres formas.
1. By clicking a hero in the dashboard.
1. By clicking a hero in the heroes list.
1. By pasting a "deep link" URL into the browser address bar that identifies the hero to display.
1. Haciendo clic en un héroe en el tablero.
1. Haciendo clic en un héroe de la lista de héroes.
1. Pegando una URL de "enlace profundo" en la barra de direcciones del navegador que identifica al héroe a mostrar.
In this section, you'll enable navigation to the `HeroDetailsComponent`
and liberate it from the `HeroesComponent`.
En esta sección, habilitará la navegación al `HeroDetailsComponent`
y libérelo del `HeroesComponent`.
### Delete _hero details_ from `HeroesComponent`
### Eliminar _detalles de héroe_ de `HeroesComponent`
When the user clicks a hero item in the `HeroesComponent`,
the app should navigate to the `HeroDetailComponent`,
replacing the heroes list view with the hero detail view.
The heroes list view should no longer show hero details as it does now.
Cuando el usuario hace clic en un elemento de héroe en el `HeroesComponent`,
la aplicación debería navegar hasta el `HeroDetailComponent`,
reemplazando la vista de lista de héroes con la vista de detalles de héroe.
La vista de lista de héroes ya no debería mostrar los detalles de los héroes como lo hace ahora.
Open the `HeroesComponent` template (`heroes/heroes.component.html`) and
delete the `<app-hero-detail>` element from the bottom.
Abre la plantilla `HeroesComponent` (`heroes/heroes.component.html`) y
elimine el elemento `<app-hero-detail>` de la parte inferior.
Clicking a hero item now does nothing.
You'll [fix that shortly](#heroes-component-links) after you enable routing to the `HeroDetailComponent`.
Hacer clic en un elemento de héroe ahora no hace nada.
Lo [arreglará en breve](#heroes-component-links) después de habilitar el enrutamiento al `HeroDetailComponent`.
### Add a _hero detail_ route
### Agregar una ruta _detalle del héroe_
A URL like `~/detail/11` would be a good URL for navigating to the *Hero Detail* view of the hero whose `id` is `11`.
Una URL como `~/detail/11` sería una buena URL para navegar a la vista *Hero Detail* del héroe cuyo `id` es `11`.
Open `AppRoutingModule` and import `HeroDetailComponent`.
Abre `AppRoutingModule` e importe `HeroDetailComponent`.
<code-example path="toh-pt5/src/app/app-routing.module.ts" region="import-herodetail" header="src/app/app-routing.module.ts (import HeroDetailComponent)">
</code-example>
Then add a _parameterized_ route to the `AppRoutingModule.routes` array that matches the path pattern to the _hero detail_ view.
Luego, agrega una ruta _parametrizada_ a la matriz `AppRoutingModule.routes` que coincida con el patrón de ruta de la vista _detalle del héroe_.
<code-example path="toh-pt5/src/app/app-routing.module.ts" header="src/app/app-routing.module.ts" region="detail-route">
</code-example>
The colon (:) in the `path` indicates that `:id` is a placeholder for a specific hero `id`.
Los dos puntos (:) en el `path` indican que `: id` es un marcador de posición para un `id` de héroe específico.
At this point, all application routes are in place.
En este punto, todas las rutas de aplicación están en su lugar.
<code-example path="toh-pt5/src/app/app-routing.module.ts" region="routes" header="src/app/app-routing.module.ts (all routes)">
</code-example>
### `DashboardComponent` hero links
### Enlaces de héroe de `DashboardComponent`
The `DashboardComponent` hero links do nothing at the moment.
Los enlaces de héroe `DashboardComponent` no hacen nada en este momento.
Now that the router has a route to `HeroDetailComponent`,
fix the dashboard hero links to navigate via the _parameterized_ dashboard route.
Ahora que el enrutador tiene una ruta a `HeroDetailComponent`,
Corrige los enlaces del héroe del tablero para navegar a través de la ruta del tablero _parameterized_.
<code-example
path="toh-pt5/src/app/dashboard/dashboard.component.html"
@ -318,161 +316,161 @@ fix the dashboard hero links to navigate via the _parameterized_ dashboard route
header="src/app/dashboard/dashboard.component.html (hero links)">
</code-example>
You're using Angular [interpolation binding](guide/interpolation) within the `*ngFor` repeater
to insert the current iteration's `hero.id` into each
[`routerLink`](#routerlink).
Estás usando el [enlace de interpolación](guide/interpolation) Angular dentro del repetidor `*ngFor`
para insertar el `hero.id` de la iteración actual en cada
[`enlace del enrutador`](#routerlink).
{@a heroes-component-links}
### `HeroesComponent` hero links
### Enlaces de héroe de `HeroesComponent`
The hero items in the `HeroesComponent` are `<li>` elements whose click events
are bound to the component's `onSelect()` method.
Los elementos de héroe en el `HeroesComponent` son elementos` <li> `cuyos eventos de clic
están vinculados al método `onSelect()` del componente.
<code-example path="toh-pt4/src/app/heroes/heroes.component.html" region="list" header="src/app/heroes/heroes.component.html (list with onSelect)">
</code-example>
Strip the `<li>` back to just its `*ngFor`,
wrap the badge and name in an anchor element (`<a>`),
and add a `routerLink` attribute to the anchor that
is the same as in the dashboard template
Quita el `<li>` de nuevo a su `* ngFor`,
envuelve la insignia y el nombre en un elemento de anclaje (`<a>`),
y agrega un atributo `routerLink` al ancla que
es el mismo que en la plantilla del panel
<code-example path="toh-pt5/src/app/heroes/heroes.component.html" region="list" header="src/app/heroes/heroes.component.html (list with links)">
</code-example>
You'll have to fix the private stylesheet (`heroes.component.css`) to make
the list look as it did before.
Revised styles are in the [final code review](#heroescomponent) at the bottom of this guide.
Tendrás que arreglar la hoja de estilo privada (`heroes.component.css`) para hacer
la lista tiene el mismo aspecto que antes.
Los estilos revisados se encuentran en la [revisión final del código](#heroescomponent) al final de esta guía.
#### Remove dead code (optional)
#### Eliminar código muerto (opcional)
While the `HeroesComponent` class still works,
the `onSelect()` method and `selectedHero` property are no longer used.
Si bien la clase `HeroesComponent` todavía funciona,
el método `onSelect()` y la propiedad `selectedHero` ya no se utilizan.
It's nice to tidy up and you'll be grateful to yourself later.
Here's the class after pruning away the dead code.
Es agradable poner en orden y te lo agradecerás más tarde.
Aquí está la clase después de podar el código muerto.
<code-example path="toh-pt5/src/app/heroes/heroes.component.ts" region="class" header="src/app/heroes/heroes.component.ts (cleaned up)">
</code-example>
## Routable `HeroDetailComponent`
## `HeroDetailComponent` enrutable
Previously, the parent `HeroesComponent` set the `HeroDetailComponent.hero`
property and the `HeroDetailComponent` displayed the hero.
Anteriormente, el padre `HeroesComponent` configuraba el `HeroDetailComponent.hero`
propiedad y el `HeroDetailComponent` mostraba el héroe.
`HeroesComponent` doesn't do that anymore.
Now the router creates the `HeroDetailComponent` in response to a URL such as `~/detail/11`.
`HeroesComponent` ya no hace eso.
Ahora el enrutador crea el `HeroDetailComponent` en respuesta a una URL como `~/detail/11`.
The `HeroDetailComponent` needs a new way to obtain the hero-to-display.
This section explains the following:
El `HeroDetailComponent` necesita una nueva forma de obtener el héroe a mostrar.
Esta sección explica lo siguiente:
* Get the route that created it
* Extract the `id` from the route
* Acquire the hero with that `id` from the server via the `HeroService`
* Obtén la ruta que lo creó
* Extrae el `id` de la ruta
* Adquirir el héroe con ese "id" del servidor a través de "HeroService"
Add the following imports:
Agrega las siguientes importaciones:
<code-example path="toh-pt5/src/app/hero-detail/hero-detail.component.ts" region="added-imports" header="src/app/hero-detail/hero-detail.component.ts">
</code-example>
{@a hero-detail-ctor}
Inject the `ActivatedRoute`, `HeroService`, and `Location` services
into the constructor, saving their values in private fields:
Inyecta los servicios `ActivatedRoute`, `HeroService` y `Location`
en el constructor, guardando sus valores en campos privados:
<code-example path="toh-pt5/src/app/hero-detail/hero-detail.component.ts" header="src/app/hero-detail/hero-detail.component.ts" region="ctor">
</code-example>
The [`ActivatedRoute`](api/router/ActivatedRoute) holds information about the route to this instance of the `HeroDetailComponent`.
This component is interested in the route's parameters extracted from the URL.
The "id" parameter is the `id` of the hero to display.
El [`ActivatedRoute`](api/router/ActivatedRoute) contiene información sobre la ruta a esta instancia del `HeroDetailComponent`.
Este componente está interesado en los parámetros de la ruta extraídos de la URL.
El parámetro "id" es el `id` del héroe que se mostrará.
The [`HeroService`](tutorial/toh-pt4) gets hero data from the remote server
and this component will use it to get the hero-to-display.
El [`HeroService`](tutorial/toh-pt4) obtiene los datos del héroe del servidor remoto
y este componente lo usará para mostrar el héroe.
The [`location`](api/common/Location) is an Angular service for interacting with the browser.
You'll use it [later](#goback) to navigate back to the view that navigated here.
La [`ubicación`](api/common/Location) es un servicio Angular para interactuar con el navegador.
Lo usarás [más tarde](#goback) para volver a la vista que navegó aquí.
### Extract the `id` route parameter
### Extrae el parámetro de ruta `id`
In the `ngOnInit()` [lifecycle hook](guide/lifecycle-hooks#oninit)
call `getHero()` and define it as follows.
En el `ngOnInit()` [gancho del ciclo de vida](guide/lifecycle-hooks#oninit)
llama a `getHero()` y defínalo de la siguiente manera.
<code-example path="toh-pt5/src/app/hero-detail/hero-detail.component.ts" header="src/app/hero-detail/hero-detail.component.ts" region="ngOnInit">
</code-example>
The `route.snapshot` is a static image of the route information shortly after the component was created.
`Route.snapshot` es una imagen estática de la información de la ruta poco después de que se creó el componente.
The `paramMap` is a dictionary of route parameter values extracted from the URL.
The `"id"` key returns the `id` of the hero to fetch.
El `paramMap` es un diccionario de valores de parámetros de ruta extraídos de la URL.
La clave `"id"` devuelve el `id` del héroe a buscar.
Route parameters are always strings.
The JavaScript (+) operator converts the string to a number,
which is what a hero `id` should be.
Los parámetros de ruta son siempre cadenas.
El operador JavaScript (+) convierte la cadena en un número,
que es lo que debería ser un "id" de héroe.
The browser refreshes and the app crashes with a compiler error.
`HeroService` doesn't have a `getHero()` method.
Add it now.
El navegador se actualiza y la aplicación se bloquea con un error del compilador.
`HeroService` no tiene un método `getHero()`.
Agréguelo ahora.
### Add `HeroService.getHero()`
### Agregar `HeroService.getHero ()`
Open `HeroService` and add the following `getHero()` method with the `id` after the `getHeroes()` method:
Abre `HeroService` y agrega el siguiente método `getHero()` con el `id` después del método `getHeroes ()`:
<code-example path="toh-pt5/src/app/hero.service.ts" region="getHero" header="src/app/hero.service.ts (getHero)">
</code-example>
<div class="alert is-important">
Note the backticks ( &#96; ) that define a JavaScript
[_template literal_](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) for embedding the `id`.
Ten en cuenta las comillas invertidas (&#96;) que definen un JavaScript
[_plantilla literal_](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals) para incrustar el `id`.
</div>
Like [`getHeroes()`](tutorial/toh-pt4#observable-heroservice),
`getHero()` has an asynchronous signature.
It returns a _mock hero_ as an `Observable`, using the RxJS `of()` function.
Como [`getHeroes()`](tutorial/toh-pt4#observable-heroservice),
`getHero()` tiene una firma asincrónica.
Devuelve un _mock hero_ como un `Observable`, usando la función RxJS `of()`.
You'll be able to re-implement `getHero()` as a real `Http` request
without having to change the `HeroDetailComponent` that calls it.
Podrá volver a implementar `getHero()` como una solicitud real de `Http`
sin tener que cambiar el `HeroDetailComponent` que lo llama.
#### Try it
#### Pruébalo
The browser refreshes and the app is working again.
You can click a hero in the dashboard or in the heroes list and navigate to that hero's detail view.
El navegador se actualiza y la aplicación vuelve a funcionar.
Puedes hacer clic en un héroe en el tablero o en la lista de héroes y navegar hasta la vista de detalles de ese héroe.
If you paste `localhost:4200/detail/11` in the browser address bar,
the router navigates to the detail view for the hero with `id: 11`, "Dr Nice".
Si pega `localhost:4200/detail/11` en la barra de direcciones del navegador,
el enrutador navega a la vista detallada del héroe con `id: 11`," Dr Nice ".
{@a goback}
### Find the way back
### Encuentra el camino de regreso
By clicking the browser's back button,
you can go back to the hero list or dashboard view,
depending upon which sent you to the detail view.
Al hacer clic en el botón Atrás del navegador,
puede volver a la lista de héroes o la vista del panel,
dependiendo de cuál le envió a la vista detallada.
It would be nice to have a button on the `HeroDetail` view that can do that.
Sería bueno tener un botón en la vista `HeroDetail` que pueda hacer eso.
Add a *go back* button to the bottom of the component template and bind it
to the component's `goBack()` method.
Agrega un botón *volver* en la parte inferior de la plantilla del componente y vincúlalo
al método `goBack()` del componente.
<code-example path="toh-pt5/src/app/hero-detail/hero-detail.component.html" region="back-button" header="src/app/hero-detail/hero-detail.component.html (back button)">
</code-example>
Add a `goBack()` _method_ to the component class that navigates backward one step
in the browser's history stack
using the `Location` service that you [injected previously](#hero-detail-ctor).
Agrega un método `goBack()` a la clase de componente que navega hacia atrás un paso
en la pila de historial del navegador
usando el servicio `Location` que [inyectaste previamente](#hero-detail-ctor).
<code-example path="toh-pt5/src/app/hero-detail/hero-detail.component.ts" region="goBack" header="src/app/hero-detail/hero-detail.component.ts (goBack)">
</code-example>
Actualiza el navegador y comience a hacer clic.
Los usuarios pueden navegar por la aplicación, desde el panel hasta los detalles del héroe y viceversa,
de la lista de héroes al mini detalle a los detalles del héroe y de regreso a los héroes nuevamente.
Refresh the browser and start clicking.
Users can navigate around the app, from the dashboard to hero details and back,
from heroes list to the mini detail to the hero details and back to the heroes again.
## Revisión final del código
## Final code review
Here are the code files discussed on this page.
Aquí están los archivos de código discutidos en esta página.
{@a approutingmodule}
{@a appmodule}
@ -561,13 +559,13 @@ Here are the code files discussed on this page.
</code-pane>
</code-tabs>
## Summary
## Resumen
* You added the Angular router to navigate among different components.
* You turned the `AppComponent` into a navigation shell with `<a>` links and a `<router-outlet>`.
* You configured the router in an `AppRoutingModule`
* You defined simple routes, a redirect route, and a parameterized route.
* You used the `routerLink` directive in anchor elements.
* You refactored a tightly-coupled master/detail view into a routed detail view.
* You used router link parameters to navigate to the detail view of a user-selected hero.
* You shared the `HeroService` among multiple components.
* Agregó el enrutador Angular para navegar entre diferentes componentes.
* Convirtió el `AppComponent` en un caparazón de navegación con enlaces `<a>`y un `<router-outlet>`.
* Configuró el enrutador en un `AppRoutingModule`
* Definió rutas simples, una ruta de redireccionamiento y una ruta parametrizada.
* Usó la directiva `routerLink` en elementos de anclaje.
* Refactorizó una vista maestra/detallada estrechamente acoplada en una vista de detalle enrutada.
* Usó parámetros de enlace del enrutador para navegar a la vista detallada de un héroe seleccionado por el usuario.
* Compartió el "HeroService" entre varios componentes.

View File

@ -12,7 +12,7 @@ describe('site App', function() {
it('should show features text after clicking "Features"', () => {
page.navigateTo('');
page.click(page.getTopMenuLink('features'));
expect(page.getDocViewerText()).toMatch(/Progressive web apps/i);
expect(page.getDocViewerText()).toMatch(/Aplicaciones Web Progresivas/i);
});
it('should set appropriate window titles', () => {
@ -20,7 +20,7 @@ describe('site App', function() {
expect(browser.getTitle()).toBe('Angular');
page.click(page.getTopMenuLink('features'));
expect(browser.getTitle()).toBe('Angular - FEATURES & BENEFITS');
expect(browser.getTitle()).toBe('Angular - FUNCIONALIDADES & VENTAJAS');
page.click(page.homeLink);
expect(browser.getTitle()).toBe('Angular');
@ -79,7 +79,7 @@ describe('site App', function() {
// navigate to a different page
page.click(page.getTopMenuLink('features'));
expect(page.getDocViewerText()).toMatch(/Progressive web apps/i);
expect(page.getDocViewerText()).toMatch(/Aplicaciones Web Progresivas/i);
// Show the menu
page.click(page.docsMenuLink);
@ -87,7 +87,7 @@ describe('site App', function() {
// Tutorial folder should still be expanded because this test runs in wide mode
// Navigate to the tutorial introduction via a link in the sidenav
page.click(page.getNavItem(/El Editor de Héroe/i));
expect(page.getDocViewerText()).toMatch(/The hero editor/i);
expect(page.getDocViewerText()).toMatch(/El editor de Héroe/i);
});
it('should render `{@example}` dgeni tags as `<code-example>` elements with HTML escaped content', () => {