//ДЗ: Functional OOP //Password //LoginForm //LoginForm Constructor //Password Verify let wrapper = document.querySelector(".wrapper"); // Сюда буду создавать поле ввода для пароля function Password(parent, open){ // Функция конструктор let container = document.createElement("div"); // Создаю контейнер для инпутов let button = document.createElement("button"); // Создание кнопки button.disabled = true; // Отключаю кнопку т.к. при создании формы инпут логина по умолчанию пустой let openCheck = open; // Переменная с которой я буду сравнивать открытость текста в поле ввода let buildInputs = function(type, innerText) { // Функция для создания одних и тех же инпутов с разными типами let input = document.createElement("input"); let label = document.createElement("label"); input.type = type; label.innerText = innerText; label.appendChild(input); return label; } let toggleHiddenPassword = function(type, open) { // Функция для переключения состояния инпута с паролем с видимого на невидимый и переключение чекбокса inputPassword.type = type; inputCheckbox.checked = open; }; let toggleHiddenCheckPassword = function(check) { // Функция для скрытия повтора пароля если Password в открытом режиме if(check) { container.removeChild(checkPasswordLabel); } else { container.insertBefore(checkPasswordLabel, container.children[2]); } }; let verifyPassword = function(additional = true) { // Проверка на наличие символов в логине и пароле, если в одном из полей пусто то кнопка disabled if(inputLogin.value.length > 0 && inputPassword.value.length > 0 && additional) { button.disabled = false; } else { button.disabled = true; } } container.appendChild(buildInputs("login", "Login")); // Собираю форму ввода container.appendChild(buildInputs("password", "Password")); container.appendChild(buildInputs("password", "Повторите пароль")).classList.add("check-password"); // Тут добавил класс что бы различать два инпута, type = password, друг от друга container.appendChild(buildInputs("checkbox", "Показать пароль")); container.appendChild(button).innerText = "Отправить"; parent.appendChild(container); let inputLogin = container.querySelector("input[type='login']"); // Нахожу все инпуты и присваиваю их в переменные для дальнейшей работы с ними let inputPassword = container.querySelector("input[type='password']"); let inputCheckPassword = container.querySelector(".check-password input"); let checkPasswordLabel = inputCheckPassword.parentElement; let inputCheckbox = container.querySelector("input[type='checkbox']"); open == true ? toggleHiddenPassword("text", true) : toggleHiddenPassword("password", false); // Один раз запускаю функцию toggleHiddenPassword для корректной отрисовки инпутов в зависимости от переданных параметров в конструкторе if(open == true) { // Скрываю inputCheckPassword если конструктор вызвали с параметром open = true, то есть когда Password в открытом режиме container.removeChild(container.querySelector(".check-password")); } this.setValue = function(value) { inputPassword.value = value; }; this.getValue = function() { return inputPassword.value; }; this.setOpen = function(hidden) { hidden == true ? toggleHiddenPassword("text", true) : toggleHiddenPassword("password", false); openCheck = hidden; toggleHiddenCheckPassword(hidden); }; this.getOpen = function() { return openCheck; }; container.oninput = (evt) => { // Навешиваю слушатель события на весь контейнер, а не на каждый инпут в отдельности if(evt.target.type == "text" || evt.target.type == "password") { // Тут идет проверка что изменения происходят на инпутах типа text или password для передачи value в onChange this.onChange(evt.target.value); } else { // Если эти изменения в инпуте с другим типом (Остается только чекбокс) то я запускаю setOpen с противоположным значением openCheck, как бы переключаю значение на противоположное this.setOpen(!openCheck); this.onOpenChange(openCheck); } if(openCheck == true) { // Тут идет проверка, если Password в открытом режиме то мне не нужно проверять на совпадение value у inputPassword и inputCheckPassword verifyPassword(); } else { verifyPassword(inputPassword.value == inputCheckPassword.value); } }; }; let p = new Password(wrapper, true); p.onChange = data => console.log(data); p.onOpenChange = open => console.log(open); p.setValue('qwerty'); console.log(p.getValue()); p.setOpen(false); console.log(p.getOpen()); //Form //Отображение //Sync //Validators //Many Inputs //Validators messages //OkButton //CancelButton let formContainer = document.querySelector(".form-container"); // Контейнер для формы function Form(el, data, okCallback, cancelCallback){ let formBody = document.createElement('div') let okButton = document.createElement('button') let table = document.createElement("table"); // Создаю таблицу, в ней буду отрисовывать key и value из data let copyData = data; // Копия объекта data для возможности перейти к дефолтным значениям okButton.innerHTML = 'OK' let cancelButton = document.createElement('button') cancelButton.innerHTML = 'Cancel' formBody.innerHTML = '

