[Перевод] Магия AngularJS: никогда не вешайте binding на примитивы
Если вы используете AngularJS, скорее всего вы неоднократно сталкивались с правилом «Не вешайте binding на примитивы». В этом посте я подробно разберу пример, в котором использование примитивов создает проблемы: создание списка элементов , в котором каждый из элементов привязан к строке.Наш пример Скажем, вы работаете над приложением с книгами, и у каждой книги есть список тегов. Наивным способом предоставления пользователю возможности редактировать теги будет:
Это происходит потому, что ng-repeat создает child scope для каждого тега и в реальности scopes могут выглядеть так:
bookCtrl scope = { tags: [ 'foo', 'bar', 'baz' ] } ng-repeat child scopes: { tag: 'foo' }, { tag: 'bar' }, { tag: 'baz' } В этих child scopes ng-repeat не создает двусторонний binding для значения тега. Это означает, что при изменении первого поля ng-model просто меняет первый child scope на { tag: 'something' }, и это никак не отражается в объекте book.Теперь вы увидели, как примитивы могут сыграть с вами дурную шутку. Если бы мы для каждого тега вместо строк использовали объекты, то все бы работало, так как тег в child scopes был бы тем же самым instance, что и в book.tags, и изменения его значения (например, tag.name) просто бы работало, даже без 2-way binding.
Но, предположим, что здесь мы не хотим использовать объекты. Как поступить в таком случае?
Неудачная попытка — Я знаю! — могли бы вы подумать. — Я свяжу ng-repeat напрямую со списком тегов вышестоящего уровня! Давайте попробуем:
В этом нужно винить ng-repeat. Ради эффективности ng-repeat отслеживает все значения в списке и перерисовывает конкретные элементы, подвергшиеся изменению.
Но примитивы (числа и строки, например) являются immutable в JavaScript. Поэтому, если их нужно изменить, предыдущий instance выбрасывается и используется новый. Таким образом, любое изменения примитива заставляет ng-repeat перерисовать его. В нашем случае это означает, что когда мы избавляемся от старого и добавляем новый, по дороге мы теряем фокус.
Решение Нам нужно найти способ заставить ng-repeat идентифицировать элементы в списке без зависимости от их примитивного значения. Хорошим вариантом было бы использовать индекс примитива в списке. Но как научить ng-repeat отслеживать элементы в списке? На наше счастье в Angular 1.2 появился оператор track by: