#! 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) #!- ;