Novo pacote de validação para o React no Mobx @ quantumart / mobx-form-validation-kit

Boa tarde
Hoje eu quero falar sobre um novo pacote para validações assíncronas em projetos baseados em React , Mobx e escritos em Typescript .
O desenvolvimento do Frontend moderno envolve uma grande quantidade de lógica ao preencher páginas com documentos, questionários e documentos para um empréstimo, ordens de pagamento, páginas de registro no site. O principal ônus lógico recai sobre as verificações de validação. Os desenvolvedores angulares pensaram sobre esse ponto e ofereceram aos desenvolvedores o uso do mecanismo FormControl interno para esses fins, que, embora tenha várias desvantagens, ainda é melhor do que a ausência completa de uma solução no React . A situação é complicada pelo fato de que a tendência atual do desenvolvimento do React envolve o uso de mobx para organizar a lógica de negócios.
Diante desses problemas, resolvemos todos eles escrevendo um pacote: @ quantumart / mobx-form-validation-kit

Vantagens do pacote:
  • Inteiramente no TypeScript
  • Compatível com Mobx (versão 4, que suporta todos os favoritos, IE10)
  • Projetado para funcionar em React (pode ser usado em projetos sem reagir)
  • Projetado para validação assíncrona
  • Fácil de incorporar em um projeto existente.

Instruções para trabalhar com a embalagem sob o corte.


No início, descreveremos a funcionalidade do pacote @ quantumart / mobx-form-validation-kit , no final do artigo, escreveremos uma página totalmente funcional com um exemplo do formulário de inscrição no site.

Formcontrol


O @ quantumart / mobx-form-validation-kit permite criar uma camada entre os dados de origem e o formulário a ser exibido. O que, por sua vez, permite validá-los e, se necessário, alterar os dados antes que eles cheguem ao objeto original.

A biblioteca @ quantumart / mobx-form-validation-kit contém três classes principais (componentes de validação) para gerenciar o formulário:
  • FormGroup - permite combinar componentes de validação. A classe é digitada e permite refazer a interface com a lista de campos como um parâmetro genérico. Qualquer um é registrado por padrão, é altamente recomendável não usá-lo sem digitar, mas existe uma possibilidade.
  • FormControl - usado para validar um campo específico, a classe mais usada. A classe é digitada e assume como parâmetro genérico o tipo da variável que deve armazenar. Por padrão, a string é registrada, porque o padrão é string, como a opção mais privada para formulários.
  • FormArray - permite criar e gerenciar uma matriz de componentes de validação.

Além disso, existem classes abstratas básicas
  • AbstractControl - a classe base para todas as classes de validação listadas, não digitadas.
  • FormAbstractControl - classe base para FormGroup e FormArray , não digitado.
  • FormAbstractGroup - não uma classe base digitada para FormControl, contém um link para o elemento html que está sendo renderizado.

A melhor prática para criar um formulário de validação seria a seguinte idéia.
Um objeto do tipo um FormGroup é criado no formulário e os campos já estão listados nele
this.form = new FormGroup<IUserInfo>({ name: new FormControl( this.userInfo.name, [], v => (this.userInfo.name = v) ), surname: new FormControl( this.userInfo.surname, [], v => (this.userInfo.surname = v) ) // … }); 

O FormGroup suporta aninhamento, ou seja,
 this.form = new FormGroup<IUserInfo>({ name: new FormControl( this.userInfo.name, [], v => (this.userInfo.name = v) ), surname: new FormControl( this.userInfo.surname, [], v => (this.userInfo.surname = v) ) passport: new FormGroup<IPassport >({ number: new FormControl( this.userInfo.passport.number, [], v => (this.userInfo.passport.number = v) ), // … }) // … }); 

Você pode adicionar o FormArray , que, por sua vez, pode passar o tipo FormControl e / ou o FormGroup inteiro, criando objetos de qualquer complexidade e estrutura.
  • FormArray <FormControl>
    FormArray <FormGroup>

    O próprio FormControl leva o seguinte conjunto de parâmetros para o construtor
    • value : TEntity é o valor inicial digitado.
    • validators : ValidatorFunctionFormControlHandler [] - um conjunto de validadores.
    • callbackValidValue : UpdateValidValueHandler | null - função de retorno de chamada para a qual o último valor válido é passado. É chamado toda vez que um valor no FormControl é alterado e esse valor passa nas validações descritas.
    • ativar : (() => booleano) | null - a função ativará / desativará as validações por condição (sempre ativada por padrão). Por exemplo, a validade da data de término do serviço não precisa ser verificada se a caixa de seleção "Ilimitado" não estiver marcada. Como resultado, basta digitar aqui uma função que, ao marcar o estado do campo observável responsável pela caixa de seleção "Ilimitado", você pode desativar automaticamente todas as validações associadas ao campo para verificar a data, em vez de registrar essa lógica em cada uma das validações de campo de data.
    • additionalData : TAdditionalData | null - um bloco com informações adicionais permite adicionar informações adicionais a um FormControl específico e usá-las posteriormente, por exemplo, para visualização. Isso é conveniente se houver construtores para o FormControl nos quais você precise analisar determinadas informações e não passar essas informações por meio de um pacote de dados complexo para os controles de visualização. Embora eu não possa fornecer um cenário de aplicativo exato e inegável, é melhor ter essa oportunidade do que sofrer sem ela.

    Há uma limitação que o Angular FormControl também possui: não há necessidade de reutilizar objetos em diferentes formas. I.e. Você pode criar um construtor FormGroup e criar seu próprio objeto em cada página. Mas usar um objeto por grupo de páginas é uma prática ruim.
    Além disso, o FormControl é inicializado com um único valor e, se esse valor for alterado, o novo valor não entrará no FormControl . Isso é feito de propósito, porque, como a prática mostrou, por algum motivo, todo mundo está tentando teimosamente editar inicialmente o objeto original ignorando validações, e não o valor no FormControl . Basta atribuir um novo valor ao campo de valor do FormControl para modificar o objeto original.
    O FormGroup aceita o seguinte conjunto de parâmetros no construtor:
    • Controles : TControls - um objeto herdado de AbstractControls . De fato, basta criar uma interface herdada de AbstractControls na qual você enumera campos do tipo FormGroup , FormControl , FormArray . É claro que você pode definir o tipo como any , mas todas as vantagens do TypeScript serão perdidas.
    • validators : ValidatorFunctionFormGroupHandler [] - um conjunto de validadores para valores de grupo. Por exemplo, você pode criar um FormGroup contendo dois valores - a data mínima e máxima, para o controle de seleção de período. É nesses validadores que você precisará passar a função / funções de verificar o período. Por exemplo, que a data de início não seja maior que a data de término
    • ativar : (() => booleano) | null - a função ativará / desativará as validações por condição (sempre ativada por padrão). Deve-se entender que a aplicação da função de validação a um grupo desativa a validação no nível de todo o grupo. Por exemplo, temos uma caixa de seleção para um documento de identidade. Você pode criar vários Grupos de Formulários com um conjunto diferente de campos para documentos: passaporte, carteira de motorista, passaporte de marinheiro, etc. ... Nesta função, verifique os valores suspensos e, se o valor selecionado não corresponder a esse grupo, todas as verificações de validação serão desativadas. Mais precisamente, o grupo será considerado válido, independentemente dos valores nele contidos.

    Vamos falar sobre os campos FormControl , incluindo a presença do FormGroup e do FormArray .
    • ControlTypes - tipo de controle (Control | Group | Array)
    • processing : boolean - no processo de análise. Porque Validações assíncronas são suportadas, por exemplo, aquelas que requerem uma solicitação do servidor. O status atual da verificação pode ser encontrado neste campo.
      Além disso, o FormGroup e o FormArray oferecem suporte ao método wait , que permite aguardar a conclusão da verificação. Por exemplo, quando você clica no botão "enviar dados", é necessário registrar o seguinte design.
       await this.form.wait(); if (this.form.invalid) { … 

    • disabled : boolean - a verificação de erros está desativada (o controle é sempre válido)
    • active : boolean - a verificação de erros está ativada. Depende do resultado da função de ativação. Esse valor é muito conveniente para ocultar um grupo de campos em um formulário e não gravar funções adicionais e duplicadas da lógica de negócios.
    • inválido : booleano; - para FormControl - significa que o campo contém erros de validação. Para FormGroup e FormArray, significa que o próprio controle de grupo contém erros ou um dos campos aninhados (em qualquer um dos níveis de aninhamento) contém erros de validação. I.e. para verificar a validade de todo o formulário, basta executar uma verificação inválida ou válida no FormGroup superior.
    • válido : booleano - para FormControl - significa que o campo não contém erros de validação. Para FormGroup e FormArray, significa que o próprio controle de grupo não contém erros e nenhum dos campos aninhados (em qualquer um dos níveis de aninhamento) contém erros de validação.
    • pristine : boolean - o valor no campo, após a inicialização com o valor padrão, não foi alterado.
    • dirty : boolean - o valor no campo, após a inicialização com o valor padrão, foi alterado.
    • intocado : booleano - para FormControl - significa que o campo (por exemplo, entrada) não estava em foco. For FormGroup e FormArray significa que nenhum dos FormControls aninhados estavam em foco. Um valor false neste campo significa que o foco não foi apenas definido, mas também removido do campo.
    • tocado : booleano - Para FormControl - significa que o campo (por exemplo, entrada) estava em foco. Para FormGroup e FormArray , significa que um dos FormControls aninhados estava em foco. Um valor true nesse campo significa que o foco não foi apenas definido, mas também removido do campo.
    • focus : boolean - para FormControl - significa que o campo (por exemplo, entrada) está agora em foco. Para FormGroup e FormArray , isso significa que um dos FormControls aninhados está agora em foco.
    • errors : ValidationEvent [] - o campo contém erros de validação. Ao contrário dos campos listados, essa matriz contém exatamente os erros de FormControl , FormGroup ou FormArray , ou seja , erros desse controle e nem todos aninhados. Afeta o campo válido / inválido
    • avisos : ValidationEvent [] - o campo contém mensagens de aviso. Ao contrário dos campos listados, essa matriz contém exatamente os erros de FormControl, FormGroup ou FormArray, ou seja, mensagens deste controle, mas nem todas anexadas. Não afeta o campo válido / inválido
    • informationMessages : ValidationEvent [] - o campo contém mensagens informativas. Ao contrário dos campos listados, essa matriz contém exatamente os erros de FormControl, FormGroup ou FormArray, ou seja, mensagens deste controle, mas nem todas anexadas. Não afeta o campo válido / inválido
    • sucessos : ValidationEvent - o campo contém mensagens de validade adicionais. Ao contrário dos campos listados, essa matriz contém exatamente os erros de FormControl, FormGroup ou FormArray, ou seja, mensagens deste controle, mas nem todas anexadas. Não afeta o campo válido / inválido
    • maxEventLevel () - o nível máximo de mensagens de validação atualmente no campo.
    • O método retornará um dos valores de enumeração, na próxima prioridade.
      1. ValidationEventTypes.Error;
      2. ValidationEventTypes.Warning;
      3. ValidationEventTypes.Info;
      4. ValidationEventTypes.Success;

    • serverErrors : string [] - após enviar uma mensagem ao servidor, é uma boa forma de verificar a validade do formulário no servidor também. Como resultado, o servidor pode retornar erros da verificação final do formulário e a matriz serverErrors destina-se a esses erros. Um dos principais recursos do serverErrors é a limpeza automática de mensagens de validação quando o foco é perdido do campo ao qual os erros do servidor foram atribuídos e os erros do servidor também são limpos se o campo tiver sido alterado.
    • onChange: Delegate - além do mecanismo mobx padrão - reação, você pode usar delegate e adicionar uma função de retorno de chamada a ele, chamada quando os dados são alterados.
    • setDirty (dirty: boolean): void - o método permitirá alterar o valor dos campos imaculados / sujos
    • setTouched (tocado: booleano): nulo; - o método permitirá alterar o valor dos campos intocados / tocados
    • descarte (): nulo; - necessário para chamar o controle responsável pela página em componentWillUnmount.


    Esses eram campos comuns para todos os controles, mas cada controle também possui campos exclusivos para seu tipo.
    Formcontrol
    • valor - contém o valor atual do campo. Você também pode atribuir um novo valor a este campo.


    FormGroup e FormArray contêm
    • wait - o método permite que você espere o fim das verificações de todas (validações), incluindo aninhadas
    • allControls () - esse método permite obter um conjunto completo de todos os FormControl, incluindo aninhados em diferentes níveis. I.e. de fato, ele expande um objeto FormGroup multinível, que também pode conter o FormGroup, em uma lista grande que consiste apenas em FormControl. Essa funcionalidade é necessária se quisermos encontrar o primeiro elemento inválido e colocar o foco nele.
      o código, neste caso, ficaria assim:
       await this.form.wait(); if (this.form.invalid) { this.form.setTouched(true); const firstError = this.form.allControls().find(c => c.invalid && !!c.element); if (!!firstError) { firstError.element.focus(); } } ... 



    Validações


    Obviamente, além dos controles que permitem trabalhar com dados, precisamos de validações. O pacote @ quantumart / mobx-form-validation-kit contém naturalmente várias validações predefinidas e também suporta a criação de validações personalizadas personalizadas.
    Um exemplo de configuração de validações para FormControl para um campo de idade.
     new FormControl<number>( this.userInfo.age, [required(), minValue(18, "    18 .", ValidationEventTypes.Warning)], v => (this.userInfo.age = v) ) 

    Cada validação com os parâmetros mais recentes leva:
    • Mensagem - uma mensagem de validação.
    • eventType - nível da mensagem. 4 níveis de mensagem são suportados.
      1. Erro - erros
      2. Aviso - avisos
      3. Info - Mensagens informativas
      4. Sucesso - mensagens sobre validade. Por exemplo, você pode verificar se a senha é realmente complexa.


    O pacote contém o seguinte conjunto de validações:
    • obrigatório (... - campo obrigatório
    • notEmptyOrSpaces (... - o campo não está vazio e não contém apenas espaços. De fato, obrigatório, levando em consideração a proibição de espaços.
    • padrão (regExp: RegExp, ... - o primeiro parâmetro é a expressão regular que o campo deve corresponder. Um erro será gerado se não houver correspondência de padrão.
    • invertPattern (regExp: RegExp, ... - o primeiro parâmetro é a expressão regular que o campo não deve corresponder. Um erro será gerado se houver uma correspondência de padrão.
    • minLength (minlength: number, .... - o primeiro parâmetro é o comprimento mínimo do texto inclusive. É gerado um erro se o comprimento for menor que o transmitido.
    • maxLength (maxlength: number, .... - o primeiro parâmetro é o comprimento máximo do texto inclusive. É emitido um erro se o comprimento for maior que o transmitido.
    • absoluteLength (length: number, .... - o primeiro parâmetro é o tamanho exato do texto. Um erro será emitido se o comprimento não corresponder ao especificado.
    • minValue (min: TEntity | (() => TEntity), ... - esta validação destina-se apenas a números e datas. Um erro é definido se o valor for menor que o especificado. A peculiaridade da validação é a capacidade de aceitar não apenas um valor específico, mas também function, o que significa que se você ler o valor nessa função no campo @observable do objeto, a validação em si será reiniciada não apenas quando o campo no qual a validação está pendurada for alterado, mas também quando o "campo vinculado" for alterado. primeiro, exceto tag campo que é lido como o valor @observable.
    • maxValue (max: TEntity | (() => TEntity), ... - esta validação destina-se apenas a números e datas. Um erro é definido se o valor for maior que o especificado. O recurso de validação é a capacidade de aceitar não apenas um valor específico, mas também function, o que significa que se você ler o valor nessa função no campo @observable do objeto, a validação em si será reiniciada não apenas quando o campo no qual a validação está pendurada for alterado, mas também quando o "campo vinculado" for alterado. primeiro, exceto campo tag que é lido como o valor @observable
    • notContainSpaces (... - diferente de notEmptyOrSpaces, um erro será emitido se o valor contiver pelo menos um espaço.
    • compare (expression: (value: TEntity) => boolean (... - escrever sua própria função de validação gera muito código de copiar e colar, esse wrapper foi desenvolvido para se livrar desse problema. Essa função de validação aceita uma função como o primeiro parâmetro, que por sua vez passa o valor atual campos, que permitem que você faça uma verificação complexa, por exemplo, calculando o hash para o número de NIF ou passaporte e retorne verdadeiro / falso.Um erro será exibido se a verificação retornar falso.
    • isEqual (value: string ... - uma verificação simples de correspondência de string.

    A seguir, são descritas as funções do wrapper que controlam o fluxo de gatilhos de validação.
    Deve-se observar que o conjunto de validações passadas para FormControl , FormGroup , FormArray é iniciado em uma única matriz e, de fato, não possui sequência de execução. Como resultado do trabalho, teremos nos campos erros , avisos , informações , mensagens, matrizes de sucessos que consistem em erros, avisos combinados em uma única matriz, etc ...
    Muitas vezes, o cliente quer ver apenas um erro, e não todos de uma vez. Além disso, o TOR pode ser projetado para que uma verificação seja realizada somente após a aprovação da anterior.
    Para resolver esse problema, wrapperSequentialCheck é usado. Sua chamada e sua aplicação não são diferentes do validador de função usual, mas na entrada ele recebe uma matriz de validadores que serão iniciados sequencialmente, ou seja, A próxima validação é iniciada somente após a aprovação anterior sem erros.
    A segunda função do wrapper é a função de controle de fluxo das validações. wrapperActivateValidation como o primeiro parâmetro assume uma função na qual as condições para a ativação da validação devem ser gravadas. Diferentemente da função de ativação que é passada para o FormControl, essa verificação é projetada para uma lógica mais complexa. Suponha que tenhamos um construtor comum para toda a forma de pagamento do FormGroup e, além disso, existe apenas um método no servidor que aceita um conjunto comum de campos. Mas o problema é que, embora o formulário seja um, dependendo do "tipo de pagamento", mostramos um conjunto diferente de campos para o usuário. Portanto, wrapperActivateValidation permite que você escreva uma lógica na qual várias verificações serão realizadas, dependendo do tipo de pagamento.
    O uso de wrappers será parecido com funções comuns.
     new FormControl( this.userInfo.megapole, [wrapperActivateValidation(() => this.info.A === 10, [ required(), pattern(/\^d{10}$/) ]), wrapperActivateValidation(() => this.info.A === 20, [ wrapperSequentialCheck([ notContainSpaces(), pattern(/\^d{20}$/) ]) ])], v => (this.userInfo.megapole = v) ) 

    Este exemplo mostra que as verificações obrigatórias (), padrão (/ \ ^ d {10} $ /) serão executadas apenas com this.info.A === 10 e se this.info.A === 20 , as validações notContainSpaces (), pattern (/ \ ^ d {20} $ /) funcionarão. Além disso, essas validações funcionarão sequencialmente, ao contrário do primeiro caso.

    Naturalmente, chegará o momento em que o conjunto padrão de validações não será mais suficiente.
    Então você deve escrever suas próprias funções assíncronas. Felizmente, isso é feito sem dificuldades.
    O FormControl foi originalmente aprimorado pelas funções de validação assíncrona, que podem querer acessar dados do servidor, e essa resposta precisa aguardar. E, como resultado, todas as validações são assíncronas.
     async function checkValueOnServer(control: FormControl): Promise<ValidationEvent[]> { if (control.value == null) { return []; } const result = await sendToServer(control.value); if (result.errorMessage) { return [ { message: result.errorMessage, type: ValidationEventTypes.Error, }, ]; } return []; } 

    Aqui você precisa prestar atenção em dois objetos.
    Primeiro, sempre nos importamos com a matriz. I.e. de fato, você pode retornar várias mensagens de erro de uma vez, se desejar.
    O segundo ponto é o objeto retornado, com o seguinte conjunto de campos.
    • key ?: string - um campo opcional, permite especificar uma "chave" para uma validação específica. Para todas as chaves de base, a chave é única e corresponde ao nome. Você pode usar a tecla para renderizar a lista como reagir, mas, como a prática demonstrou, é uma má idéia. No futuro, no exemplo, mostrarei que é melhor usar a mensagem e não toque em nenhuma tecla. De qualquer forma, é como em Angunar, mas sua necessidade foi reduzida, de fato, a 0.
    • message : string - uma mensagem de validação. Campo obrigatório.
    • tipo : ValidationEventTypes - tipo de mensagem.
      1. Erro - erros
      2. Aviso - avisos
      3. Info - Mensagens informativas
      4. Sucesso - mensagens sobre validade. Por exemplo, você pode verificar se a senha é realmente complexa.

    • additionalData ?: any - informações adicionais que podem ser transmitidas juntamente com a validação, se necessário. Pode ser alguma marcação html adicional ou um estilo específico. Em geral, você pode colocar tudo em qualquer.


    Extensões


    Qualquer mágica é baseada em coisas triviais. E, nesse caso, para que a configuração do foco funcione, obter alterações nos campos requer vincular o FormControl em um campo de entrada específico.
    Porque O FormControl não limita o desenvolvedor no tipo de dados validados, devido à sua versatilidade, era necessário sacrificar um pouco de aplicabilidade nos elementos de reação.
    Ao mesmo tempo, para entrada e área de texto, foi possível criar funções simples de ligação de dados em um elemento; para outros componentes, o processador ainda precisará fazer esforços mínimos para substituir os dados.

    Para entrada, a ligação do elemento ao FormControl (nome) ficará assim.
    <input type = "text" {... InputFormControl.bindActions (controls.name)} />
    Para a área de texto, a ligação será assim
    <textarea {... TextAreaFormControl.bindActions (controls.name)} />

    InputFormControl.bindActions e TextAreaFormControl.bindActions aceitam dois parâmetros:
    • formControl : FormControl - na verdade FormControl que será usado para ligação. Obrigatório.
    • eventos ? - Um parâmetro opcional que contém uma lista de funções que podem ser chamadas se você precisar personalizá-las. , bindActions - Element, , element FormControl -, . . event. .
      • ref
      • onChange
      • onBlur
      • onFocus


    , FormControl - .
     this.form = new FormGroup<IUserInfo>({ name: new FormControl( this.userInfo.name, [], v => (this.userInfo.name = v) ) }); 

    this.userInfo.name , FormControl . FormControl.for
     this.form = new FormGroup<IUserInfo>({ name: FormControl.for(this.userInfo, 'name', []) }); 

    , name name . , TypeScript, name , . userInfo — .

    — . :)

    Exemplo


    React TypeScript mobx.
    .
    npm install @quantumart/mobx-form-validation-kit

    ,
    :

    ,
    npm init –y
    npm install --save-dev webpack webpack-cli
    npm install --save react react-dom
    npm install --save-dev @types/react @types/react-dom
    npm install --save-dev typescript ts-loader source-map-loader


    tsconfig.json
     { "compilerOptions": { "outDir": "./dist/", "sourceMap": true, "noImplicitAny": true, "module": "commonjs", "target": "es6", "jsx": "react", "experimentalDecorators": true, "emitDecoratorMetadata": true } } 


    • src\components\Hello.tsx
    • src\index.tsx
    • index.html
    • src\assets\global.scss


    src\components\ Hello.tsx
     import * as React from "react"; export class Hello extends React.Component { render() { return ( <h1> Hello from TypeScript and React! </h1> ); } } 

    src\ index.tsx
     import * as React from "react"; import * as ReactDOM from "react-dom"; import { Hello } from "./components/Hello"; ReactDOM.render( <Hello />, document.getElementById("example") ); 

    index.html
     <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>Hello React!</title> </head> <body> <div id="example"></div> <!-- Dependencies --> <script src="./node_modules/react/umd/react.development.js"></script> <script src="./node_modules/react-dom/umd/react-dom.development.js"></script> <!-- Main --> <script src="./dist/main.js"></script> </body> </html> src\assets\global.scss .row { display: inline; } 



    webpack-dev-server
    npm install --save-dev webpack-dev-server
    npm install --save-dev awesome-typescript-loader
    npm install --save-dev html-webpack-plugin

    webpack.config.js
     const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); module.exports = { entry: './src/index.tsx', resolve: { extensions: ['.ts', '.tsx', '.js'] }, output: { path: path.join(__dirname, '/dist'), filename: 'bundle.min.js' }, module: { rules: [ { test: /\.tsx?$/, loader: 'awesome-typescript-loader' }, { test: /\.(scss|css)?$/, use: [ { loader: 'style-loader' }, { loader: 'css-loader', options: { importLoaders: 1, }, }, { loader: 'sass-loader', options: { sourceMap: true } }, ], }, ] }, plugins: [ new HtmlWebpackPlugin({ template: './index.html' }) ] } 

    , mobx React.
     "mobx": "4", "mobx-react": "^6.1.1", 

    C IE10 mobx, 4 , package.json.

      "style-loader": "^0.23.1", "css-loader": "^3.1.0", "node-sass": "^4.12.0", "sass-loader": "^7.1.0" 


    npm install

    , .
    npm install @quantumart/mobx-form-validation-kit



    package.json
     { "name": "ttt", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "webpack-dev-server --mode development --open", "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "MIT", "devDependencies": { "@types/react": "^16.9.5", "@types/react-dom": "^16.9.1", "awesome-typescript-loader": "^5.2.1", "html-webpack-plugin": "^3.2.0", "source-map-loader": "^0.2.4", "ts-loader": "^6.2.0", "typescript": "^3.6.3", "webpack": "^4.41.0", "webpack-cli": "^3.3.9" }, "dependencies": { "@quantumart/mobx-form-validation-kit": "^1.0.8", "mobx": "4", "mobx-react": "^6.1.1", "react": "^16.10.2", "react-dom": "^16.10.2", "webpack-dev-server": "^3.8.2", "style-loader": "^0.23.1", "css-loader": "^3.1.0", "node-sass": "^4.12.0", "sass-loader": "^7.1.0" } } 


    npm run start


    Hello from TypeScript and React!


    Hello . RegistrationStore RegistrationStore.ts
    src\RegistrationStore.ts
     import { observable } from "mobx"; export class RegistrationStore { @observable public userInfo = { name: "" }; } export const registrationStore = new RegistrationStore(); 

    Hello.ts, .
     import * as React from "react"; import { observer } from "mobx-react"; import { registrationStore } from "../RegistrationStore"; @observer export class Hello extends React.Component { private changeName = (event: React.ChangeEvent<HTMLInputElement>) => { registrationStore.userInfo.name = event.target.value; }; render() { return ( <React.Fragment> <h1>, {registrationStore.userInfo.name}</h1> <div className="row"> <span>:</span> <input type="text" value={registrationStore.userInfo.name} onChange={this.changeName} /> </div> </React.Fragment> ); } } 

    , Store Mobx. input.
    , . , . «» . , .

    @quantumart/mobx-form-validation-kit

    - .
    stc/ErrorWraper.tsx
     import * as React from "react"; import { observer } from "mobx-react"; import { FormControl } from "@quantumart/mobx-form-validation-kit"; interface Props { formControl: FormControl; } @observer export class ErrorWraper extends React.Component<Props> { render() { return ( <div> {this.props.children} {this.props.formControl.errors.map(error => ( <span key={error.message} className="error"> {error.message} </span> ))} </div> ); } } 

    , -, .

    Hello.tsx .
    - — changeName. {...InputFormControl.bindActions(controls.name)} . .
    - – input, input , , , .
    - – form store , , , componentWillUnmount registrationStore.form.dispose() . mobx FromControl .
     import * as React from "react"; import { observer } from "mobx-react"; import { registrationStore } from "../RegistrationStore"; import { ErrorWraper } from "../ErrorWraper"; import { InputFormControl } from "@quantumart/mobx-form-validation-kit"; @observer export class Hello extends React.Component { constructor(props: any) { super(props); registrationStore.initForm(); } componentWillUnmount() { registrationStore.form.dispose(); } render() { const controls = registrationStore.form.controls; return ( <React.Fragment> <h1>, {registrationStore.userInfo.name}</h1> <div className="row"> <span>:</span> <ErrorWraper formControl={controls.name}> <input type="text" {...InputFormControl.bindActions(controls.name)} /> </ErrorWraper> </div> </React.Fragment> ); } } 

    RegistrationStore.ts.
    .
    ( ) userInfo, form. - userInfo.
     import { observable } from "mobx"; import { FormControl, FormGroup, AbstractControls } from "@quantumart/mobx-form-validation-kit"; interface IUserInfo extends AbstractControls { name: FormControl; } export class RegistrationStore { @observable public userInfo = { name: "" }; @observable public form: FormGroup<IUserInfo>; public initForm(): void { this.form = new FormGroup<IUserInfo>({ name: new FormControl( this.userInfo.name, [], v => (this.userInfo.name = v) ) }); } } export const registrationStore = new RegistrationStore(); 

Source: https://habr.com/ru/post/pt470537/


All Articles