o_formulario_de_cadastro_de_foto - VWJavascript/Alurapic GitHub Wiki
Vamos alterar nosso componente CadastroComponent e adicionar um formulário ao seu template, inclusive colocaremos os botões "salvar" e "voltar". Apagando seu conteúdo e adicionando o novo:
alurapic/client/app/cadastro/cadastro.component.html
<div class="container">
<form class="row">
<div class="col-md-6">
<div class="form-group">
<label>Título</label>
<input class="form-control" autocomplete="off">
</div>
<div class="form-group">
<label>URL</label>
<input class="form-control" autocomplete="off">
</div>
<div class="form-group">
<label>Descrição</label>
<textarea class="form-control" autocomplete="off">
</textarea>
</div>
<button type="submit" class="btn btn-primary">
Salvar
</button>
<a [routerLink]="['']" class="btn btn-primary">Voltar</a>
<hr>
</div>
</form>
</div>
Com o servidor rodando, basta recarregarmos nossa página e clicarmos no botão "Nova foto" para vermos nosso formulário de cadastro
Com o formulário pronto, precisamos capturar a entrada do usuário para então pensar na solução de envio para o servidor. Na definição de CadastroComponent vamos adicionar a propriedade foto, um objeto que receberá os dados inseridos pelo usuário:
// alurapic/client/app/cadastro/cadastro.component.ts
import { Component, Input } from '@angular/core';
@Component({
moduleId: module.id,
selector: 'cadastro',
templateUrl: './cadastro.component.html'
})
export class CadastroComponent {
// tipando a propriedade como Object
foto: Object = {};
}
Precisamos agora realizar uma associação dos input's do formulário com CadastroComponent, mais notadamente na propriedade foto. Já aprendemos a realizar data binding, tanto com Expression Language ou com a sintaxe especial que usa o atributo entre colchetes. Para poluir menos a marcação da nossa página, usarei a segunda forma. Mas em qual atributo dos input's? Sabemos essas tag's de entrada guardam seu valor no atributo value. Achamos o alvo da nossa associação:
<!-- alurapic/client/app/cadastro/components/cadastro.html -->
<div class="container">
<form class="row">
<div class="col-md-6">
<div class="form-group">
<label>Título</label>
<input [value]="foto.titulo" class="form-control" autocomplete="off">
</div>
<div class="form-group">
<label>URL</label>
<input [value]="foto.url" class="form-control" autocomplete="off">
</div>
<div class="form-group">
<label>Descrição</label>
<textarea [value]="foto.descricao" class="form-control" autocomplete="off">
</textarea>
</div>
<button type="submit" class="btn btn-primary">
Salvar
</button>
<a [routerLink]="['']" class="btn btn-primary">Voltar</a>
<hr>
</div>
</form>
</div>
Quando recarregamos nossa página, nosso formulário mostra em todos os campos o texto undefined:
Quando nosso formulário é carregado, através da associação de dados ele tenta ler as propriedade titulo, url e descricao do objeto foto do componente Cadastro. No entanto, este objeto não possui essas propriedades o que resulta no valor undefined. Como resolver?
Podemos adicionar essas propriedades no objeto foto de Cadastro e preencher com uma string em branco todos os seus valores:
// alurapic/client/app/cadastro/components/cadastro.ts
import { Component, Input } from '@angular/core';
@Component({
moduleId: module.id,
selector: 'cadastro',
templateUrl: './cadastro.component.html'
})
export class CadastroComponent {
foto: Object = {
titulo: '',
url: '',
descricao: ''
};
}
Muito bom, agora nosso formulário é exibido com todos os campos em branco, mas vamos refletir nessa solução. Primeiro, nosso editor de texto não consegue autocompletar foto por que seu tipo é Object. Além disso, em todo lugar que precisarmos que uma foto tenha sempre um título, url e descrição teremos que lembrar de garantir essa estrutura.
E se conseguíssemos o autocomplete e ainda deixarmos de ter a preocupação de sempre garantir uma estrutura comum para todos os objetos que representam uma foto? E se eu te falar que já temos tudo no lugar para conseguir isso?
Primeiro, vamos dar uma olhada na definição do nosso componente Foto:
// alurapic/client/app/foto/foto.component.ts
import { Component, Input } from '@angular/core';
@Component({
moduleId: module.id,
selector: 'foto',
templateUrl: './foto.component.html'
})
export class FotoComponent {
@Input() titulo: string;
@Input() url: string;
descricao: string;
}
E se no lugar de adicionarmos no objeto foto de CadastroComponent todas as propriedades que uma foto deve ter e passarmos a usar em seu lugar uma instância de FotoComponent? Não precisaríamos nos preocupar com adicionar as propriedades, e ainda poderíamos pegar carona no tipagem do TypeScript e usufruir do autocomplete do nosso editor.
Já adicionamos nos capítulos anteriores a propriedade descricao em FotoComponent. Contudo ela não usou o decorator Input pois não queremos que a descrição seja passada para nosso componente através de .
// alurapic/client/app/foto/foto.component.ts
import { Component, Input } from '@angular/core';
@Component({
moduleId: module.id,
selector: 'foto',
templateUrl: './foto.component.html'
})
export class FotoComponent {
@Input() titulo: string = '';
@Input() url: string = '';
descricao: string = '';
}
Veja que já inicializamos com uma string vazia cada propriedade para evitar que undefined seja exibida no formulário. Agora, vamos alterar CadastroComponent e alterar o tipo da propriedade foto de Object para FotoComponent:
// alurapic/client/app/cadastro/cadastro.component.ts
import { Component, Input } from '@angular/core';
import { FotoComponent } from '../foto/foto.component';
@Component({
moduleId: module.id,
selector: 'cadastro',
templateUrl: './cadastro.component.html'
})
export class CadastroComponent {
foto: FotoComponent = new FotoComponent();
}
Perfeito! Usamos uma instância de Foto que receberá a entrada do usuário pelo nosso formulário. Se, mais tarde, for necessário adicionar mais uma propriedade na classe Foto, só precisaremos acrescentá-la em um lugar e todas as instâncias da classe terão a nova propriedade.
A questão agora é saber se nosso objeto foto em Cadastro está sendo atualizado com os dados inseridos no formulário. Isso é importante, pois precisamos do objeto atualizado para enviá-lo para o servidor.
Sabemos que o envio de dados para o servidor só pode ser feito quando o formulário for submetido. Neste momento, podemos imprimir os dados de foto no console do browser e verificar se foram atualizados para depois pensarmos no seu envio. Mas onde escreveremos esse código?
Lembre-se que um componente é dotado de dados, apresentação e comportamento. Chegou a hora de criarmos o primeiro comportamento do nosso componente, o de cadastrar:
// alurapic/client/app/cadastro/cadastro.component.ts
import { Component, Input } from '@angular/core';
import { FotoComponent } from '../foto/foto.component';
@Component({
moduleId: module.id,
selector: 'cadastro',
templateUrl: './cadastro.component.html'
})
export class CadastroComponent {
foto: FotoComponent = new FotoComponent();
cadastrar() {
console.log(this.foto);
}
}
Adicionamos o método cadastrar na definição da classe Cadastro que apenas exibe o valor da propriedade foto quando chamado. Porém, como nossa aplicação saberá quando chamar o método?