Published
Edited
Feb 7, 2021
1 star
Insert cell
Insert cell
Insert cell
Insert cell
viewof app1 = component_app({
header: "Todo list",
todos: ["Buy Apples", "Buy Oranges"],
footer: "Thank you"
})
Insert cell
app1
Insert cell
viewof app2 = component_app({
header: "Another Todo list",
todos: ["Eat Apples", "Eat Oranges", "Eat Bananas"],
footer: "Thanks!"
})
Insert cell
app2
Insert cell
component_app = props => {
var state;
var setter = value => {
state = value;
if (dom) {
dom.value = state;
dom.dispatchEvent(new CustomEvent("input"));
}
};

var dom = html`<div class="app">
${component_header({ title: props.header })}
${component_todo({ todos: props.todos, setter: setter })}
${component_footer({ title: props.footer })}
</div>`;

dom.value = state;

return dom;
}
Insert cell
component_header = props => {
return html`<h1 class="header">${props.title}</h1>`;
}
Insert cell
Insert cell
component_todo = props => {
let root = html`<div>`;
let todos;

// Inserts a new todo before the specified index.
function addTodo(i = todos.length) {
const newTodos = todos.slice();
newTodos.splice(i, 0, "");
setTodos(newTodos);
root.children[i].querySelector("input").focus();
}

// Modifies the todo at the given index to have the given value.
function modifyTodo(i, value) {
const newTodos = todos.slice();
newTodos[i] = value;
setTodos(newTodos);
//root.children[i].querySelector("input").focus();
}

// Removes the todo at the given index, focusing the todo before.
function removeTodo(i) {
const newTodos = todos.slice();
newTodos.splice(i, 1);
setTodos(newTodos);
root.children[i - 1].querySelector("input").focus();
}

// Renders the view, like a React useState hook.
function setTodos(newTodos) {
todos = newTodos.map(t => t.toUpperCase());

let new_root = html`<div class="todos">
${todos.map(
(todo, i) => html`<div>
${html`<input value=${todo}
oninput=${event =>
modifyTodo(i, event.currentTarget.value)}>`}
${html`<button onclick=${() => removeTodo(i)}>-`}
</div>`
)}
${html`<button onclick=${() => addTodo()}>+`}
</div>`;

//Diff at top level render instead? Is this a good idea?
//No, it's bad, the input focus is lost while editing
morph(root, new_root);

//Just replace instead of morph:
//root.replaceWith(new_root);
//root = new_root; //when the node mounts for the first time

//let the parent handle the data
//root.value = todos;
//root.dispatchEvent(new CustomEvent("input"));
props.setter(todos);
}

setTodos(props.todos);
return root;
}
Insert cell
Insert cell
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more