Skip to content

Instantly share code, notes, and snippets.

@WolfDan
Last active January 8, 2018 22:49
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save WolfDan/5d4e7f3c7d994b1b0a3bb4e4c6f07d66 to your computer and use it in GitHub Desktop.
Save WolfDan/5d4e7f3c7d994b1b0a3bb4e4c6f07d66 to your computer and use it in GitHub Desktop.
ReForm validation proposal
/* Source from: https://github.com/Astrocoders/reform I edited it to handle the onChange events and validation */
type action =
| HandleChange((string, string))
| HandleSubmit;
type validation =
| Required
| MinLength(int)
| MaxLength(int)
| Between(int, int)
| SameAs(string)
| Regex(string)
| Alpha
| AlphaNum
| Numeric
| Email
| Url;
let validate = (validation, value) =>
switch validation {
| Required => value === ""
| MinLength(length) => String.length(value) < length
| MaxLength(length) => String.length(value) > length
| Between(min, max) => String.length(value) < min || String.length(value) > max
| SameAs(same_value) => value != same_value
| Regex(regex) => Js.Re.test(value, Js.Re.fromString(regex))
| Alpha => ! Js.Re.test(value, [%bs.re "/^[a-zA-Z]*$/"])
| AlphaNum => ! Js.Re.test(value, [%bs.re "/^[a-zA-Z0-9]*$/"])
| Numeric => ! Js.Re.test(value, [%bs.re "/^[0-9]*$/"])
| Email => ! Js.Re.test(value, [%bs.re "/@/"])
| Url =>
!
Js.Re.test(
value,
[%bs.re
"/^(?:(?:https?|ftp):\\/\\/)(?:\\S+(?::\\S*)?@)?(?:(?!(?:10|127)(?:\\.\\d{1,3}){3})(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))(?::\\d{2,5})?(?:[/?#]\\S*)?$/"
]
)
};
let doValidation = (validationList, value) =>
validationList |> List.filter((validation) => validate(validation, value));
module Create =
(
Config: {
type state;
let handleChange: (action, state) => state;
let initialState: state;
let getValidation: (string, state) => (list(validation), string);
}
) => {
type values = Config.state;
type state = {values};
let component = ReasonReact.reducerComponent("ReForm");
let make = (~onSubmit: values => unit, ~validate: list(string), children) => {
...component,
initialState: () => {values: Config.initialState},
reducer: (action, state) =>
switch action {
| HandleChange((_, _)) =>
ReasonReact.Update({values: Config.handleChange(action, state.values)})
| HandleSubmit =>
onSubmit(state.values);
ReasonReact.NoUpdate
},
render: (self) => {
let handleChange = (field) =>
self.reduce(
(event) =>
HandleChange((
field,
ReactDOMRe.domElementToObj(ReactEventRe.Form.target(event))##value
))
);
let handleFormSubmit = self.reduce((_) => HandleSubmit);
let isValid = (field: string) => {
let (validationList, fieldValue) = Config.getValidation(field, self.state.values);
doValidation(validationList, fieldValue)
};
let handleSubmit = (_) => {
let validationError = validate |> List.map((field) => isValid(field)) |> List.flatten;
validationError == [] ? handleFormSubmit() : ignore()
};
children(~form=self.state, ~handleChange, ~handleSubmit, ~isValid)
}
};
};
open ReactRouter;
open ReForm;
let text = ReasonReact.stringToElement;
module SignInFormParams = {
type state = {
password: string,
password_confirmation: string,
email: string
};
let initialState = {password: "", email: "", password_confirmation: ""};
let handleChange = (action, state) =>
switch action {
| HandleChange(("password", value)) => {...state, password: value}
| HandleChange(("email", value)) => {...state, email: value}
| HandleChange(("password_confirmation", value)) => {...state, password_confirmation: value}
| _ => state
};
let getValidation = (field, state) =>
switch field {
| "password" => ([Required, Between(8, 15)], state.password)
| "password_confirmation" => ([SameAs(state.password)], state.password_confirmation)
| "email" => ([Email, Required], state.email)
| _ => ([], "")
};
};
let showError = (error) =>
switch error {
| Required => "The field is required"
| Between(min, max) =>
"Name must have at least "
++ string_of_int(min)
++ " and less than "
++ string_of_int(max)
++ "letters"
| Email => "The given email is invalid"
| SameAs(_value) => "The given value aren't equal"
| _ => "What ever"
};
module ReForm = ReForm.Create(SignInFormParams);
let component = ReasonReact.statelessComponent("Register");
let make = (_children) => {
...component,
render: (_self) => {
let validate: list(string) = ["password", "password_confirmation", "email"];
let signInMutation: SignInFormParams.state => unit = (values) => Js.log(values);
<div className="grid-full-background">
<div className="main-nav"> <NavBar navClassName="content" /> </div>
<div className="background-container">
<div className="background" />
<div className="form-auth">
<div className="title-section">
<h1 className="title auth"> (text("Welcome")) </h1>
<span className="subtitle auth"> (text("Register and join the community")) </span>
</div>
<ReForm onSubmit=signInMutation validate>
...(
(~form, ~handleChange, ~handleSubmit, ~isValid) =>
<Form>
<label>
(text("Email"))
<input
_type="email"
value=form.values.email
onChange=(handleChange("email"))
/>
(
ReasonReact.arrayToElement(
Array.map(
(error) => text(showError(error)),
Array.of_list(isValid("email"))
)
)
)
</label>
<label>
(text("Password"))
<input
_type="password"
value=form.values.password
onChange=(handleChange("password"))
/>
(
ReasonReact.arrayToElement(
Array.map(
(error) => text(showError(error)),
Array.of_list(isValid("password"))
)
)
)
</label>
<label>
(text("Password Confirmation"))
<input
_type="password"
value=form.values.password_confirmation
onChange=(handleChange("password_confirmation"))
/>
(
ReasonReact.arrayToElement(
Array.map(
(error) => text(showError(error)),
Array.of_list(isValid("password_confirmation"))
)
)
)
</label>
<button className="button primary full" onClick=handleSubmit>
(text("Sign Up"))
</button>
</Form>
)
</ReForm>
<div className="additional-section">
<span> (text("Already have an account? ")) </span>
<Link _to="/account/login"> (text("Login here")) </Link>
</div>
</div>
</div>
</div>
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment