#! let a = 1; #! let b = 2; #! let c = a + b; #! console.log(c); // 3 #! a = 3; #! console.log(c); // 5
#! const a = rdot(1);
#! const b = rdot(2);
#! const c = rdot(() => a + b); // (!) Зависимость
#! c.onValue(val => console.log("c: " + val)); // "c: 3"
#! a.set(3); // "c: 5"
#! b.set(7); // "c: 10"
<form name="counter"> <button name="up" type="button">+</button> <button name="down" type="button">-</button> <input name="result" readonly/> </form>
const form = document.forms.counter;
#! let sum = 0;
#!+ function counter(x) {
sum += x;
#! render();
#!- }
#!+ function render() {
form.result.value = sum;
#! }
#! form.up.addEventListener("click", render.bind(null, +1));
#! form.down.addEventListener("click", render.bind(null, -1));
#! render();
const form = document.forms.counter; #!+ // Счетчик + сеттер, который при получении нового значения, прибовляет к нему старое #!- const counter = rdot(0, (newValue, oldValue) => newValue + (oldValue|0)); #! // Создаем «реактивный поток» на основе события `click`: #! rdot.fromEvent(form.up, "click").onValue(() => counter.set(+1)); #! rdot.fromEvent(form.down, "click").onValue(() => counter.set(-1)); #!+ // Связываем значение реактивной переменной с элементом формы #!- counter.assign(form.result, "value");
<form name="reg"> <div> <input placeholder="Username" name="username"/> <span></span> </div> <div> <input placeholder="Fullname" name="fullname"/> <span></span> </div> <button name="send">Reg</button> </form>
const form = document.forms.reg;
#!+ // Создаем реактивную двухстороннюю связку с Input-элементом
const username = rdot.dom(form.username);
#!- const fullname = rdot.dom(form.fullname);
#!+ // Реактивное правило валидации
#!- const validate = rdot(() => username().length > 0 && fullname().length > 0);
#!+ // Связываем кол-во введенных символов и их вывод в соответствующем DOM-элементе
[username, fullname].forEach(dot => {
#!+ dot
#!- .map(value => value.length)
#! .assign(dot.el.nextElementSibling);
#!- });
#!+ // Связываем правило валидации с состоянием кнопки
#!- validate.not().assign(form.send, "disabled");
const KEY_ENTER = 13;
const FILTER_ALL = "/";
const FILTER_ACTIVE = "/active";
const FILTER_COMPLETED = "/completed";
const newTodoEl = document.querySelector(".new-todo");
const listEl = document.querySelector(".todo-list");
const footerEl = document.querySelector(".footer");
const filtersEl = footerEl.querySelectorAll(".filters a");
const todoCountEl = footerEl.querySelector(".todo-count");
const clearCompletedEl = footerEl.querySelector(".clear-completed");
// Реактивные переменные
#!+ const location = rdot
.fromEvent(window, "hashchange")
#!- .map(value => window.location.href.split('#')[1] || FILTER_ALL);
#!+ // Полле ввода нового todo
#!- const newTodo = rdot.dom(newTodoEl);
#!+ // Реактивный список дел
#!- const todosStorage = new rdot.Model.List();
#!+ // Статистика
const stats = new rdot.Model({
#! left: 0, // сколько осталось
#! completed: 0 // всего
#!- });
// Слушаем `Enter`
#!+ rdot.fromEvent(newTodo.el, "keydown").onValue(evt => {
if (evt.keyCode === KEY_ENTER) {
#!+ todosStorage.unshift(new rdot.Model({
value: newTodo(),
completed: false
#!- }));
#! newTodo.set(""); // clear
}
#!- });
// Видимость подвала
const footerVisiblity = todosStorage
.map(todos => todos.length > 0);
#!+ footerVisiblity
#! .map(state => state ? "" : "none")
#! .assign(footerEl.style, "display");
#!+ // Выбранный фильтр
#!- location
#! .filter(filter => footerVisiblity()) // (!) Зависимость
#!+ .onValue(filter => {
[].forEach.call(filtersEl, a => {
a.className = a.href.split("#")[1] === filter ? "selected" : "";
});
#!- });
// Подсчет статистики
todosStorage.fetch(todos => {
let left = 0;
let completed = 0;
#!+ todos.forEach(todo => {
left += !todo.completed();
completed += todo.completed();
#!- });
#! stats.left.set(left);
#! stats.completed.set(completed);
});
// Осталось дел
stats.left
#! .map(cnt => `${cnt} ${cnt === 1 ? 'item' : 'items'}`)
#! .assign(todoCountEl);
#!+ // Выполненых дел
#!- stats.completed
#! .map(cnt => cnt ? "" : "none")
#! .assign(clearCompletedEl.style, "display");
// Обновление списка
#!+ rdot
#! .combine([location, todosStorage]) // (!) Зависимость
#!+ .arrayFilter(([filter, todos]) => ({
#! array: todos,
#!+ callback(todo) {
return (
(FILTER_ALL === filter) ||
(FILTER_ACTIVE === filter && !todo.completed()) ||
(FILTER_COMPLETED === filter && todo.completed())
);
#!- }
#!- }))
#! .onValue(renderTodosList)
#!- ;