um_novo_tipo - VWJavascript/Alurapic GitHub Wiki
Um novo tipo
Uma das grandes vantagem do TypeScript é justamente o que perdemos agora. Será que podemos resolver nosso problema e ainda usar essas vantagens da linguagem? Sim podemos, mas para isso precisaremos criar um novo tipo, isto é, uma nova classe.
No final do arquivo alurapic/client/app/foto/foto.service.ts vamos criar e exportar outra classe chamada MensagemCadastro.
// alurapic/client/app/foto/foto.service.ts
// código anterior omitido
// veja que `foto.service.ts` agora exporta duas classes, FotoService e MensagemCadastro!
export class MensagemCadastro {
mensagem: string;
inclusao: boolean;
constructor(mensagem: string, inclusao: boolean) {
this.mensagem = mensagem;
this.inclusao = inclusao;
}
}
Nossa classe possui as propriedades mensagem e inclusao e um construtor. Agora, vamos utilizá-la como retorno do método cadastra de FotoService:
// alurapic/client/app/foto/foto.service.ts
// código anterior omitido
cadastra(foto: FotoComponent): Observable<MensagemCadastro> {
if(foto._id) {
return this.http.put(this.url + '/' + foto._id, JSON.stringify(foto),
{ headers: this.headers })
.map(() => new MensagemCadastro('Foto alterada com sucesso', false));
} else {
return this.http.post(this.url, JSON.stringify(foto),
{ headers: this.headers })
.map(() => new MensagemCadastro('Foto incluída com sucesso', true));
}
}
Tanto para a inclusão e alteração passamos para a função map do nosso observable stream uma instância da classe MensagemCadastro que recebe a mensagem específica de cada operação, inclusive indicado se é uma inclusão ou não.
Perfeito! Agora em CadastroComponent, podemos usar o autocomplete porque o TypseScript agora sabe o tipo. Veja que também usamos a mensagem retornada pelo objeto MensagemCadastro.
TypeScript e o favorecimento do encapsulamento
A solução funciona, mas faz sentido podemos alterar a mensagem de MensagemCadastro? Em nosso sistema não faz, mas ninguém impede do programador fazer algo do tipo:
// alurapic/client/app/cadastro/cadastro.component.ts
// código anterior omitido
cadastrar(event) {
event.preventDefault();
console.log(this.foto);
this.service
.cadastra(this.foto)
.subscribe(res => {
res.mensagem = 'Esta é minha nova mensagem'; // não deveria permitir adulterar a mensagem que veio do servidor
this.mensagem = res.mensagem;
this.foto = new FotoComponent();
if(!res.inclusao) this.router.navigate(['']);
}, erro => {
console.log(erro);
this.mensagem = 'Não foi possível savar a foto';
});
}
Isso acontece porque o modificador de acesso padrão de toda propriedade de uma classe é public, podemos até deixar implícito esse modificador padrão em nosso código se assim desejarmos:
// alurapic/client/app/foto/foto.service.ts
// código anterior omitido
export class MensagemCadastro {
public mensagem: string; // tornando explícito o modificador de acesso public
public inclusao: boolean; // tornando explícito o modificador de acesso public
constructor(mensagem: string, inclusao: boolean) {
this.mensagem = mensagem;
this.inclusao = inclusao;
}
}
O primeiro passo para impedirmos que uma mensagem devolvida por FotoService seja adulterada é escondermos as propriedades mensagem e inclusao de todas as outras classes que não seja a própria classe `FotoService. Conseguimos isso através do modificador de acesso private:
// alurapic/client/app/foto/foto.service.ts
// código anterior omitido
export class MensagemCadastro {
private mensagem: string;
private inclusao: boolean;
constructor(mensagem: string, inclusao: boolean) {
this.mensagem = mensagem;
this.inclusao = inclusao;
}
}
Excelente, ninguém mais poderá alterar as propriedades mensagem e inclusao de uma instância de MensagemCadastro, contudo isso nos trouxe um problema. Nosso CadastroComponent precisa consultar as duas propriedades e agora como elas estão encapsuladas em MensagemCadastro o compilador do TypeScript nos dá a seguinte mensagem de erro:
File change detected. Starting incremental compilation...
app/cadastro/cadastro.component.ts(56,37): error TS2341: Property 'mensagem' is private and only accessible within class 'MensagemCadastro'.
app/cadastro/cadastro.component.ts(58,25): error TS2341: Property 'inclusao' is private and only accessible within class 'MensagemCadastro'.
O modificador private
Queremos proibir a escrita nessas propriedades, mas a leitura tem que ser possível, caso contrário todo nosso trabalho foi por água a baixo. Como solucionar? Por padrão, todo método de uma classe também possui modificador public. Que tal criarmos dois métodos públicos que ao serem chamados retornam os valores de mensagem e inclusao respectivamente? Se os métodos fazem parte de MensagemCadastro eles terão acesso às propriedades encapsuladas. Vamos aproveitar e deixar explícito o modificador de acesso public:
// alurapic/client/app/foto/foto.service.ts
// código anterior omitido
export class MensagemCadastro {
private mensagem: string;
private inclusao: boolean;
constructor(mensagem: string, inclusao: boolean) {
this.mensagem = mensagem;
this.inclusao = inclusao;
}
public obterMensagem(): string {
return this.mensagem;
}
public ehInclusao(): boolean {
return this.inclusao;
}
}
Agora, precisamos alterar nosso componente CadastroComponent para que faça uso desses métodos no lugar de acessar as propriedades diretamente:
// alurapic/client/app/cadastro/cadastro.service.ts
// código anterior omitido
cadastrar(event) {
event.preventDefault();
console.log(this.foto);
this.service
.cadastra(this.foto)
.subscribe(res => {
this.mensagem = res.obterMensagem();
this.foto = new FotoComponent();
if(!res.ehInclusao()) this.router.navigate(['']);
}, erro => {
console.log(erro);
this.mensagem = 'Não foi possível savar a foto';
});
}