GithubHelp home page GithubHelp logo

react-binding's Introduction

react-binding

React-binding is lightweight utility for two-way data binding in React.

import React from 'react';
import ReactDOM from 'react-dom';
import Binder from 'react-binding';

export default class Form extends React.Component {
  constructor(props){
    super(props);
    this.state = {data: {}};
  },
  render {
    return (
      <div>
        <input valueLink={Binder.bindToState(this,"data", "Employee.FirstName")} />
        <div>FirstName: {this.state.data.Employee.FirstName}</div>
      </div>
    )}
});

ReactDOM.render(
  <Form />,
  document.getElementById('content')
);

Features:

  • No dependencies.
  • Minimal interface - using path with dot notation.
  • Support for complex objects.
  • Support for collection-based structures - arrays and lists.
  • Support for value converters.
  • No need to define initial values, nested structures. Binder creates this for you.
  • Support concept for references to allow JSON to be used to represent graph information.

react-binding offers two-way data binding support for:

  • object properties with path expression (dot notation)
    • Binder.bindToState(this,"data","Employee.FirstName");
    • Binder.bindToState(this,"data","Employee.Contact.Email");
  • complex objects (json) with nested properties
    • Binder.bindTo(employee,"FirstName");
    • Binder.bindTo(employee,"Contact.Email");
  • collection-based structures - arrays and lists
    • model={Binder.bindArrayToState(this,"data","Hobbies")}
      • this.props.model.items.map(function(item){ return ();})
      • this.props.model.add()
      • this.props.model.remove(item)
  • supports for "value/requestChange" interface also to enable to use ReactLink attribute
    • valueLink={Binder.bindTo(employee,"FirstName")}
  • enables binding with value converters
    • supports both directions - format (toView) and parse (fromView)
    • support for converter parameter - valueLink={Binder.bindToState(this,"data", "Duration.From",converter, "DD.MM.YYYY")}
    • converter parameter can be data-bound - valueLink={Binder.bindToState(this,"data", "Duration.From",converter, this.state.format)}
  • usable with any css frameworks

Basic principle

Each bindTo return and uses interface called "value/onChange". Each bindTo component is passed a value (to render it to UI) as well as setter to a value that triggers a re-render (typically at the top location).

The re-render is done in the component where you bind to the state via (bindToState, bindArrayToState).

BindTo can be nested - composed to support components composition. Then path is concatenate according to parent-child relationship.

Get started

npm install react-binding
bower install react-binding
npm install -g browserify
npm install reactify
browserify ./index.js > bundle.js

minimal example

import React from 'react';
import ReactDOM from 'react-dom';
import Binder from 'react-binding';

export default class Form extends React.Component {
  constructor(props){
    super(props);
    this.state = {data: {}};
  },
  render {
    return (
      <div>
        <input valueLink={Binder.bindToState(this,"data", "Employee.FirstName")} />
        <div>FirstName: {this.state.data.Employee.FirstName}</div>
      </div>
    )}
});

ReactDOM.render(
  <Form />,
  document.getElementById('content')
);

Note: React-binding as mixins - use npm install [email protected]

Overview

bindToState(key,pathExpression)

It enables to bind to object property with path expression

<input type='text' valueLink={Binder.bindToState(this,"data","Employee.Contact.Email")} />
<TextBoxInput model={Binder.bindToState(this,"data","Employee.Contact.Email")} />
var TextBoxInput = React.createClass({
  render: function() {
    var valueModel = this.props.model;
    var handleChange = function(e){
      valueModel.value = e.target.value;
    }
    return (
      <input type='text' onChange={handleChange} value={valueModel.value} />
    )
  }
});

bindTo(parent,pathExpression)

It enables to bind to complex object with nested properties and reuse bindings in components.

  • binding to state at root level
  <PersonComponent personModel={Binder.bindToState(this,"data","Employee")} />
  <PersonComponent personModel={Binder.bindToState(this,"data","Deputy")} />
  • binding to parent
  <input type='text' valueLink={Binder.bindTo(this.props.personModel,"Contact.Email")} />
  • reuse bindings in component
