Solucionado (ver solução)
Solucionado
(ver solução)
5
respostas

Auxilio aos colegas - Validação =)

Olhando os feedbacks até aqui, não é segredo pra ninguém, como este curso é especial... Para ajudar a compreender melhor a lógica da validação, segue os pontos importantes:

1) Quando a view é carregada, tenha em mente que as validações já estão definidas na instancia do FormValidator.js

class FormValidator {

    constructor(validacoes){//elas chegam pelo construtor
        this.validacoes = validacoes;

        console.log(this.validacoes);//p/ constatação do q se fala        
    }

2) Se vc não lembra quais são, observe q elas são definidas em Formulario.js

this.validador = new FormValidator([
            {
                campo:'nome',
                metodo:'isEmpty',
                validoQuando: false,
                mensagem: 'Preencha o campo Nome'
            },
            {
                campo:'livro',
                metodo:'isEmpty',
                validoQuando: false,
                mensagem: 'Preencha o campo Livro'
            },
            {
                campo:'preco',
                metodo:'isInt',
                args: [{min:0, max:99999}],
                validoQuando: true,
                mensagem: 'Preencha o campo Preço com valor numérico'
            }
        ]);

3) Blz. Compreenda que nos nossos campos do formulário, há o evento onChange() associado. Ou seja, a cada dado informado nos campos, um metodo é chamado

//classe Formulario.js . Olhe dentro do metodo render()!

<input
                            className="validate"
                            id="nome"
                            type="text"
                            name="nome"
                            value={nome}
                            onChange={this.escutadorDeInput}
                        />

4) Este metodo chama-se 'escutadorDeInput' e ele recebe um evento. Por meio do event.target, descobrimos qual elemento do DOM o disparou. Ex: Eu posso está preenchendo Nome e o event.target está retornando por debaixo dos panos:

<input id="nome" type="text" name="nome" value="Josh">

5) do event.target, o que me interessa somente é as propriedades name e value.(name = nome do elementoHTML / value = valor digitado pelo usuario.) Eu passo tais infos p/ uma constante:

const { name, value } = event.target;

6) Finalizando o entendimento do metodo 'escutadorDeInput', ainda temos a seguinte instrução:

this.setState({
     [name]: value
});

6.1) Lembra que temos o state associado ao formulario? Pois então, recorde tbm que a unica forma de atualizar os dados contidos dentro de um state é via .setState({informando o atributo : "valor a ser alterado"}). 6.2) [name] sempre equivale a um campo de nosso state. Ex: [name] : value é o mesmo que dizer atributo : "valor". Para ilustração, seria:

this.setState({
    [nome]: "Josh"//altere o valor do atributo nome contido no state!
});

6.3) Se vc não lembra em que momento o state foi definido, observe seu arquivo Formulario.js, na parte do constructor!

 this.stateInicial = {
            nome: '',
            livro: '',
            preco: '',
            validacao: this.validador.valido()
        }

        this.state = this.stateInicial;

Continua no próximo post...

5 respostas

7) Após terminamos de preencher o nosso formulario, antes de clicarmos no botao salvar, o state está guardando os estados atuais de cada propriedade (nome, livro, preco). Se algum estiver vazio, vc verá nele:

state = {
    nome: 'Josh',
    livro: 'React'
    preco: '',
    validacao: {(...)}
}

8) Agora, vc clicou no botao Salvar. O metodo submitFormulario será chamado

submitFormulario = () => {
        const validacao = this.validador.valida(this.state);

        if(validacao.isValid){
            this.props.escutadorDeSubmit(this.state);
            this.setState(this.stateInicial);
        }else{
            const {nome, livro, preco} = validacao;
            const campos = [nome, livro, preco];

            const camposInvalidos = campos.filter(elemento => {
                return elemento.isInvalid;
            });

            camposInvalidos.forEach(campo => PopUp.exibeMensagem('error', campo.message));
        }

8.1) Na 1ª instrução do metodo, temos a chamada ao valida(). Observe quem passamos como parâmetro: o state! Lembre-se tbm que o validador são as regras definidas no momento da instanciação do FormValidator (vide ponto 2).

const validacao = this.validador.valida(this.state);

9) O metodo valida() está definido em FormValidator.js. Vamos analisa-lo ponto a ponto:

valida(state) {
    let validacao = this.valido();
    (...)
}

9.1)Sua 1ª instrução aponta p/ a chamada a outro metodo: this.valido(). É necessário irmos até ele para entender melhor. Observe abaixo, a parte do codigo contido em valido().

