@djon_pulse

Как грамотно сделать 3 состояния чекбокса в фильтре Angular?

Доброго времени суток.
Столкнулся с достаточно сложной для своего уровня знаний задачей.

Работаю с проектом на Angular, больше в плане верстки. С TS не знаком, но могу разобраться с примером.
На сайте есть фильтр с 3 уровнями вложенности:
- Уровень 1
-- Уровень 2
-- Уровень 2
--- Уровень 3
--- Уровень 3
--- Уровень 3
-- Уровень 2

Если отмечен один из пунктов на 3-м уровне, чекбокс 2-го и 1-го уровня были помечены как indetermenate (3 состояние), соответственно на 2-м уровне аналогично. Если на данном уровне отмечены все чекбоксы (2 состояние), у родительского чекбокса на уровень выше чекбокс так же отмечается и убирается если убран на уровне ниже.

В верстке на jq с реализацией не возникло проблем, ну может чуть криво.

JQ
$(document).ready(function () {
    $('.b-filterItem_bvar').on('click', '.cbx-label', function () {
        // Вложенность 3-го уровня
        if ($(this).closest('.b-filterItem').hasClass('fi_b-dropdown')) {
            var f_filterItem = $(this).closest('.b-filterItem'),
                  f_filterItemHead = f_filterItem.find('.b-filterItem_head.checkGroup'),
                  f_filterItemCount = f_filterItem.find('.bvar_dropdown').find('.cbx-input').length,
                  f_filterItem_cheked = f_filterItem.find('.bvar_dropdown').find('.cbx-input:checked').length;

            if (f_filterItemCount === f_filterItem_cheked) {
            // Отмечены все чекбоксы
                f_filterItemHead.find('.cbx-label').addClass('checked');
                f_filterItemHead.find('.cbx-input').attr("checked", "checked");
            if (f_filterItemHead.find('.cbx__checkMark').hasClass('noAllChecked')) {
                f_filterItemHead.find('.cbx__checkMark').removeClass('noAllChecked');
            }
            } else if (f_filterItem_cheked === 0) {
                //  Не отмечены чекбоксы
                f_filterItemHead.find('.cbx-label').removeClass('checked');
                f_filterItemHead.find('.cbx-input').removeAttr("checked", "checked");
            if (f_filterItemHead.find('.cbx__checkMark').hasClass('noAllChecked')) {
                f_filterItemHead.find('.cbx__checkMark').removeClass('noAllChecked');
            }
            } else if (f_filterItemCount !== f_filterItem_cheked & f_filterItem_cheked !== 0) {
                // Отмечены некоторые чекбоксы
                f_filterItemHead.find('.cbx-label').removeClass('checked');
                f_filterItemHead.find('.cbx-input').removeAttr("checked", "checked");
                f_filterItemHead.find('.cbx__checkMark').addClass('noAllChecked');
            }
        }

        // Замена чекбокса родительской группы
        var f_cbxBvar = $(this).closest('.b-filterItem_bvar'),
              f_itemBodyHead = $(this).closest('.b-filterItem_body').prev('.b-filterItem_head '),
              f_cbxBvarCount = f_cbxBvar.find('.cbx-input').length,
              f_cbxBvarCount_cheked = f_cbxBvar.find('.cbx-input:checked').length;

        if (f_cbxBvarCount === f_cbxBvarCount_cheked) {
            f_itemBodyHead.find('.cbx__checkMark').removeClass('noAllChecked');
            f_itemBodyHead.find('.cbx-label').addClass('checked');
            f_itemBodyHead.find('.cbx-input').attr("checked", "checked");
        } else if (f_cbxBvarCount_cheked === 0) {
            f_itemBodyHead.find('.cbx__checkMark').removeClass('noAllChecked');
            f_itemBodyHead.find('.cbx-label').removeClass('checked');
            f_itemBodyHead.find('.cbx-input').removeAttr("checked", "checked");
        } else if (f_cbxBvarCount !== f_cbxBvarCount_cheked) {
            f_itemBodyHead.find('.cbx__checkMark').addClass('noAllChecked');
            f_itemBodyHead.find('.cbx-label').removeClass('checked');
            f_itemBodyHead.find('.cbx-input').removeAttr("checked", "checked");
        }
    });
});


В данном примере отталкиваюсь от события клика по классу .cbx-label и от него до DOMу ищу родителей и меняю их состояние.

