Os spas se tornaram modernos como a antítese para os sites renderizados pelo servidor. Existem prós e contras, como sempre, mas um spa pode criar uma experiência suave do usuário difícil de replicar sem essa abordagem. Isso faz parte do empreendimento da Web Applications no território que era tradicionalmente ocupado por aplicativos de desktop. Os aplicativos da Web são geralmente criticados por serem lentos em comparação com os aplicativos de desktop, mas grandes avanços nas tecnologias da Web (principalmente o mecanismo V8 do NodeJS e do Google, além de estruturas como Angular e Reactjs), bem como o acesso ao mainstream ao poder de computação sem precedentes significa que isso é muito menos um problema do que costumava ser.
O SPAs funciona carregando uma única página HTML no cliente e atualizando o conteúdo (e o DOM) dinamicamente - em vez de recuperar uma página HTML atualizada do servidor sempre que o usuário clicar em qualquer lugar.
NodeJS
e npm
seguindo as instruções apropriadas aqui.TypeScript
daqui.npm install -g @angular/cli
ng new my-project
cd my-project && ng serve
localhost:4200
em seu navegador da web. Se você abrir my-project/src/app/app.component.ts
você deve ser recebido com algo como o seguinte:
/** 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
}
Vamos quebrar um pouco isso:
Component
do módulo @angular
localizado na pasta node_modules
.@Component
marca uma classe como um componente angular. O decorador é parametrizado com um objeto de opções, do qual utilizaremos apenas alguns parâmetros.selector
define quais serão as tags HTML para esse componente-por exemplo, este será injetado no HTML usando <app-root> … </app-root>
.templateUrl
leva um argumento de string que aponta para o arquivo html que serve como parte da visualização do componente. Você também pode usar o parâmetro template
para gravar HTML diretamente, em vez de apontar para um arquivo. Isso geralmente não é recomendado, a menos que a parte de visualização seja simplesmente uma ou duas linhas.styleUrls
leva uma lista de strings, onde cada string é um caminho para um arquivo CSS.title
pode ser referenciado em app.component.html
. Navegue para app.component.html
. Este é o ponto de entrada para o nosso aplicativo e o HTML de nível mais alto que você deve editar. Tudo será renderizado nesse contexto.
Agora excluiremos o conteúdo deste arquivo e o substituiremos pelo seguinte:
< 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 >
Volte para o navegador da web e você verá que a página é atualizada automaticamente. Ótimo.
Visualmente, você provavelmente ficará desapontado nesta fase, mas é um trabalho em andamento.
O material angular é uma biblioteca mantida por uma equipe do Google para fornecer componentes de design de material fáceis de usar para aplicações angulares. O estilo desses componentes pode ser aprimorado no conteúdo do seu coração, mas também fornece uma maneira fácil de atualizar a aparência de um aplicativo de protótipo com um esforço mínimo.
Vamos instalar as bibliotecas necessárias com npm install --save @angular/material @angular/cdk @angular/animations
. Isso baixa os arquivos necessários na sua pasta node_modules
e atualiza o arquivo packages.json
adequadamente.
Você também terá que dizer ao Angular para carregar os componentes relevantes. Você terá que fazer um pouco de pesquisa no Google sobre como isso funciona (esse é um tipo de tutorial de fita de ducto 101), mas basicamente você só precisa modificar o arquivo app.modules.ts
para incluir os módulos necessários.
Primeiro, adicione as animações como assim:
import { BrowserAnimationsModule } from '@angular/platform-browser/animations' ;
@ NgModule ( {
...
imports : [ BrowserAnimationsModule ] ,
...
} )
Em seguida, adicione importar os módulos de que precisamos:
import { MatButtonModule , MatCardModule } from '@angular/material' ;
@ NgModule ( {
...
imports : [ MatButtonModule , MatCardModule ] ,
...
} )
Esteja ciente de que você precisará importar os componentes para usar em seu aplicativo. O motivo para não incluir toda a biblioteca é ajudar webpack
a realizar tremendo de árvores, o que basicamente implica deixando de fora peças de código não utilizadas ao agrupar todo o nosso código em alguns arquivos minificados .js
.
Quando isso for feito, teremos acesso a um monte de componentes predefinidos e você poderá simplesmente importá -los conforme necessário. Também vale a pena notar que os temas pré -construídos são fáceis de usar.
No angular, os componentes são modulares. Cada componente possui seus próprios arquivos CSS, que se aplicam apenas a essa visualização específica. A única exceção são o arquivo styles.css
encontrado no diretório src
, que se aplica globalmente.
O estilo não é um foco deste tutorial, então tentaremos evitá -lo o máximo possível. Para se sentir um pouco melhor sobre o que estamos fazendo, aqui estão alguns CSs simples para copiar e colar o arquivo styles.css
(melhor prática seria adicionar bits relevantes aos arquivos CSS dos componentes apropriados, para referência 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 ;
}
Atualize também seu HTML para incluir as classes:
...
< 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 >
...
Se você está confuso sobre flex
, recomendo verificar o Flexbox Froggy.
Uma das vantagens de usar uma estrutura como o Angular é que podemos implementar alguma lógica de programação direta no HTML. No nosso caso, os loops para a vida facilitarão a vida.
Experimente o seguinte:
...
< 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 >
...
Isso introduz uma gama de novos conceitos e deve produzir algo como o seguinte:
Conceitos a serem observados:
{{ 'Some text = ' + a_number }}
Os serviços são usados para abstrair o acesso a dados dos componentes. Isso permite que os componentes permaneçam magros e focados em apoiar a visualização. O teste de unidade e a manutenção do código são simplificados mantendo essa separação de responsabilidade.
Para gerar um serviço usando a CLI, você pode usar ng generate service football-data
(ou ng gs football-data
para pessoas com pouco tempo). Isso criará dois novos arquivos no seu diretório project-dir/src/app
: football-data.service.ts
e football-data.service.spec.ts
. Isso segue a Convenção Angular pela qual os arquivos de teste obtêm a extensão .spec.ts
. Embora os testes sejam úteis e importantes, ele fica fora do escopo imediato deste tutorial para que você possa ignorá -lo no momento (desculpe, @Cornel).
Adicione as seguintes linhas ao 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 ( ) { }
}
Os serviços são anotados com @Injectable
, que diz ao compilador que as instâncias do serviço podem ser injetadas em componentes. Você também precisa especificar um provedor para cada serviço. O provedor fornece efetivamente um singleton no nível especificado.
Por exemplo, você pode adicionar seu serviço à matriz de provedores em app.module.ts
, o que resultará em uma instância singleton do serviço exposto em todo o aplicativo. Caso contrário, você poderá adicioná -lo à lista de um componente dos provedores, o que resultará em uma instância singleton disponível para esse componente e quaisquer componentes em seu contexto.
AVISO: Torna -se fácil criar dependências circulares entre os serviços quando seu aplicativo crescer. Esteja atento a isso. Se (quando) você recebe uma mensagem de erro que se parece com aquilo Cannot resolve all parameters for MyDataService(?)
, Provavelmente está relacionado a problemas de dependência circular.
Estaremos usando a API fantástica disponível livremente em futebol data.org. Você precisará obter sua própria chave de API, mas é fácil de fazer - basta seguir as instruções no site. Há uma documentação mais aprofundada disponível, mas 99% do que você precisa pode ser visto nos pequenos exemplos listados aqui.
Para este exemplo, tudo o que realmente queremos fazer é recuperar as informações do jogo para todos os jogos na rodada atual (também conhecida como "semana do jogo" ou "dia da partida"). O Endpoint /v1/competitions/{id}/fixtures
retorna essas informações, mas para todas as rodadas anteriores na temporada atual. Para obter as informações para uma única rodada, precisamos definir o parâmetro matchday
, por exemplo /v1/competitions/{id}/fixtures?matchday=14
.
Para obter o dia atual da partida, podemos pedir a tabela da liga, pois ele retorna para o dia atual da partida por padrão.
Primeiro, precisamos injetar o serviço HttpClient
em nosso FootballDataService
para usar as funções HTTP do Angular:
import { HttpClient , HttpHeaders } from '@angular/common/http' ;
...
constructor ( private http : HttpClient ) { }
. . .
IMPORTANTE : Adicionar uma variável privada ao construtor de um serviço ou componente angular, juntamente com a declaração específica do tipo datilografado, é informações suficientes para que a magia negra do Angular funcione. O compilador agora injetará a instância apropriada neste serviço, para que você tenha acesso a ele.
Vamos adicionar uma função para recuperar a tabela da liga (e o dia atual da partida) do servidor:
...
getTable ( ) {
return this . http . get ( this . COMPETITION_URL + this . PL_ID + '/leagueTable' ,
{ headers : this . HEADERS } ) ;
}
...
O TypeScript o ajudará com isso, mas a assinatura do método para o método http.get
do Angular parece o seguinte:
/**
* 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 > ;
Os pontos de interrogação indicam que os parâmetros headers
, observe
, params
, reportProgress
, responseType
e withCredentials
são todos opcionais como parte do objeto opcional para options
. Estaremos apenas passando valores para o url
e options.headers
.
Você pode estar se perguntando o que a função getTable()
acabamos de criar o retorno. Bem, ele retorna um fluxo Observable
. Observable
S são essencialmente, como Luuk Gruijs disse, "coleções preguiçosas de vários valores ao longo do tempo". O que parece louco, mas na verdade é bastante direto.
Em resumo, um fluxo Observable
é considerado "frio" até que seja inscrito - ou seja, a variável é carregada apenas com preguiça quando sua saída for usada. Depois que o fluxo estiver "quente", ele atualizará todos os assinantes sempre que seu valor mudar. É uma alternativa ao uso de Promise
para lidar com situações assíncronas no JavaScript.
Nesse caso, a solicitação GET
será disparada apenas assim que a variável Observable
for assinada, pois a interface restante retornará apenas um único valor por chamada.
Podemos usar o poder da digitação estática no TypeScript para tornar isso mais 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 fato, como a documentação do futebol Data.org nos diz exatamente o que esperar da chamada de resto, podemos dar um passo adiante e modelar o objeto também, no src/app/models/leagueTable.ts
:
import { Team } from './team' ;
export class LeagueTable {
leagueCaption : string ;
matchday : number ;
standing : Team [ ] ;
}
E no 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 ;
}
O que nos permite atualizar o football-data.service.ts
para:
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 ) ;
}
...
Isso nos ajudará a manter nossa sanidade, minimizando o modelo mental de que precisamos manter -se atualizados enquanto trabalhamos com objetos complexos, pois o IDE pode nos guiar.
Nota lateral: a palavra -chave as
simplesmente diz a TypeScript para confiar em nós sobre o tipo de objeto, em vez de tentar descobrir isso por meio de algum tipo de inspeção. Perigoso, mas útil, como as coisas mais interessantes.
Ok, volte para src/app/app.component.ts
e adicione as seguintes linhas para injetar o FootballDataService
no componente:
import { FootballDataService } from './football-data.service' ;
...
export class AppComponent {
title = 'app' ;
constructor ( private footballData : FootballDataService ) { }
}
Agora, também adicionaremos um método ngOnInit
ao componente. Este é um gancho de ciclo de vida padrão fornecido pelo Angular que dispara após a inicialização de todas as propriedades ligadas a dados de um componente. Basicamente, dispara na inicialização do objeto, mas um pouco mais tarde do método constructor
que dispara antes que todas as entradas e saídas do componente fossem registradas.
Uma regra geral é apenas sempre colocar o código que você deseja invocar sobre a inicialização neste método ngOnInit
especial, em vez do construtor. Adicione -o a um componente assim:
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
}
...
No nosso caso, queremos carregar a tabela da liga, para que possamos adicionar algo assim:
...
ngOnInit ( ) {
// Load league table from REST service
this . footballData . getTable ( )
. subscribe (
data => console . log ( data ) ,
error => console . log ( error )
) ;
}
...
Se você abrir o console no seu navegador da web, verá algo assim:
Ótimo. Agora, temos muitos dados legais com os quais poderíamos fazer algo no futuro (não menos importante, links diretos para imagens do Club Crest), mas por enquanto estamos realmente interessados na jornada atual.
Vamos agora adicionar outra função ao nosso serviço de dados para obter as informações para a rodada atual de acessórios:
...
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 ) ;
}
...
Com GameWeek
e Fixture
definido da seguinte forma:
// 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 ; } ;
} ;
}
Com o nosso conhecimento recém -adquirido no dia da partida, podemos pedir ao servidor restante informações sobre a rodada atual de acessórios. No entanto, precisamos esperar a primeira chamada de descanso para concluir primeiro antes de fazer o segundo. Alguns refatoramentos significa que podemos fazê -lo no retorno de chamada com bastante facilidade:
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 )
) ;
}
}
Agora estamos chegando a algum lugar!
Como definimos os dados como uma variável de membro em nosso componente TypeScript, eles podem ser acessados diretamente pelo HTML associado. De fato, se você estiver usando o código do Visual Studio, o editor de código aberto da Microsoft, poderá adicionar o plug-in de serviço de idioma angular para obter a conclusão do código JavaScript no HTML ! Incrível. E é mantido pela equipe angular.
Vamos substituir os dados fictícios 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 >
Observe a Sintaxe da gameweek?.fixtures
: The ?
O símbolo atua como uma mão curta para if (gameweek != null) { return gameweek.fixtures } else { return null }
, e é inacreditavelmente útil ao acessar variáveis que só serão preenchidas por chamadas de descanso assíncronas.
Et pronto!
Esta próxima parte não é estritamente necessária, mas ilustra uma maneira angular importante de fazer as coisas, e definitivamente ajudará a manter nosso código modular e contêneas se decidirmos levá -lo adiante (dê uma olhada no NativeScript como uma maneira de criar um aplicativo móvel nativo com angular e datilografado).
Vamos abrachar o cartão de pontuação do dispositivo ao seu próprio componente. Comece com alguma ajuda da CLI: ng generate component score-card
(ou ng gc score-card
). Isso criará arquivos .ts
, .html
e .css
no src/app/score-card
.
Abra score-card.component.ts
a ser recebido por um decorador familiar:
...
@ Component ( {
selector : 'app-score-card' , // Note this!
templateUrl : './score-card.component.html' ,
styleUrls : [ './score-card.component.css' ]
} )
...
Observe o campo selector
-ele nos informa como acessar o componente (neste caso usando tags <app-score-card></app-score-card>
).
Refactor nosso código movendo a 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 >
Observe o bit [fixture]="fixture"
dentro das tags <app-score-card>
. É assim que passamos informações entre os componentes.
Na sintaxe angular, [...]
denota entradas, (…)
denota saídas e [(…)]
denota uma ligação de mão dupla. [(…)]
Também é chamado de "sintaxe da caixa de banana" e você a encontrará frequentemente na forma de [(ngModel)]="someVariable"
. Isso implica uma ligação bidirecional entre o valor de uma variável e o valor de um objeto DOM. Esta é uma parte essencial do uso do Angular.
Por exemplo, poderíamos mapear o valor de uma tag input
diretamente para uma variável que é exibida na tela, e o DOM seria atualizado automaticamente sempre que o valor do elemento input
muda:
< p >
What is your name?
< input type =" text " [(ngModel)] =" name " />
</ p >
< p >
Your name: {{ name }}
</ p >
Você pode conferir um exemplo de plunker aqui.
De volta ao futebol: para receber o valor de entrada no componente, também precisamos atualizar score-card.component.ts
da seguinte forma:
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 ( ) { }
}
Existem duas maneiras óbvias de passar dados entre componentes: usando @Input
/ @Output
e usando serviços.
@Input()
O primeiro método é útil para transmitir dados entre pais e filhos, mas pode ser tedioso se os dados precisarem viajar por camadas aninhadas. Os dados podem ser passados para um componente filho usando o decorador @Input
. Essa variável de entrada será passada por referência se for um objeto, ou passada por valor, se for um 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()
Os dados também podem ser emitidos da criança para um componente pai usando o decorador @Output()
. Esta não é uma ligação direcional, mas um emissor de eventos que dispara em tempos predefinidos. Um caso de uso típico seria notificar o pai quando um valor é alterado na criança.
// 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 } ` ) ;
}
// ...
}
Usando serviços
Existem muitas maneiras de usar serviços para passar dados entre componentes. Os detalhes estão além do escopo deste tutorial, mas vale a pena notar a existência de tais técnicas, porque provavelmente será necessário em qualquer aplicativo relativamente complexo.
O padrão geral é definir um fluxo Observable
no serviço, para o qual os componentes podem empurrar mensagens ou se inscrever para ser notificado de novas mensagens. Este é efetivamente um barramento de eventos.
No caso geral, as mensagens precisam ser digitadas para que os ouvintes sejam capazes de discernir, aplicáveis. Como um exemplo um tanto frívolo, o serviço pode emitir um PersonNameChangedEvent
, no qual o PersonComponent
poderia reagir enquanto o LandingPageComponent
pode optar por ignorar completamente esse evento.
A Angular fornece uma estrutura monolítica e altamente opinativa para criar aplicativos. Isso fornece a vantagem da estrutura - a natureza opinativa da estrutura ajuda o usuário a projetar aplicativos escaláveis desde o início, e grande parte da complexidade está oculta do desenvolvedor. Por outro lado, o uso do Angular (e o TypeScript, para esse assunto) apresenta um monte de código de caldeira que pode diminuir o atraso se você estiver criando uma pequena aplicação, por isso vale a pena considerar para onde o aplicativo está indo antes de se recompor ao Angular.
O CLI angular percorreu um longo caminho, no entanto, e dado o quanto o levantamento pesado, provavelmente usarei o Angular para quase todos os projetos em um futuro próximo.