npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

biro

v3.3.4

Published

redux + material ui forms

Downloads

102

Readme

biro

Form renderer for Redux and React.

pen picture

install

Install the module to your project:

$ npm install biro --save

usage

import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
import Biro from 'biro'
import tools from 'biro/tools'
import standardLibrary from 'biro/library/standard'

import { updatForm, submitForm } from '../actions/contactform'

const SCHEMA = [
	'firstname',   // this is turned into {type:'text',name:'firstname'}
	'surname',
	'email',
	{
		type:'text',
		name:'phone'
	}
]

export class ContactForm extends Component {
  render() {
    return (
      <div>
        <div>
          Contact Details
        </div>
        <div>
          <Biro 
          	data={this.props.data}
          	schema={SCHEMA}
          	library={standardLibrary}
          	meta={this.props.meta}
            validate={this.props.validate.bind(this)}
          	update={this.props.update} />
        </div>
        <div>
          <Button onClick={this.submitForm.bind(this)}>
            Submit Details
          </Button>
        </div>
      </div>
    )
  }

  validateForm(data) {
    var ret = {}
    if(data.firstname==data.surname){
      ret.firstname = 'Your surname and firstname are the same?'
    }
    return ret
  }

  submitForm() {
    if(this.props.meta.valid){
      return this.props.submit(this.props.data)
    }

    // this means the form is invalid - force all fields to
    // be dirty so the errors display
    this.props.update(this.props.data, tools.forceDirty(this.props.meta))
  }
}

function mapStateToProps(state, ownProps) {
  return {
  	data:state.contactform.data,
  	meta:state.contactform.meta || {}
  }
}

function mapDispatchToProps(dispatch, ownProps) {
  return {
    update:function(data, meta){
      dispatch(updatForm(data, meta))
    },
    submit:function(data){
      dispatch(submitForm(data, meta))
    }
  }
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(ContactForm)

properties

A full list of the properties you can use:

  • data - the current form data
  • meta - the current form meta
  • library - an object of form library components
  • layout - an object of form layout components
  • schema - the list of the fields to render
  • validate - an overall validation function that can work with all form data
  • update - a function to call when the form state changes
  • rawschema - do not apply the pre-processor to the schema
  • getContext - a function that is passed into each field component

update

The state management of form data is up to you. Biro expects an update property that is a function that is run whenever the state of the form has changed.

This function will receive 2 arguments - data and meta. These 2 arguments should be stashed somewhere in your reducer and fed back into the biro form.

Here is an example of the state object once our reducer has stashed the data and meta for a form - in this example the data and meta properties are exactly what is received from biro:

{
	data:{
		name:'bob',
		email:'[email protected]',
		address:null
	},
	meta:{
		valid:false,
    dirty:true,
    changed:true,
    fields:{
      name:{
        dirty:true,
        valid:true
      },
      email:{
        dirty:false,
        valid:true
      },
      address:{
        dirty:true,
        valid:false,
        error:'cannot be blank'
      }
    }

	}
}

When the update function is run - you should emit an action with the data and meta properties. Then - in the component that renders the form you should feed these values back as the data and meta props of the biro component.

schema

The schema is an array of objects describing each field in the form - each object has these properties:

  • name - what field of the data object to write the value to
  • type - what library component to use to render the field (default to 'text')
  • title - what to display next to the field (default to name)
  • validate - a function to validate the value

The schema entry can have any other fields also - for example a select list would need some options.

The validate function has the following signature and returns a string for an error or any other type of value for ok.

function(value){
	if(!value || value.indexOf('*')<0){
		return 'must contain an asterix'
	}
	else {
		return false
	}
}

library

A library element is a React component with the following properties:

  • title - the title of the field
  • value - the current form value to display
  • error - the current error to display
  • schema - the schema entry for this field
  • update - a function to run when the user changes the value
  • blur - a function to run when the user blurs focus (this will trigger validation)

It is responsible for renderering the GUI for the form field alone - not the title or other markup.

import React, { Component, PropTypes } from 'react'

class MyElement extends Component {

	handleChange(e) {
		this.props.update(e.target.value)
	}

	handleBlur(e) {
		this.props.blur()
	}

	render() {
		return (
			<input type="text" onChange={this.handleChange.bind(this)} onBlur={this.handleBlur.bind(this)} value={this.props.value} />
		)
	}
}

export default MyElement

layout components

The layout components control the wrapping markup for the form elements.

There are the following types of layout component - these should be properties of the layout object passed to the form:

  • row - renders markup around the component itself such as cols, title etc
  • form - renders an array of components
import standardLibrary from 'biro/library/standard'

class MyRowRenderer extends Component {
	render() {
		return (
			<div>
				<span>{this.props.field.title}</span>
				{this.props.children}
			</div>
		)
	}
}

class MyFormRenderer extends Component {
	render() {
		return (
			<div>
				This is a {this.props.title} form
				<hr />
				{this.props.children}
			</div>
		)
	}
}

const layout = {
	row:MyRowRenderer,
	form:MyFormRenderer
}

class MyForm extends Component {
	render() {
		return (
			<div>
				<Biro 
					data={this.props.data}
					meta={this.props.meta}
					library={standardLibrary} 
					schema={SCHEMA}
					layout={layout}
					update={update} />
			</div>
		)
	}
}

export default MyForm

initialize form

By providing a null meta property you are resetting the form state - do this to assign a new set of data to the form.

When biro notices a null meta property it will immediately call the update function with the meta filled in with the current validation status.

validate form

Errors are displayed for form fields if the error property and the dirty property of the meta for that field are set.

When you initially assign data to a form (by passing a null meta prop) - all of the fields will have dirty=false even though some may have errors.

Biro validates continously and so at any given moment the validation state of the form can be calculated using the meta property.

The dirty property must be true for a form error to display - this means if a user clicks submit before entering any information we would want to not submit and instead display the errors.

You can use the biro/tools library to do this (as in the example at the top of the page).

The changed property of the meta data (and each field) is set to true simply if the value has changed.

The validate property of the form is a function that accepts an object (the data entered into the form) and returns an object with a property for each of the errors.

function(data, meta){
  var ret = {}
  if(data.password!=data.password2 && meta.password.dirty && meta.password2.dirty){
    ret.password = 'passwords must match'
  }
  return ret
}

schema pre-processors

By mapping the schema you give to a form and injecting various validation functions, you can create lots of various input types quickly:

function validateEmail(val){
  return val.indexOf('@')>0 ? null : 'invalid email address'
}

var schema = [{
  name:'name'
},{
  name:'email',
  type:'email'
}]

schema = schema.map(field => {
  if(field.type=='email'){
    field.validate = validateEmail
    field.type = 'text'
  }
  return field
})

There are some built-in schema processors that will apply commonly useful features (unless you set the rawschema property):

  • required - setting required=true in the schema will apply a required validation

license

MIT