|
| 1 | +# Features |
| 2 | + |
| 3 | +- Create links to state's attributes |
| 4 | + var nameLink = Link.state( this, 'name' ); |
| 5 | +- Create links to nested arrays and objects |
| 6 | + var phonebookLink = Link.state( this, 'phonebook' ); |
| 7 | + |
| 8 | + var list = phonebookLink.map( itemLink => ( |
| 9 | + <div> |
| 10 | + <input valueLink={ itemLink.at( 'name' ) } /> |
| 11 | + </div> |
| 12 | + )); |
| 13 | + |
| 14 | +- |
| 15 | + |
| 16 | + |
| 17 | +# Installation |
| 18 | + |
| 19 | +`npm install valuelink` |
| 20 | + |
| 21 | +# Data binding examples |
| 22 | + |
| 23 | +Here are the set of examples for typical `nestedreact` data binding use cases. |
| 24 | + |
| 25 | +Each section contains custom databound component, model definitions, and usage examples. |
| 26 | + |
| 27 | +Somewhere at the top level component(s) there must be the declaration linking model updates to UI. Either models must be (nested) members of some component's state (which will update UI even in case of deep changes), or you may link component updates to changes of models and collections passed in props. In the last case, you will need to add following line to top or middle-level component definition: |
| 28 | + |
| 29 | +``` |
| 30 | + listenToProps : 'myModel myCollection' |
| 31 | +``` |
| 32 | + |
| 33 | +It's generally advised to keep stateful components at the top level, and use `listenToProps` in the middle level for optimization purposes if you want local updates. Keep you bottom-level components pure, and try to do the same for the most of your middle level (`listenToProps` used wisely won't hurt). For further information on this topic consult the top-level guide. |
| 34 | + |
| 35 | +## Checkboxes |
| 36 | + |
| 37 | +Standard `<input/>` will work. Custom Checkbox component might be implemented like this: |
| 38 | + |
| 39 | +```javascript |
| 40 | +const Checkbox = ({ className = 'checkbox', checkedLink }) => ( |
| 41 | + <div className={ className + ( checkedLink.value ? ' selected' : '' ) } |
| 42 | + onClick = { checkedLink.update( x => !x ) } |
| 43 | + /> |
| 44 | +); |
| 45 | +``` |
| 46 | + |
| 47 | +Examples will assume working with custom Checkbox. |
| 48 | + |
| 49 | +### Binding to boolean model attributes |
| 50 | + |
| 51 | +```javascript |
| 52 | +import { Model } from 'nestedtypes' |
| 53 | + |
| 54 | +const MyModel = Model |
| 55 | + .defaults({ |
| 56 | + option1 : true, |
| 57 | + option2 : false |
| 58 | + }); |
| 59 | + |
| 60 | +const CheckboxGroup = ({ model /* instanceof MyModel */ }) => ( |
| 61 | + <div> |
| 62 | + <div> |
| 63 | + <Checkbox checkedLink={ model.getLink( 'option1' ) } /> |
| 64 | + Option 1 |
| 65 | + </div> |
| 66 | + <div> |
| 67 | + <Checkbox checkedLink={ model.getLink( 'option2' ) } /> |
| 68 | + Option 2 |
| 69 | + </div> |
| 70 | + </div> |
| 71 | +); |
| 72 | +``` |
| 73 | + |
| 74 | +### Binding to array of selected options |
| 75 | + |
| 76 | +```javascript |
| 77 | +import { Model } from 'nestedtypes' |
| 78 | + |
| 79 | +const MyModel = Model |
| 80 | + .defaults({ |
| 81 | + options : [ 'option1' ] |
| 82 | + }); |
| 83 | + |
| 84 | +const CheckboxGroup = ({ model /* instanceof MyModel */ }) => { |
| 85 | + const link = model.getLink( 'options' ); |
| 86 | + |
| 87 | + return ( |
| 88 | + <div> |
| 89 | + <div> |
| 90 | + <Checkbox checkedLink={ link.contains( 'option1' ) } /> |
| 91 | + Option 1 |
| 92 | + </div> |
| 93 | + <div> |
| 94 | + <Checkbox checkedLink={ link.contains( 'option2' ) } /> |
| 95 | + Option 2 |
| 96 | + </div> |
| 97 | + </div> |
| 98 | + ); |
| 99 | +}; |
| 100 | +``` |
| 101 | + |
| 102 | +### Binding to collection of selected models |
| 103 | + |
| 104 | +```javascript |
| 105 | +import { Model } from 'nestedtypes' |
| 106 | + |
| 107 | +const MyModel = Model |
| 108 | + .defaults({ |
| 109 | + all : Some.Collection, |
| 110 | + selected : Collection.subsetOf( 'all' ) |
| 111 | + }); |
| 112 | + |
| 113 | +const CheckboxGroup = ({ model /* instanceof MyModel */ }) => { |
| 114 | + const { all, selected } = model; |
| 115 | + |
| 116 | + return ( |
| 117 | + <div> |
| 118 | + { all.map( model => ( |
| 119 | + <div> |
| 120 | + <Checkbox checkedLink={ selected.getLink( model ) } /> |
| 121 | + { model.displayName } |
| 122 | + </div> |
| 123 | + ))} |
| 124 | + </div> |
| 125 | + ); |
| 126 | +}; |
| 127 | +``` |
| 128 | + |
| 129 | +## Radio Groups |
| 130 | + |
| 131 | +For the radio groups you will need custom Radio component. It's very similar to custom Checkbox one, |
| 132 | +with one difference in click handler: |
| 133 | + |
| 134 | +```javascript |
| 135 | +const Radio = ({ className = 'radio', checkedLink }) => ( |
| 136 | + <div className={ className + ( checkedLink.value ? ' selected' : '' ) } |
| 137 | + onClick = { checkedLink.update( () => true ) } |
| 138 | + /> |
| 139 | +); |
| 140 | +``` |
| 141 | + |
| 142 | +In this example, we bind radio to string values. It's not required for them to be strings. |
| 143 | + |
| 144 | +```javascript |
| 145 | +import { Model } from 'nestedtypes' |
| 146 | + |
| 147 | +const MyModel = Model |
| 148 | + .defaults({ |
| 149 | + option : 'option1' |
| 150 | + }); |
| 151 | + |
| 152 | +const RadioGroup = ({ model /* instanceof MyModel */ }) => { |
| 153 | + const link = model.getLink( 'option' ); |
| 154 | + |
| 155 | + return ( |
| 156 | + <div> |
| 157 | + <div> |
| 158 | + <Radio checkedLink={ link.equals( 'option1' ) } /> |
| 159 | + Option 1 |
| 160 | + </div> |
| 161 | + <div> |
| 162 | + <Radio checkedLink={ link.equals( 'option2' ) } /> |
| 163 | + Option 2 |
| 164 | + </div> |
| 165 | + </div> |
| 166 | + ); |
| 167 | +}; |
| 168 | +``` |
| 169 | + |
| 170 | +## Input fields |
| 171 | + |
| 172 | +Standard `<input>` will work. You may implement custom input controls to handle complex scenarios |
| 173 | +with validation and appearance. |
| 174 | + |
| 175 | +```javascript |
| 176 | +const Input = ({ valueLink, ...props }) => ( |
| 177 | + <div className='wrapping' |
| 178 | + <input {...props} value={ valueLink.value } onChange={ e => valueLink.set( e.target.value ) }/> |
| 179 | + </div> |
| 180 | +); |
| 181 | +``` |
| 182 | + |
| 183 | +### Binding to model attributes |
| 184 | + |
| 185 | +```javascript |
| 186 | +import { Model } from 'nestedtypes' |
| 187 | + |
| 188 | +const MyModel = Model |
| 189 | + .defaults({ |
| 190 | + number : 0, |
| 191 | + string : '' |
| 192 | + }); |
| 193 | + |
| 194 | +const InputGroup = ({ model /* instanceof MyModel */ }) => ( |
| 195 | + <div> |
| 196 | + <label> |
| 197 | + Number: |
| 198 | + <input type='number' valueLink={ model.getLink( 'number' ) } /> |
| 199 | + </label> |
| 200 | + <label> |
| 201 | + String: |
| 202 | + <input valueLink={ model.getLink( 'string' ) } /> |
| 203 | + </label> |
| 204 | + </div> |
| 205 | + ); |
| 206 | +}; |
| 207 | +``` |
| 208 | + |
| 209 | +### Binding to an array of strings |
| 210 | + |
| 211 | +The same technique may be used to bind to an array or hash of strings. First, take a link to this |
| 212 | +attribute. Next, use `link.map` method to iterate through elements links created for you. |
| 213 | + |
| 214 | +`link.map` will internally execute `link.at( key )` method to create a link to the plain object or array element. |
| 215 | +These methods may be used manually to create binding for the structures of any particular depth and complexity. |
| 216 | + |
| 217 | +However, for the JS data with known structure it's recommended to wrap them in models. |
| 218 | + |
| 219 | +```javascript |
| 220 | +import { Model } from 'nestedtypes' |
| 221 | + |
| 222 | +const MyModel = Model |
| 223 | + .defaults({ |
| 224 | + strings : [ 'first', 'second' ] |
| 225 | + }); |
| 226 | + |
| 227 | +const InputGroup = ({ model /* instanceof MyModel */ }) => ( |
| 228 | + <div> |
| 229 | + { model.getLink( 'strings' ).map( strLink => ( |
| 230 | + <div> |
| 231 | + <input type='number' valueLink={ strLink } /> |
| 232 | + </div> |
| 233 | + )) } |
| 234 | + </div> |
| 235 | + ); |
| 236 | +}; |
| 237 | +``` |
0 commit comments