I'd like to have an input
represent a list of primitive (string, number) values so you can enter them as comma-separated values but update the model as an array:
"Transforms": [
{
"Fn": "TheFunctionName",
"Args": [ "arg1", 2, "arg3" ]
},
{
"Fn": "AnotherMethod",
"Args": [ 4.678 ]
},
]
Would be administered with:
{{#each Transforms:i}}
<li>
<input value="{{Fn}}" placeholder="Function Name" />
<input value="{{implode(Args)}}" placeholder="Function Arguments" />
</li>
{{/each}}
and would render something like:
* [ TheFunctionName ] [ "arg1", 2, "arg3" ]
* [ AnotherMethod ] [ 4.678 ]
Mainly so that I don't need to figure out a good UI to dynamically add/remove argument inputs (like bind on certain keypress, use buttons to add/remove fields, etc).
I could use a "placeholder" property for databinding, and probably observe that to update the actual property, but then I'd need to filter it out when 'serializing' the underlying model. It seems like I could use computed properties, which have a getter and a setter, but it's not clear from the docs how it works with nested properties in a list (i.e. there are many entries in data
with the Transforms
list).
The trick here is to not use two-way binding, but to use more traditional event handling techniques. This example listens for change
events on the second input (not input
events, which happen with each keystroke, since that would cause the cursor to jump about while the user is still typing) in each <li>
, and tries to evaluate its contents. Meanwhile, a formatter takes the arguments and turns them back into a string:
var ractive = new Ractive({
el: 'main',
template: '#template',
data: {
transforms: [
{
fn: 'TheFunctionName',
args: [ 'arg1', 2, 'arg3' ]
},
{
fn: 'AnotherMethod',
args: [ 4.678 ]
}
],
format: function ( args ) {
return args.map( JSON.stringify ).join( ', ' );
}
},
updateArgs: function ( index, str ) {
var keypath = 'transforms[' + index + '].args',
args = this.get( keypath );
try {
// or use JSON.parse, if you don't want to eval
this.set( keypath, eval( '([' + str + '])' ) );
} catch ( err ) {
// reset
this.set( keypath, null );
this.set( keypath, args );
}
}
});
<script src="http://cdn.ractivejs.org/edge/ractive.js"></script>
<main></main>
<script id='template' type='text/ractive'>
<h2>input</h2>
<ul>
{{#each transforms:i}}
<li>
<input value='{{fn}}' placeholder='function name'/>
<input
twoway='false'
value='{{format(args)}}'
placeholder='function arguments'
on-change='updateArgs(i,event.node.value)'
/>
</li>
{{/each}}
</ul>
<h2>output</h2>
<ul>
{{#each transforms}}
<li>
<p>function name: <strong>{{fn}}</strong></p>
<p>args:</p>
<ul>
{{#each args}}
<p>{{JSON.stringify(this)}} ({{typeof this}})</p>
{{/each}}
</ul>
</li>
{{/each}}
</ul>
</script>