Published
Edited
May 12, 2020
Fork of Form Input
Importers
2 stars
Insert cell
Insert cell
Insert cell
viewof formOutput = form(
html`<form>
<div>Change the inputs and see the value change below. Note the extra field from the transform.</div>
<div>
<input type="text" name="message" value="Hi"> message
</div>
<div>
<input type="range" name="slider" min=0 max=100 value="33"> slider
</div>
</form>`,
{ transform: val => ({ ...val, added: val.slider * 2 }) }
)
Insert cell
formOutput
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
state
Insert cell
Insert cell
viewof state = new View({ message: "Initial message", hue: 180, size: 24 })
Insert cell
md`---
## *bindContainer() example*

\`// TODO: This section\`
`
Insert cell
Insert cell
// TODO: Consider adding an inverse option that lets you invert the transformation
// TODO: Add a hook into validation somewhere, perhaps in transform if it is also passed the old value
// but then needs some mechanism to reject the change
// TODO: Consider rewriting the view class as a missing property Proxy
form = function(formHtml, opts) {
const { trigger, transform } = { trigger: triggers.input, ...opts };
const container = html`<div>${formHtml}`;

// Set up the container object by adding the form and correct triggers
container.form = formHtml;
trigger(container);

// Function to call to pull a value from the input fields when the container's input event is triggered
container.setValue = () => container.value = transform
? transform(getFormValue(formHtml))
: getFormValue(formHtml);

// Function to push a value into the input fields when it is updated from a binding (or manually)
container.renderValue = value => setFormValue(formHtml, container.value);

// Pull the initial value and return
container.setValue();
return container;
}
Insert cell
Insert cell
Insert cell
Insert cell
boundForm = function(view, formHtml, opts) {
return bindContainer(view, form(formHtml, opts));
}
Insert cell
// TODO: Can this be generalized to work with any value, not just objects (forms will still require objects)?
function bindContainer(view, container) {
// Define bi-directional event handlers
// The view updater is fired without details and sets the view's value to the current container value
// The container updater is then fired each time the view is set (including above), and is called with the state in details
const updateView = () => view.value = { ...view.value, ... container.value }
const updateContainer = () => {
container.value = { ...container.value, ...view.value }
// If the container requires its display to be updated, it should provide a renderValue method
if (typeof container.renderValue === 'function') container.renderValue(container.value)
}
// Attach the event handlers
container.addEventListener('input', updateView)
view.addEventListener('input', updateContainer)
// When first loading, pull the initial value from the state
updateContainer(new CustomEvent('input', { detail: { ...view.value } }));
// Destroy the event handlers when disposed
// Probably does not require explicitly destroying the one the container
return Generators.disposable(container, () => {
view.removeEventListener('input', updateContainer)
container.removeEventListener('input', updateView)
});
}
Insert cell
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more