тут будет форма после перервы

' if (typeof okCallback === 'function'){ formBody.appendChild(okButton); okButton.onclick = (e) => { console.log(this) this.okCallback(this.data) // Отдаю текущий поредактированный объект data первым параметром в okCallback } } if (typeof cancelCallback === 'function'){ formBody.appendChild(cancelButton); cancelButton.onclick = cancelCallback } let trCreator = function(key, value, trElement, labelElement, inputElement, inputType) { // Функция для корректной сборки таблицы и расположению в ней key и value for(let i = 0; i < 2; i++) { let td = document.createElement("td"); if(i == 0) { // В tr нужно поместить два элемента tb, в первом должен быть label с key внутри, а во втором input, собсна тут я этим и занимаюсь labelElement.innerText = key; labelElement.htmlFor = key; td.appendChild(labelElement); } else { inputElement.value = value; inputElement.id = key; inputElement.type = inputType; td.appendChild(inputElement); } trElement.appendChild(td); } return trElement; }; let inputCreators = { String(key, value, oninput){ let tr = document.createElement("tr"); let label = document.createElement("label"); let input = document.createElement("input"); input.oninput = () => oninput(input.value); return trCreator(key, value, tr, label, input, "text"); }, Boolean(key, value, oninput){ let tr = document.createElement("tr"); let label = document.createElement("label"); let input = document.createElement("input"); if(value == true) { input.checked = true; } input.oninput = () => oninput(input.checked); return trCreator(key, value, tr, label, input, "checkbox"); }, Date(key, value, oninput){ let tr = document.createElement("tr"); let label = document.createElement("label"); let input = document.createElement("input"); let timespan = value.getTime(); let dateValue = ((new Date(timespan - value.getTimezoneOffset() * 60 * 1000)).toISOString()).slice(0, -14); input.oninput = () => oninput(new Date(input.value)); return trCreator(key, dateValue, tr, label, input, "date"); } } for(let [key, value] of Object.entries(data)) { // Итерирую объект data и создаю для каждой пары ключ-значение input let inputCreatorFunction = inputCreators[value.constructor.name]; let tr = inputCreatorFunction(key, value, (newValue) => { if(this.validators[key] != undefined) { // Если this.validator[key] не равен undefined для этого input предусмотрена валидация и поэтому ее нужно запустить if(typeof(this.validators[key](newValue, key, data, tr.querySelector(`#${key}`))) == "string") { // Если все это чудо вернуло тип данных который равен "String" то значит поле не прошло валидацию let span = document.createElement("span"); // Тут будет лежать текст ошибки span.innerText = this.validators[key](newValue, key, data, tr.querySelector(`#${key}`)); // Собсна пишу сам текст ошибки в span span.style.position = "absolute"; // Задаю спану absolute что бы табличка не скакала когда я его добавлю в tr tr.style.backgroundColor = "red"; tr.appendChild(span); okButton.disabled = true; // Отключаю кнопку okButton если форма не прошла валидацию } else { tr.style.backgroundColor = "inherit"; data[key] = newValue; if(tr.querySelector("span") != null) { // Тут идет проверка на наличие span с ошибкой, если его нет то удалять не нужно tr.removeChild(tr.querySelector("span")); okButton.disabled = false; // Включаю кнопку okButton если форма прошла валидацию } }; } else { data[key] = newValue; } }); table.appendChild(tr); } formBody.insertBefore(table, formBody.children[1]); el.appendChild(formBody); this.okCallback = okCallback this.cancelCallback = cancelCallback this.data = data this.validators = {} } let form = new Form(formContainer, { name: 'Anakin', surname: 'Skywalker', married: true, birthday: new Date((new Date).getTime() - 86400000 * 30*365) }, () => console.log('ok'),() => console.log('cancel') ) form.okCallback = (e) => console.log(e); form.validators.surname = (value, key, data, input) => value.length > 2 && value[0].toUpperCase() == value[0] && !value.includes(' ') ? true : 'Wrong name' console.log(form)