var PersonComponent = React.createClass({
  render: function() {
    return (
      <div>
        <input type='text' valueLink={Binder.bindTo(this.props.personModel,"FirstName")} />
        <input type='text' valueLink={Binder.bindTo(this.props.personModel,"LastName")} />
        <input type='text' valueLink={Binder.bindTo(this.props.personModel,"Contact.Email")} />
      </div>
    );
  }
});

bindArrayToState(key,pathExpression)

It enables binding to collection-based structures (array). It enables to add and remove items.

  • binding to array
    <HobbyList model={Binder.bindArrayToState(this,"data","Hobbies")} />
  • access items (this.props.model.items)
    var HobbyList = React.createClass({
        render: function() {
            if (this.props.model.items === undefined) return <span>There are no items.</span>;

            var hobbies = this.props.model.items.map(function(hobby, index) {
                return (
                    <Hobby model={hobby} key={index} onDelete={this.handleDelete} />
                );
            },this);
            return (
                <div>{hobbies}</div>
            );
        }
    });
  • add new items (this.props.model.add(newItem?))
     handleAdd: function(){
            return this.props.model.add();
     },
  • remove exiting items (this.props.model.props.delete(item))
     handleDelete: function(hobby){
            return this.props.model.remove(hobby);
     },

bindArrayTo(parent,pathExpression)

It enables binding to collection-based structures (array) for nested arrays. It enables to add and remove items.

  • binding to array
    <HobbyList model={Binder.bindArrayTo(this,parent,"Hobbies")} />
  • access items (this.props.model.items)
    var HobbyList = React.createClass({
        render: function() {
            if (this.props.model.items === undefined) return <span>There are no items.</span>;

            var hobbies = this.props.model.items.map(function(hobby, index) {
                return (
                    <Hobby model={hobby} key={index} onDelete={this.handleDelete} />
                );
            },this);
            return (
                <div>{hobbies}</div>
            );
        }
    });
  • add new items (this.props.model.add(newItem?))
     handleAdd: function(){
            return this.props.model.add();
     },
  • remove exiting items (this.props.model.props.delete(item))
     handleDelete: function(hobby){
            return this.props.model.remove(hobby);
     },

Value converters

Value converters

  • format - translates data to a format suitable for the view
  • parse - convert data from the view to a format expected by your data (typically when using two-way binding with input elements to data).

Example - date converter -> using parameters 'dateFormat' is optional

var dateConverter = function() {
  this.parse = function (input, dateFormat) {
    if (!!!input) return undefined;
    if (input.length < 8) return undefined;
    var date = moment(input, dateFormat);
    if (date.isValid()) return date.toDate();
    return undefined;
  }
  this.format = function (input,dateFormat) {
    if (!!!input) return undefined;
    return moment(input).format(dateFormat);
  }
}

using converter

    <DatePicker label="From" model={Binder.bindToState(this,"data", "Duration.From", converter, 'DD.MM.YYYY')} error={this.validationResult().Duration.From}  />
    <DatePicker label="To" model={Binder.bindToState(this,"data", "Duration.To", converter, 'DD.MM.YYYY')}  error={this.validationResult().Duration.To} />

try in Plunker

References

JSON models trees, and most application domains are graphs. Binding supports concept for references to allow JSON to be used to represent graph information.

  • Each entity is inserted into a single, globally unique location in the JSON with a unique identifier.
  • Each reference is an object of the $type='ref' and must contain value as path to single, globally unique location in the JSON - {$type:'ref',value:['todosById',44]}
{
    todosById: {
        "44": {
            name: "get milk from corner store",
            done: false,
            prerequisites: [{ $type: "ref", value: ["todosById", 54] }]
        },
        "54": {
            name: "withdraw money from ATM",
            done: false,
            prerequisites: []
        }
    },
    todos: [
        { $type: "ref", value: ["todosById", 44] },
        { $type: "ref", value: ["todosById", 54] }
    ]
};

Examples

hobby form - data binding only

hobby form with validation using business-rules-engine

value converters

Contact

For more information on react-binding please check out my blog.

react-binding's People

Contributors

fliptaboada avatar rsamec avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

react-binding's Issues

Broken in IE8

Doesnt work in IE8 with shim/shams due to:

Object.defineProperty(PathObjectBinding.prototype, "requestChange", {
        get: function () {
            var _this = this;
            return function (value) {
                _this.value = value;
            };
        },
        enumerable: true,
        configurable: true
    });

