Nouveau package de validation pour React sur Mobx @ quantumart / mobx-form-validation-kit

Bon après-midi
Aujourd'hui, je veux parler d'un nouveau package pour les validations asynchrones dans les projets basés sur React , Mobx et écrits en Typescript .
Le développement de Frontend moderne implique une grande quantité de logique lors du remplissage des pages avec des documents, des questionnaires et des documents pour un prêt, des ordres de paiement, des pages d'inscription sur le site. La principale charge logique incombe aux contrôles de validation. Les développeurs angulaires ont réfléchi à ce point et proposent aux développeurs d'utiliser le mécanisme FormControl intégré à ces fins, qui, bien qu'il présente un certain nombre d'inconvénients, est toujours meilleur que l'absence totale d'une telle solution sur React . La situation est compliquée par le fait que la tendance actuelle du développement de React implique l'utilisation de mobx pour organiser la logique métier.
Face à ces problèmes, nous les avons tous résolus en écrivant un package: @ quantumart / mobx-form-validation-kit

Avantages du package:
  • Entièrement sur TypeScript
  • Compatible avec Mobx (version 4, qui prend en charge, le favori de tous, IE10)
  • Conçu pour fonctionner dans React (peut être utilisé dans des projets sans réagir)
  • Conçu pour la validation asynchrone
  • Facile à intégrer dans un projet existant.

Instructions pour travailler avec le paquet sous la coupe.


Au début, nous décrirons les fonctionnalités du package @ quantumart / mobx-form-validation-kit , à la fin de l'article, nous écrirons une page entièrement fonctionnelle avec un exemple du formulaire d'inscription sur le site.

Formcontrol


@ quantumart / mobx-form-validation-kit vous permet de créer une couche entre les données source et le formulaire à afficher. Ce qui, à son tour, vous permet de les valider et, si nécessaire, de modifier les données avant qu'elles n'atteignent l'objet d'origine.

La bibliothèque @ quantumart / mobx-form-validation-kit contient trois classes principales (composants de validation) pour gérer le formulaire:
  • FormGroup - vous permet de combiner des composants de validation ensemble. La classe est typée et vous permet de refaire l'interface avec la liste des champs en paramètre générique. Any est enregistré par défaut, il est fortement recommandé de ne pas l'utiliser sans taper, mais il y a une possibilité.
  • FormControl - utilisé pour valider un champ spécifique, la classe la plus couramment utilisée. La classe est typée et prend comme paramètre générique le type de la variable qu'elle doit stocker. Par défaut, la chaîne est enregistrée, car la valeur par défaut est string, comme option la plus privée pour les formulaires.
  • FormArray - vous permet de créer et de gérer un tableau de composants de validation.

De plus, il existe des classes abstraites de base
  • AbstractControl - la classe de base pour toutes les classes de validation répertoriées, non typée.
  • FormAbstractControl - classe de base pour FormGroup et FormArray , non typée.
  • FormAbstractGroup - pas une classe de base typée pour FormControl, contient un lien vers l'élément html qui est rendu.

La meilleure pratique pour créer un formulaire de validation serait l'idée suivante.
Un objet de type un FormGroup est créé sur le formulaire et les champs y sont déjà répertoriés
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) ) // … }); 

FormGroup prend en charge l'imbrication, c'est-à-dire
 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) ), // … }) // … }); 