valido(){
        const validacao = {}
    this.validacoes.map(function(regra) {
            return validacao[regra.campo] = {isInvalid: false, message: ''}
        });
    return {isValid: true, ...validacao};

10) Eu pego validacoes (as regras estao ai) e dou um map nela (percorro cada item). Em cada item de validações, eu pego somente a propriedade nome (representado por: validacao[regra.campo], e atribuo a ela um valor que será composto por dois atributos! isInvalid:false e message:". Se não deu pra compreender mto bem, observe o que é gerado após esta iteração:

/*Resultado do map: um novo array contendo 3 JSON, a qual seu unico atributo é composto por outro JSON
        validacao = 
            {
                nome: {
                    isInvalid: false,
                    message: ''
                } 
            },
            {
                livro: {
                    isInvalid: false,
                    message: ''
                } 
            },
            {
                preco: {
                    isInvalid: false,
                    message: ''
                } 
            }

10.1) Quando eu chego no return de valido()...

 return {isValid: true, ...validacao};

o que ele faz é somente acrescentar isValid: true... ilustrando:

[
            {
                isValid: true
            },
            {
                nome: {
                    isInvalid: false,
                    message: ''
                } 
            },
            {
                livro: {
                    isInvalid: false,
                    message: ''
                } 
            },
            {
                preco: {
                    isInvalid: false,
                    message: ''
                } 
            },
        ]  

Continuando...

11) Voltando ao temivel método valida(), a sua segunda instrução é o forEach:

this.validacoes.forEach(regra => {   

12) O forEach irá percorrer cada regra que vimos no ponto 2 deste topico! A próxima instrução é:

const campoValor = state[regra.campo.toString()];

13) No campoValor, eu pego o valor gravado no state! Se for mais fácil compreender pra você, eu pego os dados digitados pelo usuario no input[value] presentes no state até o momento do clique no botão salvar. Passo p/ String, pois validator so trabalha com String.

Ex.: const campoValor = "Josh"

14) A próxima instrução é a do args.

const args = regra.args || [];

14.1) Recebe array vazio caso ñ haja args definidas p/ este campo OU recebe um array preenchido (somente no caso do campo em questao ser preco

Ex.: const args = [] // qdo regra se referir aos campos nome e livro
    const args = [{min:0, max:99999}] // qdo regra se referir a preco

15) Próxima instrução é referente a constante metodoValidacao:

const metodoValidacao = typeof regra.metodo === 'string' ? validador[regra.metodo] : regra.metodo;

15.1) Nela há definida um if ternário! Em miúdos se traduz assim:

'?' = então    ':' = senão    []onde tiver colchete, substitui c/ a regra.metodo definida
Se o tipo de regra.metodo equivale a uma 'string'... 
ENTAO, retorne validador.isEmpty ou validador.isInt...
SENAO, retorna regra.metodo direto! 

15.2) Colegas. A const metodoValidacao irá guardar a estrutura de uma função. Se vc adicionar um simples console.log(metodoValidacao) após esta instrução, verás na sua devops do chrome printado uma função. (abaixo está o exemplo de qual função fica reservada em metodoValidacao, quando a regra está relacionada aos campos nome e livro)

function isEmpty(str, options) {
  (0, _assertString.default)(str);
  options = (0, _merge.default)(options, default_is_empty_options);
  return (options.ignore_whitespace ? str.trim().length : str.length) === 0;
}

16) A proxima instrução do método valida() é:

if(metodoValidacao(campoValor, ...args, state) !== regra.validoQuando){

16.1) Imagine que no loop do forEach, encontramos no primeiro loop, as regras referentes ao campo nome. Você deve concordar comigo que o metodo de validação a ser usada é a do isEmpty certo? Olhando o ponto 15 deste topico... vemos que metodoValidacao representa esta função . Trocando em miudos, por debaixo dos panos acontece isto:

if(isEmpty("Josh", ...args, state) !== regra.validoQuando){

Se fosse preco...

if(isInt('10', [{min:0, max:99999}], state) !== regra.validoQuando){

16.2) Observe tbm que eu levo em consideração a regra.validoQuando... Afinal se isEmpty retornar true, é sinal que tá vazio o campo Nome ou Livro, e se isInt retornar true, é sinal que o valor passado pelo usuario é valido, TODAVIA, o que me interessa neste if, é se o mesmo é invalido.

//Campo nome preenchido pelo usuario... 'Josh'
isEmpty("josh", outros parametros) != false //pula este if!

//Campo nome em branco e o usuario clicou em salvar...
isEmpty("", outros parametros) != false //entra neste if, pq o valor do campo esta vazio!

//O mesmo vale pra Livro...

//Para Preco, seria + ou - assim... OBSERVE QUE AQUI, validaQuando É TRUE!!
//Campo preco preenchido pelo usuario... '100'
isInt("100", [{min:0, max:99999}], outros parametros) != true//pula este if!

//Campo preco preenchido pelo usuario... '100Alura'
isInt("100", [{min:0, max:99999}], outros parametros) != true //entra neste if, pq o usuario ñ informou SOMENTE numeros!

//Campo preco preenchido pelo usuario... ''
isInt("100", [{min:0, max:99999}], outros parametros) != true //entra neste if, pq o usuario nada!

16.3) Bem, se constatado alguma inconsistencia, passamos p/ proxima instrução:

validacao[regra.campo] = {
       isInvalid: true,
       message: regra.mensagem
}

16.4) Eu nada +, nada -, modifico os valores das propriedades presentes na validacao. Lembra do estado anterior da validacao, a qual obtivemos do metodo valido()?

        {
                isValid: true
            },
            {
                nome: {
                    isInvalid: false,
                    message: ''
                } 
            },
            {
                livro: {
                    isInvalid: false,
                    message: ''
                } 
            },
            {
                preco: {
                    isInvalid: false,
                    message: ''
                } 
            },

16.5) Imagine que preco e livro foram passados com conteudos vazios. Seu resultado seria:

{
                isValid: true
            },
            {
                nome: {
                    isInvalid: false,
                    message: ''
                } 
            },
            {
                livro: {
                    isInvalid: true,
                    message: 'Preencha o campo Livro'
                } 
            },
            {
                preco: {
                    isInvalid: true,
                    message: 'Preencha o campo Preco'
                } 
            },

17) Por fim, na penultima instrução temos...

validacao.isValid = false;

Que sobrescreve o isValid presente em validacao. olhe de novo no 16.5, e imagine a troca do valor isValid:true para isValid:false =)

18)Por Fim... Retorno validacao! O 16.5 somado c/ a dica 17 em questao.

19) Voltando ao submitFormulario() - Formulario.js, citado no ponto 8... Temos a proxima instrução:

if(validacao.isValid){
            this.props.escutadorDeSubmit(this.state);
            this.setState(this.stateInicial);
        }

Ora... Se tudo ok... tudo preenchido pelo usuario dentro dos conformes... Eu chamo o metodo passado como propriedade ao Formulario this.props.escutadorDeSubmit(this.state) , devolvendo o estado atual do meu novo Autor! Encerrando este IF, em this.setState(this.stateInicial);, state atual tem seus valores apagados

state = {nome:"Josh", livro: "devops", preco: "400", validacao: {..}}
this.stateInicial = {
            nome: '',
            livro: '',
            preco: '',
            validacao: this.validador.valido()
        }

20) Em App.js, na instrução this.setState({autores : [...this.state.autores, novoAutor]}), eu pego tds os dados anteriores + novoAutor e insiro na propriedade autores. Por fim, finalizo com a msg de confirmação.

//Arquivo App.js

escutadorDeSubmit = novoAutor => {
    this.setState({autores : [...this.state.autores, novoAutor]})
    PopUp.exibeMensagem('success', "Autor adicionado com sucesso");
  }

21) Voltando ao submitFormulario() - Formulario.js, finalizando com o else, filtro aqueles campos, cujo atributo isInvalid contenha valor true. Como filter retorna um array, percorro este no forEach, gerando a msg pra cada um.

}else{
            const {nome, livro, preco} = validacao;
            const campos = [nome, livro, preco];

            const camposInvalidos = campos.filter(elemento => {
                return elemento.isInvalid;
            });
            camposInvalidos.forEach(campo => PopUp.exibeMensagem('error', campo.message));
        }

Se levarmos em consideração o exemplo do 16.5, livro e preco recairiam neste else =)

{
                isValid: true
            },
            {
                nome: {
                    isInvalid: false,
                    message: ''
                } 
            },
            {
                livro: {
                    isInvalid: true,
                    message: 'Preencha o campo Livro'
                } 
            },
            {
                preco: {
                    isInvalid: true,
                    message: 'Preencha o campo Preco'
                } 
            },
solução!

É isso colegas... Passei uma tarde batendo cabeça tentando compreender. Acho que consegui.. Qualquer coisa, caso haja algum erro, me desculpem... Eu procurei ajudar num ponto do curso que gerou muito desconforto por parte de quem ver React pela 1ª vez. Sem mais... Bons estudos p/ cada um de nós =)