Defining accessors (get or set) for non DOM elements is not supported under IE8 and cannot be emulated :(

Compatibility with browserify-shim

Hello!

I seem to having problem using react-binding with browserify-shim. I use gulp to bundle my app, and use this config:

"browserify-shim": {
"jquery": "global:$",
"react": "global:React",
"react-dom": "global:ReactDOM",
"react-bootstrap": "global:ReactBootstrap",
"react-router": "global:ReactRouter",
"react-binding": "global:ReactBinding",
"react-input-color": "global:ReactInputColor"
}

I include react-binding.js in index.html.

After all js is loaded in document, React app cannot find react-binding. It tries to do this in this code:

var _reactBinding = (typeof window !== "undefined" ? window['ReactBinding2'] : typeof global !== "undefined" ? global['ReactBinding2'] : null);

var _reactBinding2 = _interopRequireDefault(_reactBinding);

Do you know what is wrong and how to fix this?

Slow with arrays and ES6 classes.

Not sure what is going on, but when I bind to arrays the updates are extremely slow ()max 1-2 per second!. Do you have any idea what can be wrong?

export class ScheduleAdminView extends React.Component<Component, State> {
  constructor(props: Component) {
    super();
    this.state = {
      schedule: props.scheduleData.schedule
    };
  }



  render() {
    const schedule = scheduleData.schedule;
    const bind = Binder.bindToState.bind(null, this, 'schedule');

    return (
      <div>
        <form className={'ui form ' + clear} onSubmit={handleSubmit(save)}>
          <Segment>
            <SchedulePracticals model={Binder.bindArrayToState(this, 'schedule', 'items')} createPractical={createPractical} />
          </Segment>
        </form>
      </div>
    );
  }

export const SchedulePractical = ({ index, field }) => (
  <Form.Group key={index}>
    <Input model={Binder.bindTo(field, 'name')} width={9} />
  </Form.Group>
);

export const SchedulePracticals = ({model, allPracticals, createPractical }: ISchedulePracticalProps) => {
  let index: number;
  let field: string;
  return (
    <div>
      <For each="field" of={model.items} index="index">
        <SchedulePractical index={index} field={field} />
      </For>
    </div>
  );
};

Is possible define model without ".value" in component

In your example

<TextBoxInput model={this.bindToState("data","Employee.Contact.Email")} />
var TextBoxInput = React.createClass({
  render: function() {
    var valueModel = this.props.model;
    var handleChange = function(e){
      valueModel.value = e.target.value;
    }
    return (
      <input type='text' onChange={handleChange} value={valueModel.value} />
    )
  }
});

How i can set model to component (already written) if i just want set value as

var TextBoxInput = React.createClass({
  render: function() {
    var value= this.props.model;
    var handleChange = function(e){
      value= e.target.value;
    }
    return (
      <input type='text' onChange={handleChange} value={value} />
    )
  }
});

deeper binding question

First off I really like your project.

It looks like the structure of the initial state does not need to be defined up front, for example in the hobby sample, the initial state is basically an empty data object. As you type in the form fields, the structure of the data object is filled in, and you can see that in your pretty json.

  getInitialState: function() {
    return { data: {}};
  },

I tried to see how far that would go, and created a new plnkr that added 2 more inputs

        <TextBoxInput label="Street"  model={this.bindTo(this.props.personModel,"Address.Street")} />
        <TextBoxInput label="Phone"  model={this.bindTo(this.props.personModel,"Phone.Main.Number")} />

The 2 level deep object was filled in as expected, just like your existing inputs. The 3 level deep object did not fill in. This may be expected, but not sure. It would be slick if that worked as well.

edit ...
Adding the plnkr url for the updated sample

http://plnkr.co/edit/802962ElwKUd8tPFYan2?p=preview

BindTo object throws cannot call method getValue of undefined

The Binder.bindTo method throws an error
Cannot call method 'getValue' of undefined (STDERR) at PathParentBinding.Object.defineProperty.get [as value] react-binding\index .js:309:37)

Digging into the source i found Binder.bindTo to return a PathParentBinding, rather than an PathObjectBinding which is trying to access this.parent.source rather than this.parent to retrieve the value. Is there a particular reason for this behaviour? Shouldn't Binder.bindTo return a PathParentBinding?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.