nb-riot-rx-form
v1.4.1
Published
Reactive Functional Riot.js form validation with Bacon.js
Downloads
31
Maintainers
Readme
nb-riot-rx-form - Reactive Functional Riot.js form validation with Bacon.js
Reactive functional form validation library for Riot.js Using Bacon.js + Immutable as internal model.
Installation
$ bower install nb-riot-rx-form
or
$ npm install nb-riot-rx-form
Even better, use my yeoman generator rtjs (Working on it at the moment 2016-11-14)
Breaking change in V1.1.0
The version pre-v1.1.0 injects data into the form name / form field property directly into the current tag scope.
That leads to some unexpected side effects when using with other mixin. As of V1.1.0 Everything will be wrap
inside the $
+ formName
namespace. Check the new example below for more information.
EXAMPLE
import 'nb-riot-rx-form'; // just import and it will taken care of the rest
<app>
<div class="container">
<!-- (1) provide the tag / mixin with configuration, see (2) below -->
<nb-rx-form config={formConfig}>
<!--
(3) form submit handler - note the parent
onsubmit={parent.submitHandler}
Please see below at the $rxOnSubmit event for more explanation
-->
<form name="testForm" novalidate>
<!-- normal input -->
<div class={"row form-group": true , "has-success": $testForm.textInput.$valid , "has-error": $testForm.textInput.$fail}>
<div class="col-md-3">
<label class="control-label">
TEXT INPUT
</label>
</div>
<div class="col-md-9">
<input type="text" name="textInput" class="form-control" min="5" max="25" required />
</div>
</div>
<!-- normal input -->
<div class={"row form-group": true , "has-success": $testForm.numberInput.$valid , "has-error": $testForm.numberInput.$fail}>
<div class="col-md-3">
<label class="control-label">
NUMBER INPUT
</label>
</div>
<div class="col-md-9">
<input type="number"
name="numberInput" class="form-control" min="100" max="500" required />
<p class="help-text" if={$testForm.numberInput.$errors}>
{$testForm.numberInput.$errors}
</p>
</div>
</div>
<!-- normal input -->
<div class={"row form-group": true , "has-success": $testForm.emailInput.$valid , "has-error": $testForm.emailInput.$fail}>
<div class="col-md-3">
<label class="control-label">
EMAIL INPUT
</label>
</div>
<div class="col-md-9">
<input type="email" name="emailInput" class="form-control" rx-debounce="800" required />
</div>
</div>
<!-- with rx-validate option -->
<div class={"row form-group": true , "has-success": $testForm.validateInput.$valid , "has-error": $testForm.validateInput.$fail}>
<div class='col-md-3'>
<label class="control-label">
INPUT with Validate
</label>
</div>
<div class="col-md-9">
<input type="text"
name="validateInput"
class="form-control"
rx-pattern="[0-9]$"
required />
<p class="help-text" if={$testForm.validateInput.$errors}>
{$testForm.validateInput.$errors}
</p>
</div>
</div>
<!-- checkbox -->
<div class={"row form-group": true , "has-success": $testForm.checkboxInput.$valid , "has-error": $testForm.checkboxInput.$fail}>
<div class="col-md-3">
<label class="control-label">
CHECKBOX
</label>
</div>
<div class="col-md-9">
<div class='checkbox'>
<label>
<input type="checkbox" name="checkboxInput" required /> OPTION
</label>
</div>
</div>
</div>
<!-- radio -->
<div class={"row form-group": true , "has-success": $testForm.radioInput.$valid , "has-error": $testForm.radioInput.$fail}>
<div class="col-md-3">
<label class="control-label">
RADIO
</label>
</div>
<div class="col-md-9">
<div class="radio">
<label>
<input type="radio" name="radioInput" value="1" required /> ONE
</label>
<label>
<input type="radio" name="radioInput" value="2" /> TWO
</label>
</div>
</div>
</div>
<!-- select -->
<div class={"row form-group": true , "has-error": $testForm.selectInput.$fail , "has-success": $testForm.selectInput.$valid}>
<div class="col-md-3">
<label class="control-label">
SELECT
</label>
</div>
<div class="col-md-9">
<select class="form-control" name="selectInput" required>
<option value="">SELECT</option>
<option each={options} value={value}>{key}</option>
</select>
</div>
</div>
<!-- textarea -->
<div class={"row form-group": true , "has-success": $testForm.textareaInput.$valid}>
<div class="col-md-3">
<label class="control-label">
TEXTAREA
</label>
</div>
<div class="col-md-9">
<textarea name="textareaInput" class="form-control"></textarea>
</div>
</div>
<div class="row">
<div class="col-md-12">
<button class={'btn': true , 'btn-success': $testForm.testForm.$success , ' btn-primary': $testForm.testForm.$valid}
type="submit"
disabled={!$testForm.testForm.$valid}>
SUBMIT
</button>
</div>
</div>
</form>
<br />
<br />
<br />
<div class="panel panel-default">
<div class="panel-heading">DISPLAY DATA LIVE</div>
<div class="panel-body">
textArea: {$testForm.textareaInput.$value}
<br />
{$testForm.textareaInput.$valid}
</div>
</div>
</nb-rx-form>
</div><!-- END OF HTML -->
<script>
/**
* (2) providing the tag / mixin with default options see (1) above
*/
this.formConfig = {
debounce: 400,
debug: true
};
/**
* (3) form submit handler
* in the template level must prefix this method with
* `parent` because the form is in a child scope
* see next $rxOnSubmit
*/
this.submitHandler = () =>
{
// NOTE this object, its your form name prefix with $
this.$testForm.$ready( (data , formState) =>
{
// the `data` is key value pair of every qualify form field in this form
// the `formState` will tell you whether this from is valid or not
// formState.$valid then you can use this to determine what to do
// with the data
});
return false;
};
/**
* (4) listening to the $rxOnSubmit event instead of the $ready handler
* due to an unknown bug (at the time of this writing)
* when this module is being use in the wild. Other third parties library
* seems to interfere with the submit event and cause the data return failed
* instead you could now listen to a new event trigger internally
*/
this.on('$rxOnSubmit' , (results) =>
{
const [formData , formState] = results;
// same as (3)
});
/**
* init data on mount
* see the select options are provided here even the nb-rx-form tag wrapping
* the form itself. But since Riot's child tag automatically inherit from its
* parent, therefore this works
*/
this.on('mount' , () =>
{
console.log('app tag' , this);
this.update({
options: [
{key: 'One' , value: 1},
{key: 'Two' , value: 2},
{key: 'Three' , value: 3},
{key: 'Four' , value: 4},
{key: 'Five' , value: 5}
]
});
});
</script>
</app>
The above example is taken from our gh-pages
for development. A much longer API doc and article explaining about it is coming to the wiki.
Development
Clone this repository to your dev machine. Then checkout the gh-pages
branch:
$ git checkout gh-pages
You will see all the source files and a gulpfile.js
, first install all dependencies:
$ npm install && bower install
Then start gulp:
$ gulp dev
Joel Chu 2016 London
@TODO
- Re-test with Riot V.3.
- new
nb-form
sub tag. The idea is like Angularng-form
- Re-organize how the export works. Apart from export just the tag, also export the mixin(s) and the new tag(s). This will allow user to construct their form like a lego.
- The underlying data model require re-architect. The problem is we do this completely re-active at the moment (nothing happen nothing change) but at the initial stage when we might want to have the data to pass around (regardless it's empty or not) so we need to grab all the available model then push back to the current tag / mixin level.