Vous pouvez ajouter FormArray , qui à son tour peut être passé le type FormControl et ou l'ensemble FormGroup créant des objets de toute complexité et structure.
  • FormArray <FormControl>
    FormArray <FormGroup>

    FormControl lui-même prend l'ensemble de paramètres suivant dans le constructeur
    • valeur : TEntity est la valeur initiale typée.
    • validateurs : ValidatorFunctionFormControlHandler [] - un ensemble de validateurs.
    • callbackValidValue : UpdateValidValueHandler | null - fonction de rappel à laquelle la dernière valeur valide est transmise. Elle est appelée chaque fois qu'une valeur de FormControl change et cette valeur passe les validations décrites.
    • activer : (() => booléen) | null - la fonction activera / désactivera les validations par condition (toujours activée par défaut). Par exemple, la validité de la date de fin du service n'a pas besoin d'être vérifiée si la case "Illimité" n'est pas cochée. Par conséquent, en entrant simplement une fonction ici qui vérifie l'état du champ observable responsable de la case à cocher "Illimité", vous pouvez désactiver automatiquement toutes les validations associées au champ pour vérifier la date, plutôt que d'enregistrer cette logique dans chacune des validations du champ de date.
    • additionalData : TAdditionalData | null - un bloc contenant des informations supplémentaires vous permet d'ajouter des informations supplémentaires à un FormControl spécifique et de les utiliser ultérieurement, par exemple, pour la visualisation. Cela est pratique s'il existe des générateurs pour FormControl dans lesquels vous devez analyser certaines informations et ne pas transmettre ces informations via un ensemble de données complexes aux contrôles pour la visualisation. Bien que je ne puisse pas donner un scénario d'application exact et indéniable, il vaut mieux avoir une telle opportunité que d'en souffrir.

    Il y a aussi une limitation qu'Angular FormControl a: il n'est pas nécessaire de réutiliser des objets sur des formulaires différents. C'est-à-dire Vous pouvez créer un générateur FormGroup et créer votre propre objet sur chaque page. Mais utiliser un objet par paquet de pages est une mauvaise pratique.
    De plus, FormControl est initialisé avec une seule valeur, et si cette valeur est modifiée, la nouvelle valeur n'entrera pas dans FormControl . Cela a été fait exprès, car, comme la pratique l'a montré, pour une raison quelconque, tout le monde essaie obstinément de modifier initialement l'objet d'origine en contournant les validations, et non la valeur dans FormControl . Attribuez simplement une nouvelle valeur au champ de valeur du FormControl pour modifier l'objet d'origine.
    FormGroup accepte l'ensemble de paramètres suivant dans le constructeur:
    • contrôles : TControls - un objet hérité de AbstractControls . En fait, il suffit de créer une interface héritée de AbstractControls dans laquelle vous énumérez des champs de type FormGroup , FormControl , FormArray . Vous pouvez bien sûr définir le type sur any , mais tous les avantages de TypeScript seront perdus.
    • validateurs : ValidatorFunctionFormGroupHandler [] - un ensemble de validateurs pour les valeurs de groupe. Par exemple, vous pouvez créer un FormGroup contenant deux valeurs - la date minimale et maximale, pour le contrôle de sélection de période. C'est dans ces validateurs que vous devrez passer la fonction / les fonctions de vérification de la plage de dates. Par exemple, que la date de début n'est pas supérieure à la date de fin
    • activer : (() => booléen) | null - la fonction activera / désactivera les validations par condition (toujours activée par défaut). Il faut comprendre que l'application de la fonction de validation à un groupe désactive la validation au niveau de l'ensemble du groupe. Par exemple, nous avons une boîte de sélection pour un document d'identité. Vous pouvez créer plusieurs FormGroups avec un ensemble différent de champs pour les documents: passeport, permis de conduire, passeport de marin, etc. ... Dans cette fonction, vérifiez les valeurs déroulantes, et si la valeur sélectionnée ne correspond pas à ce groupe, toutes les vérifications de validation sont désactivées. Plus précisément, le groupe sera considéré comme valide, quelles que soient les valeurs qu'il contient.

    Parlons des champs FormControl , y compris la présence de FormGroup et FormArray .
    • ControlTypes - type de contrôle (Control | Group | Array)
    • traitement : booléen - en cours d'analyse. Parce que Les validations asynchrones sont prises en charge, par exemple celles qui nécessitent une demande de serveur. L'état actuel de l'analyse se trouve dans ce champ.
      En outre, FormGroup et FormArray prennent en charge la méthode d' attente , qui vous permet d'attendre la fin de la vérification. Par exemple, lorsque vous cliquez sur le bouton "envoyer des données", vous devez enregistrer le modèle suivant.
       await this.form.wait(); if (this.form.invalid) { … 

    • désactivé : booléen - la vérification des erreurs est désactivée (le contrôle est toujours valide)
    • actif : booléen - la vérification des erreurs est activée. Dépend du résultat de la fonction d'activation. Cette valeur est très pratique à utiliser pour masquer un groupe de champs sur un formulaire et non pour écrire des fonctions supplémentaires et en double de la logique métier.
    • invalide : booléen; - pour FormControl - signifie que le champ contient des erreurs de validation. Pour FormGroup et FormArray, cela signifie que le contrôle de groupe lui-même contient des erreurs, ou l'un des champs imbriqués (à l'un des niveaux d'imbrication) contient des erreurs de validation. C'est-à-dire pour vérifier la validité de l'ensemble du formulaire, il suffit d'effectuer une vérification invalide ou valide du FormGroup supérieur.
    • valide : booléen - pour FormControl - signifie que le champ ne contient pas d'erreurs de validation. Pour FormGroup et FormArray, cela signifie que le contrôle de groupe lui-même ne contient pas d'erreurs et qu'aucun des champs imbriqués (à aucun niveau d'imbrication) ne contient d'erreurs de validation.
    • pristine : boolean - la valeur dans le champ, après l'initialisation avec la valeur par défaut, n'a pas changé.
    • dirty : boolean - la valeur dans le champ, après l'initialisation avec la valeur par défaut, a changé.
    • intacte : booléen - pour FormControl - signifie que le champ (par exemple, entrée) n'était pas au point. For FormGroup et FormArray signifie qu'aucun des FormControls imbriqués n'était au point. Une valeur false dans ce champ signifie que le focus a été non seulement défini, mais également supprimé du champ.
    • touched : boolean - Pour FormControl - signifie que le champ (par exemple, entrée) était au point. Pour FormGroup et FormArray , cela signifie que l'un des FormControls imbriqués était au point. Une valeur true dans ce champ signifie que le focus a été non seulement défini, mais également supprimé du champ.
    • focus : booléen - pour FormControl - signifie que le champ (par exemple, entrée) est maintenant au point. Pour FormGroup et FormArray , cela signifie que l'un des FormControls imbriqués est maintenant au point.
    • erreurs : ValidationEvent [] - le champ contient des erreurs de validation. Contrairement aux champs répertoriés, ce tableau contient exactement les erreurs de FormControl , FormGroup ou FormArray , c'est-à-dire erreurs de ce contrôle, et pas toutes imbriquées. Affecte un champ valide / invalide
    • warnings : ValidationEvent [] - le champ contient des messages d'avertissement. Contrairement aux champs répertoriés, ce tableau contient exactement les erreurs de FormControl, FormGroup ou FormArray, c'est-à-dire messages de ce contrôle, mais pas tous attachés. N'affecte pas le champ valide / invalide
    • informationMessages : ValidationEvent [] - le champ contient des messages d'information. Contrairement aux champs répertoriés, ce tableau contient exactement les erreurs de FormControl, FormGroup ou FormArray, c'est-à-dire messages de ce contrôle, mais pas tous attachés. N'affecte pas le champ valide / invalide
    • succès : ValidationEvent - le champ contient des messages de validité supplémentaires. Contrairement aux champs répertoriés, ce tableau contient exactement les erreurs de FormControl, FormGroup ou FormArray, c'est-à-dire messages de ce contrôle, mais pas tous attachés. N'affecte pas le champ valide / invalide
    • maxEventLevel () - le niveau maximum de messages de validation actuellement dans le champ.
    • La méthode renverra l'une des valeurs d'énumération, dans la priorité suivante.
      1. ValidationEventTypes.Error;
      2. ValidationEventTypes.Warning;
      3. ValidationEventTypes.Info;
      4. ValidationEventTypes.Success;

    • serverErrors : string [] - après avoir envoyé un message au serveur, il est bon de vérifier également la validité du formulaire sur le serveur. Par conséquent, le serveur peut renvoyer des erreurs de la vérification de forme finale et le tableau serverErrors est destiné à ces erreurs. Une caractéristique clé de serverErrors est le nettoyage automatique des messages de validation lorsque le focus est perdu du champ auquel les erreurs de serveur ont été affectées, et les erreurs de serveur sont également effacées si le champ a été modifié.
    • onChange: Délégué - en plus du mécanisme mobx standard - réaction, vous pouvez utiliser délégué et lui ajouter une fonction de rappel, qui est appelée lorsque les données changent.
    • setDirty (dirty: boolean): void - la méthode vous permettra de modifier la valeur des champs vierges / sales
    • setTouched (touched: boolean): void; - la méthode vous permettra de changer la valeur des champs non touchés / touchés
    • dispose (): vide; - requis pour appeler le contrôle responsable de la page dans componentWillUnmount.


    Ces champs étaient communs à tous les contrôles, mais chaque contrôle possède également des champs uniques à son type.
    Formcontrol
    • valeur - contient la valeur actuelle du champ. Vous pouvez également attribuer une nouvelle valeur à ce champ.


    FormGroup et FormArray contiennent
    • wait - la méthode vous permet de vous attendre à la fin des contrôles de tous (validations) y compris imbriqués
    • allControls () - cette méthode vous permet d'obtenir un ensemble complet de tous les FormControl, y compris imbriqués à différents niveaux. C'est-à-dire en fait, il développe un objet FormGroup à plusieurs niveaux, qui peut également contenir FormGroup, en une seule grande liste composée uniquement de FormControl. Cette fonctionnalité est requise si nous voulons trouver le premier élément invalide et y mettre l'accent.
      le code, dans ce cas, ressemblerait à ceci:
       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(); } } ... 



    Validations


    Bien sûr, en plus des contrôles qui vous permettent de travailler avec des données, nous avons besoin de validations elles-mêmes. Le package @ quantumart / mobx-form-validation-kit contient naturellement un certain nombre de validations prédéfinies et prend également en charge la création de validations personnalisées personnalisées.
    Un exemple de définition de validations pour FormControl pour un champ d'âge.
     new FormControl<number>( this.userInfo.age, [required(), minValue(18, "    18 .", ValidationEventTypes.Warning)], v => (this.userInfo.age = v) ) 

    Chaque validation avec les derniers paramètres prend:
    • Message - Un message de validation.
    • eventType - niveau du message. 4 niveaux de messages sont pris en charge.
      1. Erreur - erreurs
      2. Avertissement - avertissements
      3. Info - Messages d'information
      4. Succès - messages sur la validité. Par exemple, vous pouvez vérifier que le mot de passe est vraiment complexe.


    Le package contient l'ensemble de validations suivant:
    • obligatoire (... - champ obligatoire
    • notEmptyOrSpaces (... - le champ n'est pas vide et ne contient pas que des espaces. En fait, obligatoire, compte tenu de l'interdiction des espaces.
    • pattern (regExp: RegExp, ... - le premier paramètre est l'expression régulière à laquelle le champ doit correspondre. Une erreur est générée s'il n'y a pas de correspondance de modèle.
    • invertPattern (regExp: RegExp, ... - le premier paramètre est l'expression régulière à laquelle le champ ne doit pas correspondre. Une erreur est générée s'il existe une correspondance de modèle.
    • minLength (minlength: nombre, .... - le premier paramètre est la longueur minimale du texte inclus. Une erreur est générée si la longueur est inférieure à celle transmise.
    • maxLength (maxlength: nombre, .... - le premier paramètre est la longueur maximale du texte inclus. Une erreur est émise si la longueur est plus longue que celle transmise.
    • absoluteLength (longueur: nombre, .... - le premier paramètre est la longueur exacte du texte. Une erreur est émise si la longueur ne correspond pas à celle spécifiée.
    • minValue (min: TEntity | (() => TEntity), ... - cette validation est destinée uniquement aux nombres et aux dates. Une erreur est définie si la valeur est inférieure à la valeur spécifiée. La particularité de la validation est la capacité d'accepter non seulement une valeur spécifique, mais aussi , ce qui signifie que si vous lisez la valeur de cette fonction dans le champ @observable de l'objet, la validation elle-même sera redémarrée non seulement lorsque le champ auquel la validation est suspendue est modifié, mais également lorsque le «champ lié» est modifié. premier hors champ de l'étiquette qui est lue comme la valeur @observable.
    • maxValue (max: TEntity | (() => TEntity), ... - cette validation est destinée uniquement aux nombres et aux dates. Une erreur est définie si la valeur est supérieure à celle spécifiée. La fonction de validation est la capacité d'accepter non seulement une valeur spécifique, mais aussi , ce qui signifie que si vous lisez la valeur de cette fonction dans le champ @observable de l'objet, la validation elle-même sera redémarrée non seulement lorsque le champ auquel la validation est suspendue est modifié, mais également lorsque le «champ lié» est modifié. premier hors champ de l'étiquette qui est lue comme la valeur @observable
    • notContainSpaces (... - contrairement à notEmptyOrSpaces, une erreur sera émise si la valeur contient même au moins un espace.
    • compare (expression: (valeur: TEntity) => booléen (... - l'écriture de votre propre fonction de validation génère beaucoup de code copier-coller, ce wrapper a été développé pour se débarrasser de ce problème. Cette fonction de validation accepte une fonction comme premier paramètre, qui à son tour transmet la valeur actuelle , ce qui vous permet d'effectuer une vérification complexe, par exemple, de calculer le hachage pour le numéro TIN ou le numéro de passeport, puis de renvoyer true / false. Une erreur s'affiche si la vérification renvoie false.
    • isEqual (valeur: chaîne ... - une simple vérification de correspondance de chaîne.

    Ce qui suit décrit les fonctions d'encapsuleur qui contrôlent le flux des déclencheurs de validation.
    Il convient de noter que l'ensemble des validations passées à FormControl , FormGroup , FormArray est lancé dans un seul tableau et n'a en fait aucune séquence d'exécution. À la suite du travail, nous aurons dans les champs des erreurs , des avertissements , des messages d' information , des tableaux de succès composés d'erreurs, d'avertissements, combinés en un seul tableau, etc.
    Souvent, le client ne souhaite voir qu'une seule erreur, et pas toutes à la fois. De plus, le TOR peut être conçu de telle sorte qu'un contrôle ne soit effectué qu'après le précédent.
    Pour résoudre ce problème, wrapperSequentialCheck est utilisé. Son appel et son application ne diffèrent pas du validateur de fonction habituel, mais à l'entrée, il reçoit un tableau de validateurs qui seront lancés séquentiellement, c'est-à-dire La validation suivante ne démarre qu'après que la précédente s'est passée sans erreur.
    La deuxième fonction wrapper est la fonction de contrôle de flux des validations. wrapperActivateValidation comme premier paramètre prend une fonction dans laquelle les conditions d'activation de la validation doivent être écrites. Contrairement à la fonction d'activation transmise à FormControl, cette vérification est conçue pour une logique plus complexe. Supposons que nous ayons un générateur commun pour l'ensemble des formes de paiement FormGroup , et de plus, il n'y a qu'une seule méthode sur le serveur qui accepte un ensemble commun de champs. Mais le hic, c'est que même si le formulaire en est un, en fonction du "type de paiement", nous montrons un ensemble de champs différent à l'utilisateur. Ainsi, wrapperActivateValidation vous permet d'écrire une logique dans laquelle différents contrôles seront effectués en fonction du type de paiement.
    L'utilisation de wrappers ressemblera à des fonctions ordinaires.
     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) ) 

    Cet exemple montre que les vérifications requises (), de modèle (/ \ ^ d {10} $ /) seront effectuées uniquement avec this.info.A === 10 , et si this.info.A === 20 , alors les validations notContainSpaces (), pattern (/ \ ^ d {20} $ /) fonctionneront, de plus, ces validations fonctionneront séquentiellement, contrairement au premier cas.

    Naturellement, le moment viendra où l'ensemble de validations standard ne sera plus suffisant.
    Ensuite, vous devez écrire vos propres fonctions asynchrones. Heureusement, cela se fait sans difficultés particulières.
    FormControl a été initialement affiné par des fonctions de validation asynchrones, qui pourraient vouloir aller sur le serveur pour les données, et cette réponse doit attendre. Et par conséquent, toutes les validations sont asynchrones.
     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 []; } 

    Ici, vous devez faire attention à deux objets.
    D'abord, nous nous occupons toujours du tableau. C'est-à-dire en fait, vous pouvez renvoyer plusieurs messages d'erreur à la fois, si vous le souhaitez.
    Le deuxième point est l'objet renvoyé; il possède l'ensemble de champs suivant.
    • key ?: string - un champ facultatif, vous permet de spécifier une "clé" pour une validation spécifique. Pour toutes les clés de base, la clé est unique et correspond à leur nom. Vous voudrez peut-être utiliser la clé pour rendre la liste en réaction, mais comme la pratique l'a montré, c'est une mauvaise idée. À l'avenir, dans l'exemple, je montrerai qu'il est préférable d'utiliser le message et de ne pas toucher du tout à la touche. En tout cas, elle l'est, comme à Angunar, mais son besoin a été réduit, en fait, à 0.
    • message : string - un message de validation. Champ obligatoire.
    • type : ValidationEventTypes - type de message.
      1. Erreur - erreurs
      2. Avertissement - avertissements
      3. Info - Messages d'information
      4. Succès - messages sur la validité. Par exemple, vous pouvez vérifier que le mot de passe est vraiment complexe.

    • additionalData ?: any - informations supplémentaires qui peuvent être transmises avec validation, si nécessaire. Il peut s'agir d'un balisage HTML supplémentaire ou d'un style spécifique. En général, vous pouvez tout mettre en tout.


    Extensions


    Toute magie est basée sur des choses triviales. Et dans ce cas, pour que le paramètre de focus fonctionne, obtenir des modifications dans les champs nécessite de lier FormControl dans un champ de saisie spécifique.
    Parce que FormControl ne limite pas le développeur dans le type de données validées, en raison de sa polyvalence, il fallait sacrifier un peu d'applicabilité aux éléments réactifs.
    Dans le même temps, pour la saisie et la zone de texte, il était possible de créer des fonctions simples de liaison de données sur un élément, pour d'autres composants, le processeur devra encore faire des efforts minimes pour remplacer les données.

    Pour l'entrée, la liaison de l'élément à FormControl (nom) ressemblera à ceci.
    <input type = "text" {... InputFormControl.bindActions (controls.name)} />
    Pour la zone de texte, la reliure sera comme ceci
    <textarea {... TextAreaFormControl.bindActions (controls.name)} />

    InputFormControl.bindActions et TextAreaFormControl.bindActions acceptent deux paramètres:
    • formControl : FormControl - en fait FormControl qui sera utilisé pour la liaison. Requis.
    • événements ? - Un paramètre facultatif contenant une liste de fonctions qui peuvent être appelées si vous avez besoin de les personnaliser. , 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 — .

    — . :)


    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/fr470537/


All Articles