diff --git a/16 Validation/readme.md b/16 Validation/readme.md index b1a5fbb..b9fa976 100644 --- a/16 Validation/readme.md +++ b/16 Validation/readme.md @@ -1,10 +1,10 @@ -# 15 Login form Validation +## 15 Login form Validation Let's add validation support to this form. For this we will use lc-form-validation library -Summary steps: +## Summary steps: - Install lc-form-validation library. - Refactor input component to a common component and include error validation info. @@ -39,6 +39,7 @@ _./common/forms/textFieldForm.tsx_ ```tsx import * as React from "react"; import TextField from "@material-ui/core/TextField"; +import Typography from "@material-ui/core/Typography"; interface Props { name: string; @@ -58,7 +59,7 @@ const onTextFieldChange = (fieldId : string, onChange: (fieldId, value) => void) } export const TextFieldForm : React.StatelessComponent = (props) => { - const {name, label, onChange, value, error, type} = props; + const { name, label, onChange, value, error, type } = props; return ( <> = (props) => { ) } - ``` @@ -130,7 +130,7 @@ _./src/pages/login/loginPage.tsx_ ```diff import { isValidLogin } from '../../api/login'; -+ import {LoginFormErrors} from './viewmodel'; ++ import { LoginFormErrors } from './viewmodel'; interface State { loginInfo: LoginEntity; @@ -146,11 +146,11 @@ _./src/pages/login/loginPage.tsx_ // Adding imports ```diff import { isValidLogin } from '../../api/login'; -+ import {LoginFormErrors, createDefaultLoginFormErrors} from './viewmodel'; ++ import { LoginFormErrors, createDefaultLoginFormErrors } from './viewmodel'; + import { loginFormValidation } from './loginValidations'; ``` -_./src/pages/login/loginPageContainer.tsx_ +_./src/pages/login/loginPage.tsx_ ```diff constructor(props) { @@ -183,7 +183,7 @@ _./src/pages/login/loginPageContainer.tsx_ - We need to pass down dataFormErrors -_./src/loginPageContainer.tsx_ +_./src/loginPage.tsx_ ```diff public render() { @@ -204,12 +204,12 @@ _./src/loginForm.tsx_ ```diff import { LoginEntity } from "../../model/login"; -+ import {LoginFormErrors} from './viewmodel'; ++ import { LoginFormErrors } from './viewmodel'; interface Props { loginInfo: LoginEntity; - updateField: (string, any) => void; - doLogin: () => void; + onUpdateField: (string, any) => void; + onLogin: () => void; + loginFormErrors : LoginFormErrors; } ``` @@ -221,7 +221,7 @@ _./src/common/pages/loginForm.tsx_ ```diff import { LoginEntity } from "../../model/login"; -import {LoginFormErrors} from './viewmodel'; +import { LoginFormErrors } from './viewmodel'; + import { TextFieldForm } from '../../common/forms/textFieldForm'; ``` @@ -282,8 +282,7 @@ export const LoginForm = (props: Props) => { npm start ``` -- And let's add an alert (Excercise and a notification) when the user clicks and -the form all the fields are valid. +- And let's add an alert (Excercise and a notification) when the user clicks and the form all the fields are valid. _./src/pages/login/loginPageContainer.tsx_ diff --git a/16 Validation/readme_es.md b/16 Validation/readme_es.md new file mode 100644 index 0000000..e5b93e1 --- /dev/null +++ b/16 Validation/readme_es.md @@ -0,0 +1,305 @@ +## 15 Login form Validation + +Vamos a añadir validaciones de apoyo a este formulario. + +Para esto usaremos la librería lc-form-validation + +## Resumen de pasos: + +- Instala la librería lc-form-validation. +- Refactoriza el componente input a un componente común e incluye información de errores de validación. +- Define la validación para el formulario. +- Vamos a engancharlo. + +## Prerrequisitos + +Instalar [Node.js y npm](https://nodejs.org/en/) (v6.6.0 o superior) si no las tenemos instaladas en nuestro ordenador. + +> Verifica que estás usando al menos node v6.x.x and npm 3.x.x usando los comandos `node -v` y `npm -v` en un terminal/consola. Versiones anteriores pueden producir errores. + +## Pasos para construirlo + +- Copia el contenido de _15 React Form_ y execute _npm install_. + +```bash +npm install +``` + +- Instala la librería (esta incluye los tipos). + +```bash +npm install lc-form-validation --save-dev +``` + +- Para evitar tener mucho código repetido vamos a mover a comun un componente input, incluyendo este label mas el texto de validación. + +_./common/forms/textFieldForm.tsx_ + +```tsx +import * as React from "react"; +import TextField from "@material-ui/core/TextField"; +import Typography from "@material-ui/core/Typography"; + +interface Props { + name: string; + label: string; + onChange: any; + value: string; + error?: string; + type? : string; +} + +const defaultProps : Partial = { + type: 'text', +} + +const onTextFieldChange = (fieldId : string, onChange: (fieldId, value) => void) => (e) => { + onChange(fieldId, e.target.value); +} + +export const TextFieldForm : React.StatelessComponent = (props) => { + const { name, label, onChange, value, error, type } = props; + return ( + <> + + + {props.error} + + + ) +} +``` + +- Ahora definamos una validación básica para el formulario, queremos asegurar de que ambos campos están informado. + +_./src/pages/login/loginValidations.ts_ + +```typescript +import { + createFormValidation, ValidationConstraints, Validators, +} from 'lc-form-validation'; + +const loginFormValidationConstraints: ValidationConstraints = { + fields: { + login: [ + { validator: Validators.required }, + ], + password: [ + { validator: Validators.required }, + ], + }, +}; + +export const loginFormValidation = createFormValidation(loginFormValidationConstraints); +``` + +- Crea ahora a una clase a ambos dataFormErrors. + +_./src/login/viewmodel.ts_ + +```typescript +import { FieldValidationResult } from 'lc-form-validation'; + +export interface LoginFormErrors { + login: FieldValidationResult; + password: FieldValidationResult; +} + +export const createDefaultLoginFormErrors = (): LoginFormErrors => ({ + login: new FieldValidationResult(), + password: new FieldValidationResult(), +}); +``` + +- Ahora vamos por el lado del componente. + +- Primero vamos a añadir el estado al dataFormErrors del componente. + +_./src/pages/login/loginPage.tsx_ + +```diff +import { isValidLogin } from '../../api/login'; ++ import { LoginFormErrors } from './viewmodel'; + +interface State { + loginInfo: LoginEntity; + showLoginFailedMsg: boolean; ++ loginFormErrors : LoginFormErrors; +} +``` + +- Ahora vamos a actualizar la llamada onUpdate para incluir la validación. + +_./src/pages/login/loginPage.tsx_ + +// Adding imports +```diff +import { isValidLogin } from '../../api/login'; ++ import { LoginFormErrors, createDefaultLoginFormErrors } from './viewmodel'; ++ import { loginFormValidation } from './loginValidations'; +``` + +_./src/pages/login/loginPage.tsx_ + +```diff + constructor(props) { + super(props); + + this.state = { loginInfo: createEmptyLogin(), + showLoginFailedMsg : false, ++ loginFormErrors: createDefaultLoginFormErrors(), + } + } + ++ // This could be simplified and made in one go + updateLoginField = (name, value) => { + this.setState({loginInfo: { + ...this.state.loginInfo, + [name]: value, + } + }); + ++ loginFormValidation.validateField(this.state.loginInfo, name, value) ++ .then((fieldValidationResult) => { + ++ this.setState({loginFormErrors: { ++ ...this.state.loginFormErrors, ++ [name]: fieldValidationResult, ++ }); ++ }); + } +``` + +- Necesitamos pasar para abajo dataFormErrors + +_./src/loginPage.tsx_ + +```diff + public render() { + return ( + + ) + } +``` + +- Ahora necesitamos definir la propiedad en el componente loginForm. + +_./src/loginForm.tsx_ + +```diff +import { LoginEntity } from "../../model/login"; ++ import { LoginFormErrors } from './viewmodel'; + +interface Props { + loginInfo: LoginEntity; + onUpdateField: (string, any) => void; + onLogin: () => void; ++ loginFormErrors : LoginFormErrors; +} +``` + +- Ahora actualiza nuestros componentes para encontrar el componente input nuevo. + +_./src/common/pages/loginForm.tsx_ + +```diff +import { LoginEntity } from "../../model/login"; +import { LoginFormErrors } from './viewmodel'; ++ import { TextFieldForm } from '../../common/forms/textFieldForm'; +``` + +_./src/common/pages/loginForm.tsx_ + +```diff +export const LoginForm = (props: Props) => { +- const { onLogin, onUpdateField, loginInfo } = props; ++ const { onLogin, onUpdateField, loginInfo, loginFormErrors } = props; + +- const onTexFieldChange = (fieldId) => (e) => { +- onUpdateField(fieldId, e.target.value); +- } + + return ( +
+- ++ + +- ++ + + + +
+ ) +} +``` + +- Vamos a intentarlo. + +```bash +npm start +``` + +- Y vamos a añdir un alert (Ejercicio y una notificación) cuando el usuario pulse y los campos del formulario son válidos. + +_./src/pages/login/loginPageContainer.tsx_ + +```diff + onLogin = () => { ++ loginFormValidation.validateForm(this.state.loginInfo) ++ .then((formValidationResult) => { ++ if(formValidationResult.succeeded) { + if (isValidLogin(this.state.loginInfo)) { + this.props.history.push('/pageB'); + } else { + this.setState({ showLoginFailedMsg: true }); + } ++ } else { ++ alert('error, review the fields'); ++ } ++ }) + } +``` + +// TODO: mapFormValidationResultToFieldValidationErrors + +> Ejercicio crea una información genérica dentro del snackbar y elimina el alert. \ No newline at end of file