Spas se puso de moda como la antítesis de los sitios web de la parte del lado del servidor. Hay pros y contras, como siempre, pero un spa puede crear una experiencia de usuario suave que sea difícil de replicar sin tal enfoque. Esto es parte de la empresa de aplicaciones web en el territorio que tradicionalmente estaba ocupada por aplicaciones de escritorio. Las aplicaciones web generalmente son criticadas por ser lentos en comparación con las aplicaciones de escritorio, pero los avances importantes en las tecnologías web (especialmente NodeJs y el motor V8 de Google, así como marcos como Angular y ReactJs), así como el acceso convencional al poder informático sin precedentes significa que esto es mucho que esto es mucho Menos de un problema de lo que solía ser.
Los SPA funcionan cargando una sola página HTML en el cliente y luego actualizando el contenido (y el DOM) dinámicamente, en lugar de recuperar una página HTML actualizada desde el servidor cada vez que el usuario hace clic en cualquier lugar.
NodeJS
y npm
siguiendo las instrucciones apropiadas aquí.TypeScript
desde aquí.npm install -g @angular/cli
ng new my-project
cd my-project && ng serve
localhost:4200
en su navegador web. Si abre my-project/src/app/app.component.ts
debe ser recibido con algo como lo siguiente:
/** app.component.ts */
import { Component } from '@angular/core' ; // #1
@ Component ( { // #2
selector : 'app-root' , // #3
templateUrl : './app.component.html' , // #4
styleUrls : [ './app.component.css' ] // #5
} )
export class AppComponent {
title = 'app' ; // #6
}
Desglosemos esto un poco:
Component
desde el módulo @angular
ubicado en la carpeta node_modules
.@Component
marca una clase como un componente angular. El decorador está parametrizado con un objeto de opciones, de los cuales utilizaremos solo unos pocos parámetros.selector
define cuáles serán las etiquetas HTML para ese componente, por ejemplo, este se inyectará en HTML usando <app-root> … </app-root>
.templateUrl
toma un argumento de cadena que apunta al archivo HTML que sirve como parte de la vista del componente. También puede usar el parámetro template
para escribir HTML directamente, en lugar de señalar un archivo. Esto generalmente no se recomienda, a menos que la parte de vista sea simplemente una o dos líneas.styleUrls
toma una lista de cadenas, donde cada cadena es una ruta a un archivo CSS.title
se puede hacer referencia desde app.component.html
. Navegue a app.component.html
. Este es el punto de entrada a nuestra aplicación, y el HTML de más alto nivel que debe estar editando. Todo se representará dentro de este contexto.
Ahora eliminaremos el contenido de este archivo y lo reemplazaremos con lo siguiente:
< div class =" container " >
< h1 > Live football scores </ h1 >
< div class =" score-card-list " >
< div class =" score-card " >
< span > Team 1 </ span >
< span > 0 : 0 </ span >
< span > Team 2 </ span >
</ div >
</ div >
</ div >
Vuelva a su navegador web y debería ver que la página se actualiza automáticamente. Excelente.
Visualmente, probablemente estarás decepcionado en esta etapa, pero es un trabajo en progreso.
Angular Material es una biblioteca mantenida por un equipo de Google para proporcionar componentes de diseño de material fáciles de usar para aplicaciones angulares. El estilo de estos componentes se puede ajustar al contenido de su corazón, pero también proporciona una manera fácil de actualizar el aspecto de una aplicación prototipo con un esfuerzo mínimo.
Instale las bibliotecas requeridas con npm install --save @angular/material @angular/cdk @angular/animations
. Esto descarga los archivos requeridos en su carpeta node_modules
y actualiza el archivo packages.json
adecuadamente.
También tendrá que decirle a Angular que cargue los componentes relevantes. Tendrá que buscar en Google sobre cómo funciona esto (este es un tipo de tutorial de cinta adhesiva de 101), pero básicamente solo tiene que modificar el archivo app.modules.ts
para incluir los módulos que necesita.
Primero, agregue las animaciones como así:
import { BrowserAnimationsModule } from '@angular/platform-browser/animations' ;
@ NgModule ( {
...
imports : [ BrowserAnimationsModule ] ,
...
} )
Luego agregue Importar los módulos que necesitamos:
import { MatButtonModule , MatCardModule } from '@angular/material' ;
@ NgModule ( {
...
imports : [ MatButtonModule , MatCardModule ] ,
...
} )
Tenga en cuenta que necesitará importar los componentes que use en su aplicación. La razón para no incluir toda la biblioteca es ayudar a webpack
a realizar el agitación de los árboles, lo que básicamente implica dejar de lado las piezas de código no utilizadas al agrupar todo nuestro código en algunos archivos minificados .js
.
Una vez hecho esto, tendremos acceso a un montón de componentes predefinidos, y simplemente puede importarlos según sea necesario. También vale la pena señalar que los temas preconstruidos son fáciles de usar.
En angular, los componentes son modulares. Cada componente tiene sus propios archivos CSS, que solo se aplican a esa vista específica. La única excepción es el archivo styles.css
que se encuentra en el directorio src
que se aplica a nivel mundial.
El estilo no es un foco de este tutorial, por lo que intentaremos evitarlo tanto como sea posible en el futuro. Para sentirse un poco mejor sobre lo que estamos haciendo, aquí hay algunos CSS simples para copiar y pegar en el archivo styles.css
(una mejor práctica sería agregar bits relevantes a los archivos CSS de componentes apropiados, para referencia futura):
// Outer wrapper div
. container {
text-align : center;
}
// Wrapper for the score cards
. score-card-list {
display : flex;
flex-direction : column;
align-items : center;
}
// Each score card
. score-card {
width : 550 px ;
display : flex !important ;
}
// Home team score
. home {
flex : 1 ;
text-align : right;
}
// Away team score
. away {
flex : 1 ;
text-align : left;
}
// Score section of score card
. score {
width : 100 px ;
}
Actualice también su HTML para incluir las clases:
...
< mat-card class =" score-card " >
< span class =" home " > Team 1 </ span >
< span class =" score " > 0 : 0 </ span >
< span class =" away " > Team 2 </ span >
</ mat-card >
...
Si está confundido acerca de flex
, le recomiendo que revise Flexbox Froggy.
Una de las ventajas de usar un marco como Angular es que podemos implementar alguna lógica de programación directamente en el HTML. En nuestro caso, For-Loops hará la vida mucho más fácil.
Prueba lo siguiente:
...
< mat-card class =" score-card " *ngFor =" let team of ['Arsenal', 'Liverpool', 'Tottenham']; let i=index " >
< span class =" home " > {{ team }} </ span >
< span class =" score " > 0 : 0 </ span >
< span class =" away " > Team {{ i+1 }} </ span >
</ mat-card >
...
Esto introduce una gama de nuevos conceptos, y debe producir algo como lo siguiente:
Conceptos a tener en cuenta:
{{ 'Some text = ' + a_number }}
Los servicios se utilizan para abstrair el acceso de datos lejos de los componentes. Esto permite que los componentes permanezcan delgados y se centraran en apoyar la vista. Las pruebas unitarias y el mantenimiento del código se simplifican al mantener esta separación de la responsabilidad.
Para generar un servicio utilizando la CLI, puede usar ng generate service football-data
(o ng gs football-data
para personas a poco tiempo). Esto creará dos archivos nuevos en su directorio project-dir/src/app
: football-data.service.ts
y football-data.service.spec.ts
. Esto sigue la convención angular por la cual los archivos de prueba obtienen la extensión .spec.ts
. Aunque las pruebas son útiles e importantes, queda fuera del alcance inmediato de este tutorial para que pueda ignorarlo por el momento (lo siento, @cornel).
Agregue las siguientes líneas a football-data.service.ts
:
import { Injectable } from '@angular/core' ;
import { HttpHeaders } from '@angular/common/http' ;
@ Injectable ( ) // Designate this class as an injectable service
export class FootballDataService {
// Token for accessing data on football-data.org (see: https://www.football-data.org/client/register)
HEADERS = new HttpHeaders ( { 'X-Auth-Token' : 'dc25ff8a05123411sadgvde5bb16lklnmc7' } ) ;
// Convenience constant
COMPETITION_URL = 'http://api.football-data.org/v1/competitions/' ;
// ID for the Premier League
PL_ID = 445 ;
constructor ( ) { }
}
Los servicios se anotan con @Injectable
, que le dice al compilador que las instancias del servicio pueden inyectarse en componentes. También debe especificar un proveedor para cada servicio. El proveedor proporciona efectivamente un singleton en el nivel especificado.
Por ejemplo, puede agregar su servicio a la matriz de proveedores en app.module.ts
, lo que dará como resultado una instancia de Singleton del servicio expuesto en toda la aplicación. De lo contrario, puede agregarlo a la lista de proveedores de un componente, lo que dará como resultado que una instancia de singleton esté disponible para ese componente y cualquier componente dentro de su contexto.
Advertencia: se hace fácil crear dependencias circulares entre los servicios una vez que su aplicación se hace más grande. Tenga en cuenta esto. Si (cuándo) recibe un mensaje de error que parece algo así como Cannot resolve all parameters for MyDataService(?)
, Probablemente esté relacionado con problemas de dependencia circular.
Haremos uso de la fantástica API disponible gratuitamente en fútbol-data.org. Deberá obtener su propia clave API, pero es fácil de hacer, solo siga las instrucciones en el sitio. Hay una documentación más profunda disponible, pero el 99% de lo que necesita se puede ver en los pequeños ejemplos enumerados aquí.
Para este ejemplo, todo lo que realmente queremos hacer es recuperar la información del juego para todos los juegos en la ronda actual (también conocida como "Semana del juego" o "Día del partido"). El punto final /v1/competitions/{id}/fixtures
devuelve esta información, pero para todas las rondas pasadas en la temporada actual. Para obtener la información para una sola ronda, necesitamos establecer el parámetro matchday
, por ejemplo /v1/competitions/{id}/fixtures?matchday=14
.
Para obtener el día actual del partido, podemos solicitar la tabla de la liga, ya que regresa para el día actual del partido por defecto.
Primero, necesitamos inyectar el servicio HttpClient
en nuestro FootballDataService
para utilizar las funciones HTTP de Angular:
import { HttpClient , HttpHeaders } from '@angular/common/http' ;
...
constructor ( private http : HttpClient ) { }
. . .
IMPORTANTE : Agregar una variable privada al constructor de un servicio o componente angular, junto con la declaración específica de tipo TypeScript, es suficiente información para que funcione la magia negra de Angular. El compilador ahora inyectará la instancia apropiada en este servicio, por lo que tiene acceso a él.
Agreguemos una función para recuperar la tabla de la liga (y el día del partido actual) del servidor:
...
getTable ( ) {
return this . http . get ( this . COMPETITION_URL + this . PL_ID + '/leagueTable' ,
{ headers : this . HEADERS } ) ;
}
...
TypeScript lo ayudará con esto, pero la firma del método para el método http.get
de Angular se ve lo siguiente:
/**
* Construct a GET request which interprets the body as JSON and returns it.
*
* @return an `Observable` of the body as an `Object`.
*/
get ( url : string , options ?: {
headers ?: HttpHeaders | {
[ header : string ] : string | string [ ] ;
} ;
observe?: 'body' ;
params?: HttpParams | {
[ param : string ] : string | string [ ] ;
} ;
reportProgress?: boolean ;
responseType?: 'json' ;
withCredentials?: boolean ;
} ) : Observable < Object > ;
Los signos de interrogación denotan que headers
de los parámetros, observe
, params
, reportProgress
, responseType
y withCredentials
son opcionales como parte del objeto opcional para options
. Solo pasaremos valores para la url
y options.headers
.
Es posible que se pregunte qué función getTable()
que acabamos de crear. Bueno, devuelve una corriente Observable
. Los s Observable
son esencialmente, como lo expresaron Luuk Gruijs, "colecciones perezosas de múltiples valores a lo largo del tiempo". Lo que suena loco, pero en realidad es bastante sencillo.
En resumen, una corriente Observable
se considera "fría" hasta que se suscribe, es decir, la variable solo se carga perezosamente una vez que su salida se usa. Una vez que la transmisión esté "caliente", actualizará a todos los suscriptores cada vez que cambie su valor. Es una alternativa al uso Promise
para manejar situaciones de asíncrono en JavaScript.
En este caso, la solicitud GET
solo se disparará una vez que la variable Observable
se suscita ya que la interfaz REST solo devolverá un valor único por llamada.
Podemos usar el poder de la escritura estática en TypeScript para que esto sea más explícito:
...
import { Observable } from 'rxjs/Observable' ;
...
getTable ( ) : Observable < any > {
return this . http . get ( this . COMPETITION_URL + this . PL_ID + '/leagueTable' ,
{ headers : this . HEADERS } ) ;
}
...
De hecho, dado que la documentación de fútbol-data.org nos dice exactamente qué esperar de la llamada de descanso, podemos ir un paso más allá y modelar el objeto también, en src/app/models/leagueTable.ts
:
import { Team } from './team' ;
export class LeagueTable {
leagueCaption : string ;
matchday : number ;
standing : Team [ ] ;
}
Y en src/app/models/team.ts
:
export class Team {
teamName : string ;
crestURI : string ;
position : number ;
points : number ;
playedGames : number ;
home : {
goals : number ;
goalsAgainst : number ;
wins : number ;
draws : number ;
losses : number ;
} ;
away : {
goals : number ;
goalsAgainst : number ;
wins : number ;
draws : number ;
losses : number ;
} ;
draws : number ;
goalDifference : number ;
goals : number ;
goalsAgainst : number ;
losses : number ;
wins : number ;
}
Que nos permite actualizar el football-data.service.ts
a:
import 'rxjs/add/operator/map' ;
import { LeagueTable } from './models/leagueTable' ;
...
getTable ( ) : Observable < LeagueTable > {
return this . http . get ( this . COMPETITION_URL + this . PL_ID + '/leagueTable' ,
{ headers : this . HEADERS } )
. map ( res => res as LeagueTable ) ;
}
...
Esto nos ayudará a mantener nuestra cordura minimizando el modelo mental que necesitamos para mantenerse al día mientras trabajamos con objetos complejos, ya que el IDE puede guiarnos.
Nota al margen: la palabra clave as
simplemente le dice a TypeScript que confíe en nosotros sobre el tipo de objeto, en lugar de tratar de resolverlo a través de algún tipo de inspección. Peligro pero útil, como la mayoría de las cosas interesantes.
Bien, navegue de regreso a src/app/app.component.ts
y agregue las siguientes líneas para inyectar el FootballDataService
en el componente:
import { FootballDataService } from './football-data.service' ;
...
export class AppComponent {
title = 'app' ;
constructor ( private footballData : FootballDataService ) { }
}
Ahora también agregaremos un método ngOnInit
al componente. Este es un gancho de ciclo de vida estándar proporcionado por Angular que se inicia los disparos después de todas las propiedades unidas a los datos de un componente. Básicamente se dispara en la inicialización del objeto, pero ligeramente posterior al método constructor
que dispara antes de que se hayan registrado todas las entradas y salidas al componente.
Una regla general común es siempre colocar el código que desee invocar sobre la inicialización en este método especial ngOnInit
, en lugar del constructor. Agréguelo a un componente como así:
import { Component , OnInit } from '@angular/core' ;
...
export class AppComponent implements OnInit {
...
constructor ( private footballData : FootballDataService ) { }
ngOnInit ( ) {
// Code you want to invoke on initialisation goes here
}
...
En nuestro caso, queremos cargar la tabla de la liga, para que podamos agregar algo como esto:
...
ngOnInit ( ) {
// Load league table from REST service
this . footballData . getTable ( )
. subscribe (
data => console . log ( data ) ,
error => console . log ( error )
) ;
}
...
Si abre la consola en su navegador web, debería ver algo como esto:
Excelente. Ahora tenemos muchos datos geniales con los que podríamos hacer algo en el futuro (no menos menos en enlaces directos a las imágenes de Club Crest), pero por ahora estamos realmente interesados en la jornada actual.
Ahora agregemos otra función a nuestro servicio de datos para obtener la información para la ronda actual de accesorios:
...
import { GameWeek } from './models/gameWeek' ;
...
getFixtures ( matchDay : number ) : Observable < GameWeek > {
return this . http . get ( this . COMPETITION_URL + this . PL_ID + '/fixtures?matchday=' + matchDay , { headers : this . HEADERS } )
. map ( res => res as GameWeek ) ;
}
...
con GameWeek
y Fixture
definidos de la siguiente manera:
// src/app/models/gameWeek.ts
import { Fixture } from './fixture'
export class GameWeek {
count : number ;
fixtures : Fixture [ ] ;
}
// src/app/models/fixture.ts
export class Fixture {
awayTeamName : string ;
date : string ;
homeTeamName : string ;
matchday : number ;
result : {
goalsAwayTeam : number ;
goalsHomeTeam : number ;
halfTime : {
goalsAwayTeam : number ;
goalsHomeTeam : number ;
}
} ;
status : 'SCHEDULED' | 'TIMED' | 'POSTPONED' | 'IN_PLAY' | 'CANCELED' | 'FINISHED' ;
_links : {
awayTeam : { href : string ; } ;
competition : { href : string ; } ;
homeTeam : { href : string ; } ;
self : { href : string ; } ;
} ;
}
Con nuestro conocimiento del día de partido recién ganado, podemos pedirle al servidor REST información sobre la ronda actual de accesorios. Sin embargo, debemos esperar la primera llamada de descanso para completar primero antes de hacer la segunda. Alguna refactorización significa que podemos hacerlo en la devolución de llamada con bastante facilidad:
import { Component , OnInit } from '@angular/core' ;
import { FootballDataService } from './football-data.service' ;
import { LeagueTable } from './models/leagueTable' ;
@ Component ( {
selector : 'app-root' ,
templateUrl : './app.component.html' ,
styleUrls : [ './app.component.css' ] ,
providers : [ FootballDataService ]
} )
export class AppComponent implements OnInit {
title = 'app' ;
table : LeagueTable ;
gameweek : GameWeek ;
constructor ( private footballData : FootballDataService ) { }
ngOnInit ( ) {
this . getTable ( ) ;
}
getTable ( ) {
this . footballData . getTable ( )
. subscribe (
data => {
this . table = data ; // Note that we store the data locally
this . getFixtures ( data . matchday ) ; // Call this function only after receiving data from the server
} ,
error => console . log ( error )
) ;
}
getFixtures ( matchDay : number ) {
this . footballData . getFixtures ( matchDay )
. subscribe (
data => this . gameweek = data , // Again, save locally
error => console . log ( error )
) ;
}
}
¡Ahora estamos llegando a algún lado!
Dado que hemos establecido los datos como una variable miembro en nuestro componente TypeScript, el HTML asociado puede acceder directamente. De hecho, si está utilizando el código de Visual Studio, el editor de código abierto de Microsoft, ¡puede agregar el complemento de servicio de lenguaje Angular para obtener el código JavaScript en HTML ! Asombroso. Y el equipo angular lo mantiene.
Reemplacemos los datos ficticios de antes:
< div class =" container " >
< h1 > Live football scores </ h1 >
< div class =" score-card-list " >
< mat-card class =" score-card " *ngFor =" let fixture of gameweek.fixtures " >
< span class =" home " > {{ fixture.homeTeamName }} </ span >
< span class =" score " > {{ fixture.result.goalsHomeTeam }} : {{ fixture.result.goalsAwayTeam }} </ span >
< span class =" away " > {{ fixture.awayTeamName }} </ span >
</ mat-card >
</ div >
</ div >
NOTA THE gameweek?.fixtures
: ¿El ?
El símbolo actúa como una mano corta para if (gameweek != null) { return gameweek.fixtures } else { return null }
, y es increíblemente útil cuando se accede a variables que solo serán pobladas por llamadas de descanso asíncronas.
Et voila!
Esta siguiente parte no es estrictamente necesaria, pero ilustra una forma angular importante de hacer las cosas, y definitivamente ayudará a mantener nuestro código modular y contenible si decidimos llevarlo adelante (eche un vistazo a NativeScript como una forma de crear un Aplicación móvil nativa con angular y mecanografiado).
Abriremos la tarjeta de puntaje del fijo en su propio componente. Comience con algo de ayuda de la ng generate component score-card
(o ng gc score-card
). Esto creará archivos .ts
, .html
y .css
en src/app/score-card
.
Abra score-card.component.ts
para ser recibido por un decorador familiar:
...
@ Component ( {
selector : 'app-score-card' , // Note this!
templateUrl : './score-card.component.html' ,
styleUrls : [ './score-card.component.css' ]
} )
...
Tenga en cuenta el campo selector
: nos dice cómo acceder al componente (en este caso usando las etiquetas <app-score-card></app-score-card>
).
Refactore nuestro código moviendo la carne de app.component.html
para score-card.component.html
:
<!-- app.component.html -->
< div class =" container " >
< h1 > Live football scores </ h1 >
< div class =" score-card-list " >
< app-score-card *ngFor =" let fixture of gameweek?.fixtures " [fixture] =" fixture " > </ app-score-card >
</ div >
</ div >
<!-- score-card.component.html -->
< mat-card class =" score-card " >
< span class =" home " > {{ fixture.homeTeamName }} </ span >
< span class =" score " >
{{ fixture.result.goalsHomeTeam }} : {{ fixture.result.goalsAwayTeam }}
</ span >
< span class =" away " > {{ fixture.awayTeamName }} </ span >
</ mat-card >
Tenga en cuenta la broca [fixture]="fixture"
dentro de las etiquetas <app-score-card>
. Así es como pasamos información entre componentes.
En la sintaxis angular, [...]
denota entradas, (…)
denota salidas, y [(…)]
denota una unión bidireccional. [(…)]
también se llama "sintaxis de caja de plátano", y la encontrará a menudo en forma de [(ngModel)]="someVariable"
. Esto implica un enlace bidireccional entre el valor de una variable y el valor de un objeto DOM. Esta es una parte clave del uso de Angular.
Por ejemplo, podríamos asignar el valor de una etiqueta input
directamente a una variable que se muestra en la pantalla, y el DOM se actualizaría automáticamente cada vez que cambie el valor del elemento input
:
< p >
What is your name?
< input type =" text " [(ngModel)] =" name " />
</ p >
< p >
Your name: {{ name }}
</ p >
Puede ver un pluntor de ejemplo aquí.
Volver al fútbol: para recibir el valor de entrada en el componente, también necesitamos actualizar score-card.component.ts
de la siguiente manera:
import { Component , Input } from '@angular/core' ;
import { Fixture } from '../models/fixture' ;
@ Component ( {
selector : 'app-score-card' ,
templateUrl : './score-card.component.html' ,
styleUrls : [ './score-card.component.css' ]
} )
export class ScoreCardComponent {
@ Input ( ) fixture : Fixture ; // Note the decorator
constructor ( ) { }
}
Hay dos formas obvias de pasar datos entre componentes: usar @Input
/ @Output
y usar servicios.
@Input()
El primer método es útil para transmitir datos entre padres e hijos, pero puede ser tedioso si los datos deben viajar a través de capas anidadas. Los datos se pueden pasar a un componente infantil utilizando el decorador @Input
. Esta variable de entrada se pasará por referencia si es un objeto, o se pasará por valor si es primitivo.
// someComponent.ts
// ...
import { Input } from '@angular/core'
// ...
export class SomeComponent {
// @Input() variables get set after the `constructor(...)`
// method, but before `ngOnInit()` fires
@ Input ( ) aNumber : number ; // This gets set via parent HTML
// ...
}
<!-- someComponentParent.html -->
< h1 >
I am a parent where SomeComponent gets rendered
</ h1 >
<!-- Pass a value to the aNumber variable -->
< some-component [aNumber] =" 48 " > </ some-component >
@Output()
Los datos también se pueden emitir desde el niño a un componente principal utilizando el decorador @Output()
. Este no es una unión direccional, sino más bien un emisor de eventos que dispara en tiempos predefinidos. Un caso de uso típico notificaría al padre cuando se cambia un valor en el niño.
// someComponent.ts
// ...
import { Input , Output , EventEmitter } from '@angular/core'
// ...
export class SomeComponent {
@ Input ( ) aNumber : number ;
// Emits an event (of type `number`) to the parent
@ Output ( ) numberChanged : EventEmitter < number > = new EventEmitter < number > ( ) ;
// ...
// Event emitters need to be triggered manually
// Any object can be emitted
emitValueChanged ( ) : void {
this . numberChanged . emit ( this . aNumber ) ;
}
}
<!-- someComponentParent.html -->
< h1 >
I am a parent where SomeComponent gets rendered
</ h1 >
<!-- Pass a value to the aNumber variable -->
< some-component [aNumber] =" 48 " (valueChanged) =" aNumberChanged($event) " > </ some-component >
// someComponentParent.ts
export class SomeParentComponent {
// ...
aNumberChanged ( updatedNumber : number ) : void {
console . log ( `aNumber changed to ${ updatedNumber } ` ) ;
}
// ...
}
Uso de servicios
Hay muchas formas de usar servicios para pasar datos entre componentes. Los detalles están más allá del alcance de este tutorial, pero vale la pena señalar la existencia de tales técnicas, ya que probablemente se requerirá en cualquier aplicación relativamente compleja.
El patrón general es definir una secuencia Observable
en el servicio, a la cual los componentes pueden impulsar los mensajes o suscribirse para ser notificados de nuevos mensajes. Este es efectivamente un bus de eventos.
En el caso general, los mensajes deben escribirse para que los oyentes puedan discernir cuáles son aplicables. Como un ejemplo algo frívolo, el servicio podría emitir un PersonNameChangedEvent
que el PersonComponent
podría reaccionar mientras el LandingPageComponent
podría elegir ignorar este evento por completo.
Angular proporciona un marco monolítico y altamente obstinado dentro de las aplicaciones. Esto proporciona la ventaja de la estructura: la naturaleza obstinada del marco ayuda al usuario a diseñar aplicaciones escalables desde el principio, y el desarrollador oculta gran parte de la complejidad. Por otro lado, el uso de Angular (y TypeScript, para el caso) introduce un montón de código básico que podría retrasarlo si está creando una pequeña aplicación, por lo que vale la pena considerar hacia dónde va la aplicación antes de comprometerse con Angular.
Sin embargo, la CLI angular ha recorrido un largo camino, y dado la cantidad de trabajo pesado que probablemente usaré Angular para casi todos los proyectos en el futuro cercano.