Skip to content

Commit e5e6553

Browse files
committed
[PRAC/cont] Add logic to disable "Save" btn
Organiz availbl blocking modal "Save" btn (on "submit", "is-invalid"). Worth noting: - such protection against unnecessary "Saves" while there is invalid data in the form. core: B-3 / JS-BL
1 parent e606b80 commit e5e6553

File tree

2 files changed

+92
-24
lines changed

2 files changed

+92
-24
lines changed

core-courses/3-js-basic-level/practicum-js-basic-level/sb-crm-client/css/variables.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
--black: #333;
1919
--white: #fff;
2020
--bootstrap-red: #dc3545;
21+
--bootstrap-green: #198754;
2122
--main-purple: #9873FF;
2223
--light-purple: #B89EFF;
2324
--dark-purple: #8052FF;

core-courses/3-js-basic-level/practicum-js-basic-level/sb-crm-client/js/index.js

Lines changed: 91 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1342,6 +1342,8 @@
13421342
// ** организация "динамического" добавления строки контакта/row-contact (по нажатию "Добавить контакт" кнопки, в модальных/универсальных окнах)
13431343
function createModalContactsElement(context = {}) {
13441344
const { modalWrap, modalBodyAddContactsRowWrap, modalBodyAddBtn } = context; // получение необходимых элементов (через деструктуризациию входящего/передаваемого объекта)
1345+
const modalBodyForm = modalWrap.querySelector('form');
1346+
const saveButton = modalWrap.querySelector('#modal-body-save-btn');
13451347

13461348
if (modalContactsArr.length >= 10) return; // проверка количества контактов (не более 10)
13471349

@@ -1525,6 +1527,9 @@
15251527
}
15261528
}
15271529

1530+
// обновление состояния кнопки "Сохранить" (согласно действий с контактными инпутами)
1531+
updateSaveButtonState(modalBodyForm, saveButton);
1532+
15281533
// обновление/изменение отступов для li/вариантов выпадающего списка (для первого и последнего элементов)
15291534
updateDropItemPaddings(modalContactList);
15301535

@@ -1635,6 +1640,7 @@
16351640
modalContactXBtn.addEventListener('click', (event) => {
16361641
event.stopPropagation(); // исключение непредвиденных событий/поведения
16371642
deleteModalContactsElement(event, {
1643+
modalBodyForm,
16381644
modalBodyAddBtn,
16391645
modalBodyAddContactsRowWrap,
16401646
}); // удаление строки контактов (посредствам "X", передача context(a))
@@ -1645,6 +1651,7 @@
16451651
if (event.key === 'Enter') {
16461652
event.stopPropagation(); // исключение непредвиденных событий/поведения
16471653
deleteModalContactsElement(event, {
1654+
modalBodyForm,
16481655
modalBodyAddBtn,
16491656
modalBodyAddContactsRowWrap,
16501657
}); // удаление строки контактов (посредствам "X", передача context(a))
@@ -1811,7 +1818,9 @@
18111818
// ** удаление строки row-контакта в модальном окне (через "X" кнопку, с/без уточняющего сообщения)
18121819
function deleteModalContactsElement(event, context = {}) {
18131820
const clickedContactsXBtn = event.currentTarget; // получение ИМЕННО кнопки, а не/может внутренней иконки (согласно "размазанного" события)
1814-
const { modalBodyAddBtn, modalBodyAddContactsRowWrap } = context; // получение необходимых элементов (через деструктуризациию входящего/передаваемого объекта)
1821+
const { modalBodyForm, modalBodyAddBtn, modalBodyAddContactsRowWrap } =
1822+
context; // получение необходимых элементов (через деструктуризациию входящего/передаваемого объекта)
1823+
const saveButton = modalBodyForm.querySelector('#modal-body-save-btn'); // фиксация кнопки "Сохранить"
18151824

18161825
if (clickedContactsXBtn) {
18171826
tippy.hideAll(); // предварительное скрытие всех/вдруг "активных" tooltips (перед удалением искомой строки)
@@ -1862,6 +1871,9 @@
18621871
modalBodyAddContactsRowWrap.classList.add('d-none');
18631872
modalBodyAddBtn.classList.remove('modal-contact-btn-margin');
18641873
}
1874+
1875+
// обновление состояния кнопки "Сохранить" (после удалений строк контактов)
1876+
updateSaveButtonState(modalBodyForm, saveButton);
18651877
} else {
18661878
currentInput.focus(); // возврат фокуса искомому инпуту (после отмены удаления в confirm)
18671879
}
@@ -1994,6 +2006,8 @@
19942006
return;
19952007
}
19962008

2009+
const saveButton = modalBodyForm.querySelector('#modal-body-save-btn'); // фиксация кнопки "Сохранить"
2010+
19972011
// [СЕРВЕР] / обработка события "submit"
19982012
modalBodyForm.addEventListener(
19992013
'submit',
@@ -2016,41 +2030,94 @@
20162030
return feedback && feedback.textContent.trim() !== '';
20172031
});
20182032

2033+
// обновление состояния кнопки "Сохранить" (доступна, не доступна)
2034+
updateSaveButtonState(modalBodyForm, saveButton);
2035+
20192036
if (
20202037
!modalBodyForm.checkValidity() ||
20212038
validErrors.length > 0 ||
20222039
hasInvalidFeedback
20232040
) {
20242041
event.stopPropagation();
2025-
} else {
2026-
modalBodyForm.classList.add('was-validated'); // если всё "ок", т.е. нет ошибок, невалидных сообщений.. добавление всей форме валидационного класса (для/по Bootstrap)
2042+
return; // если есть ошибки, прекращение отработки
2043+
}
20272044

2028-
setTimeout(() => {
2029-
alert('Клиент успешно добавлен!'); // вывод сообщения об успешном добавлении клиента
2045+
modalBodyForm.classList.add('was-validated'); // если всё "ок", т.е. нет ошибок, невалидных сообщений.. добавление всей форме валидационного класса (для/по Bootstrap)
20302046

2031-
// очистка всех полей формы (удаление классов/сообщений ошибок)
2032-
allModalInputs.forEach((input) => {
2033-
input.value = '';
2034-
input.classList.remove('is-invalid');
2035-
});
2036-
modalBodyForm.classList.remove('was-validated'); // удаление класса "was-validated"
2047+
setTimeout(() => {
2048+
alert('Клиент успешно добавлен!'); // вывод сообщения об успешном добавлении клиента
20372049

2038-
// закрытие модального окна (через/посредствам Bootstrap API)
2039-
const bootstrapModal = bootstrap.Modal.getInstance(
2040-
modalBodyForm.closest('.modal')
2041-
);
2042-
if (bootstrapModal) {
2043-
bootstrapModal.hide();
2044-
}
2050+
// очистка всех полей формы (удаление классов/сообщений ошибок)
2051+
allModalInputs.forEach((input) => {
2052+
input.value = '';
2053+
input.classList.remove('is-invalid');
2054+
});
2055+
modalBodyForm.classList.remove('was-validated'); // удаление класса "was-validated"
20452056

2046-
// и напоследок.. выделение/показ только что добавленного клиента/строки
2047-
setTimeout(() => {
2048-
// movingToLastNewTableRow();
2049-
}, 300); // временная задержка, больше.. чтобы модальное окно успело закрыться
2050-
}, 200);
2051-
}
2057+
// закрытие модального окна (через/посредствам Bootstrap API)
2058+
const bootstrapModal = bootstrap.Modal.getInstance(
2059+
modalBodyForm.closest('.modal')
2060+
);
2061+
if (bootstrapModal) {
2062+
bootstrapModal.hide();
2063+
}
2064+
2065+
// и напоследок.. выделение/показ только что добавленного клиента/строки
2066+
setTimeout(() => {
2067+
// movingToLastNewTableRow();
2068+
}, 300); // временная задержка, больше.. чтобы модальное окно успело закрыться
2069+
}, 200);
20522070
},
20532071
false
20542072
);
2073+
2074+
// обновление состояния кнопки "Сохранить" (согласно ввода данных)
2075+
modalBodyForm.addEventListener('input', () =>
2076+
updateSaveButtonState(modalBodyForm, saveButton)
2077+
);
2078+
}
2079+
2080+
// ** организация блокировки доступности для модальной кнопки "Сохранить" (при "submit")
2081+
function updateSaveButtonState(modalBodyForm, saveButton) {
2082+
const allModalInputs = Array.from(
2083+
modalBodyForm.querySelectorAll('.modal-input')
2084+
);
2085+
2086+
// проверка на наличие невалидных инпутов (за исключением поля "Отчество", его "конкретного" сообщения)
2087+
const hasInvalidInputs = allModalInputs.some((input) => {
2088+
if (input.classList.contains('modal-patronymic-input')) {
2089+
const feedback = input
2090+
.closest('.modal__body-input-wrap')
2091+
.querySelector('.invalid-feedback');
2092+
if (
2093+
feedback &&
2094+
feedback.textContent.trim() !==
2095+
'Заполните поле "Отчество" или оставьте его пустым!'
2096+
) {
2097+
return true; // другие сообщения.. кнопка "Сохранить" будет не доступной
2098+
}
2099+
return false; // нужное сообщение.. будет доступна
2100+
}
2101+
return input.classList.contains('is-invalid');
2102+
});
2103+
2104+
// проверка обязательных полей ФИО (за исключением поля "Отчество")
2105+
const requiredInputsEmpty = allModalInputs.some(
2106+
(input) =>
2107+
input.required &&
2108+
!input.classList.contains('modal-patronymic-input') &&
2109+
input.value.trim() === ''
2110+
);
2111+
2112+
// проверка состояния модальной-формы (наличие "was-submitted", да/нет)
2113+
const wasSubmitted = modalBodyForm.classList.contains('was-submitted');
2114+
2115+
// блокировка кнопки, если был "submit" (если/есть, появились невалидные инпуты)
2116+
saveButton.disabled =
2117+
wasSubmitted && (hasInvalidInputs || requiredInputsEmpty);
2118+
2119+
// обновление/изменение состояния кнопки и прозрачности
2120+
saveButton.style.opacity = saveButton.disabled ? '0.5' : '1';
2121+
saveButton.style.cursor = saveButton.disabled ? 'help' : 'pointer';
20552122
}
20562123
})();

0 commit comments

Comments
 (0)