o_formulario_de_cadastro_de_foto - VWJavascript/Alurapic GitHub Wiki

O formulário de cadastro de foto

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

Data binding resolve?

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:

image

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?

⚠️ **GitHub.com Fallback** ⚠️