AngularJS: нестандартное поведение ng-if


    В проекте используется angularjs, проект уже достаточно большой, используем очень много кастомных директив, но сегодня столкнулся с проблемой поведения ng-if .

    Для меня было ожидаемо, что условие работает так же как и в js, то есть:

    if (condition) {
     ...
    } else {
     ....
    }
    


    В зависимости, что передано — элемент будет скрыт или показан.



    В моем случае проверялось наличие поля в объекте ng-if="column.is_available". Тип поля — строка, но может быть нескольких вариантов:
    • Yes
    • No
    • After %N% days
    • и так далее


    Все было хорошо, до тех пор, пока не проверил состояние No. Оказывается, angularjs использует функцию toBoolean, при проверки условия, которая имеет достаточно не обычное (по крайней мере для меня) поведение:

    function toBoolean(value) {
      if (typeof value === 'function') {
        value = true;
      } else if (value && value.length !== 0) {
        var v = lowercase("" + value);
        value = !(v == 'f' || v == '0' || v == 'false' || v == 'no' || v == 'n' || v == '[]');
      } else {
        value = false;
      }
      return value;
    }
    


    То есть вернёт false при строках:
    • f
    • 0
    • no
    • n
    • []


    В официальной документации об этом ничего не сказано. Может в каких либо случаях это круто и удобно, но в моем нет. Как минимум для таких проверок нужно использовать дополнительную директиву, ng-extra-if="condition", а для обычной ng-if — обычный if, такой же как в js.

    Решил проблему так:

    <div ng-if="isAvailable(row.is_available)">
    


    $scope.isAvailable = function(is_available){
        return !!is_available;
    } 
    


    Но можно еще и так:

    <div ng-if="!!row.is_available">
    


    Это распространяется еще и на ng-hide и ng-show.
    Как вы считаете, это нормальное поведение?

    Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 13
    • 0
      Для меня еще было неожиданностью, что пусть массив тоже будет интерпретирован как false значение.
      • +2
        так ведь не просто массив, а пустой массив, это вполне удобно когда нужно вывести сообщение на пустом массиве, который позже наполнится через ресурс
        • +1
          Возможно, но в моем случае я использовал одно и тоже свойство и для отоброжения данных и для показа лоадера, в случае когда свойство еще undefined. Таким образом получал бесконечный лоадер, когда вместо undefined был пустой массив.
        • +2
          Это известная особенность JavaScript:
          Image #2215217, 6.8 KB
          • 0
            Хотя, в toBoolean условие похитрее, которое намеренно пустой массив считает ложным значением.
            • 0
              да, все верно — в js у массива и строки по сути идет
              toBoolean = function(){
              return this.length.toBoolean();
              }
              
          • +3
            Это, в принципе, задокументировано, но во внезапном месте (api/ng/directive/ngHide), и нет четкого указания, что это относится ко всем внутренним директивам.

            По-хорошему, конечно, это должно быть написано где-то в guide/expression.

            Зачем так сделано? Думаю, для более удобного комбинирования с input… ng-value/ng-false-value. Две директивы, думаю, не надо — "!!" написать не проблема. Но надо, конечно, об этой особенности помнить.
            • 0
              Этот момент описан в официальной документации к директиве ng-show

              Note: Here is a list of values that ngShow will consider as a falsy value (case insensitive):
              «f» / «0» / «false» / «no» / «n» / "[]"


              Наверно, просто косяк документации. Я думаю, что если это нормально описано в документации и ты об этом помнишь, то это удобно, почему бы и нет? Но если об этом не знать, то безусловно, это головная боль. На один раз. Пока не разберешься. Потом знаешь и пользуешься.
              • +1
                Хочу обратить внимание, что он не просто скрывает элемент, он изымает целиком узел из DOM или вставляет на это место его копию, в то время как show|hide только меняют свойство CSS visibility. В момент удаления скоуп элемента уничтожается, и создаётся новый в момент восстановления. Иногда это критично. Часта ошибка когда пытаешься показать динамический элемент, загруженный позже, а ничего не происходит — не учтено поведение ngIf

                https://code.angularjs.org/1.2.16/docs/api/ng/directive/ngIf
                • 0
                  show|hide только меняют свойство CSS visibility

                  Меняется свойство display на "none !important;"
                • 0
                  Сравнивать со строками «Yes» или «No» мне кажется достаточно костляво и не очевидно. Лично я бы просто использовал булеву переменную и вручную занимался приведением типов. И да, как сказали выше, ng-if вырывает из контекста элемент, по сему всегда использую ng-show. Замечу, что ng-show | ng-hide изменяют не visibility, а display:

                  [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak, .ng-hide {
                  display: none !important;
                  }
                  • +1
                    Значения 0 и [] в качестве false вполне допускаю. Остальные значения выглядят несколько надуманно. Судя по опыту топикстартера, эти неожиданные значения не соответствуют false в предметной области проекта.
                    • +1
                      Интересно, что вы будете делать, когда ВНЕЗАПНО! потребуется локализовать «Yes|No|After %N% days|...»? Будете жаловаться, что ng-if неправильно понимает «Да|Нет» или будете старательно выискивать и переписывать все места, где использовалась такая конструкция?

                      Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.