HTML
<div class="b-filterItem dropdown open">
  
  <!-- 1 уровень -->
  <div class="b-filterItem_head checkGroup">
    <div class="cbx">
      <div class="cbx-label">
        <input class="cbx-input" id="letters_cbx_1" type="checkbox" name="" value="">
        <div class="cbx__box"></div>
        <div class="cbx__checkMark"></div>
      </div>
    </div>
    <div class="b-filterItem_title">
      <span class="b-filterItem_arrow f_chO"></span>
      <span class="b-filterItem_name text-overflow">Типы</span>
    </div>
  </div>

  <div class="b-filterItem_body f_chB">
    <div class="b-filterItem_bvar">
      
      <!-- 2 уровень -->
      <div class="b-filterItem">
        <div class="b-filterItem_head">
          <div class="cbx">
            <div class="cbx-label">
              <input class="cbx-input" type="checkbox" name="">
              <div class="cbx__box"></div>
              <div class="cbx__checkMark"></div>
            </div>
          </div>
          <div class="b-filterItem_bvar-title text-overflow">
              <span class="text-overflow">Аккаунт</span>
          </div>
        </div>
        <div class="bvar_dropdown f_chB" style="display: none;">

          <!-- 3 уровень -->
          <div class="bvar_dropdown-item">
            <div class="app-spacer"></div>
            <div class="cbx-label">
              <div class="cbx">
                <input class="cbx-input" type="checkbox" name="">
                <div class="cbx__box"></div>
                <div class="cbx__checkMark"></div>
              </div>
              <div class="bvar_dropdown-label">
                  <span class="bvar_dropdown-title text-overflow">
                    <span class="text-overflow">Facetime аудио</span>
                  </span>
              </div>
            </div>
          </div>
          <!-- 3 уровень -->
          <!-- 3 уровень -->

        </div>
      </div>
      <!-- 2 уровень -->
      <!-- 2 уровень -->

    </div>
  </div>
  <!-- 1 уровень -->
  <!-- 1 уровень -->
</div>


SCSS
&__box:before,
  &__box:after {
    content: "";
    position: absolute;
    width: 16px;
    height: 16px;
    border-radius: $borderRadius;
    top: 3px;
    left: 0;
  }
  &__box:before {
    border: 2px solid $greyCbxColor;
    background: transparent;
  }
  &__box:after,
  &__box.indeterminate:after {
    background: $blueColor url('../assets/icons/svg/icon-checked.svg') no-repeat center;
    background-size: 80%;
    color: #fff;
    opacity: 0;
  }

  input[type="checkbox"]:checked ~ &__box:after {
    opacity: 1;
  }
  input[type="checkbox"]:indeterminate ~ &__box:after {
    opacity: 1;
    background: $blueColor url('../assets/icons/svg/icon--indeterminate-wite.svg') no-repeat center;
  }

  input[type="checkbox"],
  &-input {
    position: absolute;
    width: 16px;
    height: 16px;
    margin: 0;
    top: 3px;
    opacity: 0;
    z-index: 1;
  }


Знаю, что jq не оптимальный и много лишних движений, но он работает хорошо и без ошибок.
Просто скопировать структуру и прикрепить js файл с jq не получается так как фильтр подгружается компонентом ангуляра.

Хочу понять как без jq можно реализовать похожий функционал с 3 состояниями.
В jq по большому счету бегает вверх и вниз и навешивает классы, галочка (2 состояние) и состояние неопределенного чекбокса (indeterminate) реализованы простым показом класса cbx__checkMark.

P.s.
В примере scss состояния реализованы не с помощью класса cbx__checkMark, а на псевдоэлементах. Думаю это не должно запутать.

Хочу понять как сделать на JS без jQuery или на TypeScrypt. Буду благодарен за подсказки.
  • Вопрос задан
  • 226 просмотров
Пригласить эксперта
Ответы на вопрос 1
@grinat
Создаешь примерно такую структуру:
{
  "value": 1,
  "isSelected": true,
  "allChildsSelected": true,
  "childs": [
    {
      "isSelected": true,
      "value": 2,
      "allChildsSelected": true,
      "childs": [
        {
          "value": 3,
          "allChildsSelected": true,
          "isSelected": true,
          "childs": null
        }
      ]
    },
    {
      "value": 5,
      "allChildsSelected": true,
      "childs": null,
      "isSelected": true
    }
  ]
}

Ее рэндришь в шаблоне. Вешаешь на чекбокс обработчик, и ходишь по этом дереву и ставишь галки как тебе нужно.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы