Как стать автором
Обновить

Комментарии 146

Всё-же будьте осторожны с Dependency Injection через название параметров функции. Если ваш JS-код впоследствии будет пропущен через YUI compressor или Uglifier, то в продакшине все перестанет работать.

YUI compressor можно заставить не переписывать параметры функции через nomunge:

angular.module('app', ['ngResource'])
  .factory 'APISession', ($resource) ->
    "$resource:nomunge"
    $resource "/session.json"


Но лучше всего прямо указать зависимость:

angular.module('app', ['ngResource'])
  .factory('APISession', ['$resource', ($resource) ->
    $resource "/session.json"
  ])


Для AngularJS-контроллеров из соображений читабельности CoffeeScript-кода я обычно использую вот такую конструкцию. Она позволяет видеть все зависимости и соответствующие им параметры контроллера в одном месте, вверху файла, и не заботиться, например, о закрывающей квадратной скобке в конце файла, если просто использовать массив (как в примере выше):

window.TranslationsEditCtrl = ['$scope', 'APITranslation']
window.TranslationsEditCtrl.push (self, APITranslation) ->
  # Controller code here
Не совсем по теме DI, но я бы рекомендовал отказаться от определения контроллеров в глобальном window и полностью перейти на метод Module.controller.

module.controller('TranslationEditCtrl', ['$scope', function ($scope) {
Прекрасный фреймворк, который набирает все больше популярности. Заставляет посмотреть на разработку UI совсем по другому.
Пишу в аналогичном стиле уже как 5 лет, естественно не на Angular. Пошел еще дальше, и почти полностью избавился от модели. Если кому интересно, могу написать статью. Первое впечатление людей, которые плотно сидят на jQuery, от фреймворков по типу Angular — «что за говно?». Событийная модель полностью меняет сознание и стиль кода. Но, самое интересное, существенно упрощает поддержку, миграцию кода, внесение изменений.
+1
Я бы почитал с удовольствием.
+1 тоже бы почитал
интересно
+1
Пишите конечно, очень интересно
Буду ждать с нетерпением.
Присоединеюсь к единичкам.
Какое-то время назад назад написал шаблон для плагинов jQuery, который позволяет отказаться от стиля, навязываемого им. Я думаю, многим будет полезно.
Как раз пару дней назад начал с ним играться.
Искаропки есть практически все, что нужно и нет.
$resource — потрясающая штука, ничего удобней для доступа к серверному api не видел.
Довольно удобная маршрутизация.
Data-binding хоть и очень удобен, но ресурсоёмок и непрозрачен.
Dependency Injection хоть и интересная штука, данная реализация мне не особо понравилась. Хорошо отложенная инициализация, но это клиентский js — могли бы и загрузку отложенной сделать. Зависимости по умолчанию получаются из приведения функции к строке, как выше заметили — могут быть проблемы при сжатии кода.
Накладывает жесткие требования к структуре коде, хотя, может, это и к лучшему.
Data-binding не так уж и ресурсоемок, если следовать простому правилу — класть в $scope только то, что тебе в данный момент нужно на экране, а не хранить там все на свете. Если есть список из 1000 элементов и используется пагинатор — то лучше в $scope класть только текущую страницу

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

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

Жесткие требования — это плюс. Это культура разработки. Когда вы повстречаетесь с отчаянными фристайл кодерами, которые выносят код в отдельные функции и файлы по одним им известным причинам — вы это почувствуете =)
Data-binding не так уж и ресурсоемок

Data-binding очень ресурсоемок, особенно вкупе с ng-repeat, проверено. При всех достоинствах angular, у него есть огромный недостаток в работе с большими объемами данных, разработчики фрейморка это знают, но пока до сих пор нет какого-то более или менее пригодного решения. В итоге, каждый кто сталкивается с необходимостью обратотать большой датасет, выкручивается как может.
Возьмем, например, бесконечный скроллинг. При относительно сложном единичном объекте для отображения — со вложенными в массивы объектами, в которые вложены другие массивы с объектами — после пары-тройки тысяч записей в ng-repeat при взаимодейсвии с элементом списка весит страницу, потому что при каждом взаимодействии вызывается $digest, котоый заставляет проверять каждый забинденный элемент. Иногда нужен какой-то ng-static-repeat, который просто отображает данные, потому что эти данные не меняются.
Ну тут еще разбираться надо, что именно тормозит. ng-repeat он там во внутренностях понаоптимизирован так, что сходу и разобраться нелегко. Одно дело у Вас перерисовывается вообще все, что в ng-repeat-е указано (если по какой-то причине у Вас там полностью список подменяется, а не дописывается в него). Другое дело куча отдельных watcher-ов на каждый {{..}} внутри ng-repeat. Во втором случае да, это все в статику вроде обычно переводят. Только ng-repeat свой делать все же, наверное, не стоит, лучше какую-нибудь директиву, которая после рендеринга подменит текущий html-элемент клоном без {{..}} и других биндингов. Но тоже внимательно надо посмотреть, что там с watcher-объектами на $scope при клонировании будет происходить.
Другое дело куча отдельных watcher-ов на каждый {{..}} внутри ng-repeat

Именно в них дело

Только ng-repeat свой делать все же, наверное, не стоит, лучше какую-нибудь директиву

ng-repeat — это и есть директива, только встроенная в фреймворк. Я попытался сделать на ее основе что-то типа ng-static-repeat, но не хватило у меня толи времени, толи терпения. Там все в итоге все равно заваязано на $compile, который в свою очередь привязан к $scope. В итоге сделал тупо hardcoded template.
Именно в них дело


Это безусловно. Но может оказаться, что не только в них.

ng-repeat — это и есть директива, только встроенная в фреймворк.


Я знаю, что ngRepeat — это встроенная директива :) Вы посмотрите ее исходники. Там хватает своих сложностей. Зачем повторять то, что уже сделано, добавляя к нему свое новое, если можно просто дополнить поведение, сделав свою дополнительную директиву mySuperStatic, которая будет делать только то, что нужно. Я имел в виду что-то типа такого — просто как отправная точка.
Смотрел я исходники ng-repeat — я же упомянул, что на основе нее хотел сделать, только не добавлять «что-то» свое, а убрать лишнее, оставив лишь рендеринг. За ваш ответ на so спасибо, подумаю, если вдруг понадобится вновь.
Возможно я Вас неправильно понял, но когда речь идет о проблемах из-за большого количества watcher-ов, то ngRepeat тут уже особо не причем. ngRepeat повторяет то, что Вы прописали. Со вложенным DOM-ом, который содержит watcher-ы, надо что-то делать. Тормоза то не внутри кода ngRepeat возникают, а в $digest.
Ну да, я про это и написал:
после пары-тройки тысяч записей в ng-repeat при взаимодейсвии с элементом списка весит страницу, потому что при каждом взаимодействии вызывается $digest, котоый заставляет проверять каждый забинденный элемент

Просто ng-repeat, то бишь список, — это самый сапространенный способ отобразить какой-то набор данных. И он не просто клонирует шаблон, он также производит привязку данных. Т.е., по-хорошему, к ng-repeat было бы круто добавить опцию «сделай просто рендеринг».
Так делать рендеринг — это не ответственность ngRepeat. Поэтому из тех решений, которые я встречал, люди делали ngBindStatic. А мне напрямую пока такого не надо было.
Напрямую ngRepeat рендерингом не занимается, но начиная с этой строчки ngRepeat генерирует новый $scope для каждого элемента списка, и отдает его для компиляции.
Ну scope, да, генерирует. Фактически это чуть ли не единственная ситуация, когда ngRepeat стоило бы сделать свой — если не надо новых scope-ов, если именно в их количестве тормоза, но тогда потеряются значение самого элемента, $index и т.п. Т.е. уже непонятно что получится, а не ngRepeat.
Т.е. это как раз к вопросу о точной идентификации места тормозов и его исправлении.
Тормоза точно из-за количества scope, потому как $digest заставляет пробежаться по всей иерархии scope, начиная с rootScope. Когда я это понял я и пытался сделать repeater без создания новых scope, просто с компиляцией шаблона, чего у меня, как я уже говорил, не вышло.
Оно пробегается не столько по всем scope-ам, сколько по watcher-ам всех scope-ов. И вот там вычисления, сравнения и т.п. Я думаю, тормозит там.
Если не секрет, сколько у вас данных суммарно в scope набирается? Вот я набросал простой пример с 1000 элементами в ng-repeat, $digest по ним отрабатывает мгновенно. У меня есть определенные сомнения, что в scope одновременно для каких-то задач требуется держать более такого количества элементов. Не забывайте, что scope — это не модель, это текущий контекст «экрана», к которому привязана модель. В случае бесконечного списка вы должны удалять из scope элементы, ушедшие далеко назад (не удаляя сами эти данные).
У меня есть определенные сомнения, что в scope одновременно для каких-то задач требуется держать более такого количества элементов

Задачи бывают ооочень разные. И не нужно мне сейчас говорить, что если на странице больше 1000 элементов, то это плохой UX, знаю, слышал, но не раз видел обратное.

В случае бесконечного списка в scope его вообще лучше не держать, потому как память все равно забъет.

В моем случае, мне нужно было отобразить список событий за произвольный интервал времени. Каждое событие — это состояние удаленного контролируемого объекта. Выбор элемента списка приводил к отображению этого состояния. В день событий скапливается 25 000 ± 5 000. Получал я их с сервера пачками по 1 000. Первая реализация на jQuery позволяла без труда просметривать события за несколько дней, навигация по списку с помощью клавиатуры: Up/Down — пред/след, PageUp/PageDown — прыжок через 10 событий, End/Home — конец/начало списка. Нажал кнопку, держишь и смотришь, как меняется состояние объекта.

Одно событие имело примерно следующую структуру:
{
  _id: "51447fdee002580db060e10b",
  date: 1363392000000,
  sections: [
    {
      caption: "Foo",
      address: 393220, 
      counters: [2, 2]
    }, {
      caption: "Bar",
      address: 15842696, 
      errors: {
        main: ["ERR_BADFRAME", "ERR_AB_OFF"]
        dupl: ["ERR_BADFRAME", "ERR_AB_OFF"]
      }
    }, {
      caption: "Baz",
      address: 245, 
      warnings: {
        dupl: ["DEV_SHIELD_A"]
      }
    }
  ],
  srvtimestamp: 1363443678891,
  trmbias: -18000000,
  trmtimestamp: 1363447489556,
  witherror: true,
  withwarning: true
}

Angular же после третьей тысячи событий начинал тормозить, потому что весь массив событий лежал в scope. Держать его в scope было удобно, но когда начались тормоза, пришлось переносить его в сервис, а ngRepet заменять кастомной директивой, потому что он работать умеет только с тем, что лежит в scope.

Вы попробуйте свой пример с таким объектом вместо простых чисел.
И не нужно мне сейчас говорить, что если на странице больше 1000 элементов, то это плохой UX, знаю, слышал, но не раз видел обратное.

Не на странице. На экране, в один конкретный момент времени.

Angular же после третьей тысячи событий начинал тормозить, потому что весь массив событий лежал в scope. Держать его в scope было удобно, но когда начались тормоза, пришлось переносить его в сервис

Ну вот видите, вы просто не умели его готовить. Разумеется, нельзя держать всю модель в scope, это разработчики Angular не устают подчеркивать. Это удобно, но неверно. И это недостаток не Angular как фреймворка, а архитектуры вашего приложения.
Ну вот видите, вы просто не умели его готовить.

Сейчас я тоже так могу ответить, но когда начинал работать с Angular, я этого не знал, как и многие другие, я уверен. Ведь нигде в документации не написано: «Attention! Do not place in scope over 3,000 objects»
И известный коммент к нему: «this should be put on the angular docs!» (:
Точно :)
Ну что поделать, молодая технология.
Они достаточно охотно принимают pull-request-ы по документации :)
а ngRepet заменять кастомной директивой, потому что он работать умеет только с тем, что лежит в scope.

Вот эта фраза тоже не очень понятна. Хорошая директива, пригодная к повторному использованию, не должна работать с сервисами напрямую, она работает именно со scope. Вам нужно было не директиву новую писать, а просто пересмотреть механизм формирования scope в контроллерах, и тогда стандартная ng-repeat бы заработала как надо.
Каким образом надо было пересмотреть «механизм формирования scope в контроллерах»? Проблема то в большом количестве watcher-ов. Например, для 2000 событий, да на каждое событие с пяток {{..}} — получили 10000 watcher-ов.

Это просто самая очевидная причина. А так, конечно, как и писал выше, надо нормально разобраться — в каком месте тормозит.
Контроллер должен контролировать, какие данные из сервисов поступают в scope, а какие еще (или уже) нет. Да, всего в модели 2000 событий, но в scope их может лежать всего 50 текущих отображаемых. И это и является задачей контроллера. Товарищ выше решил эту задачу через контроллер новой директивы, тогда как с этим вполне мог справиться контроллер той части дерева, где сидит и обычный ng-repeat.
По-моему мы начинаем уходить к сферическому коню в вакууме. Изначальная задача, поставленная mgrach — бесконечный скроллинг. Или Вы хотите сказать, что при бесконечном скроллинге торможения будут из-за большого списка в одном watch-е, который идет аргументом ng-repeat?
Я хочу сказать, что при бесконечном скроллинге нет никакой необходимости делать ng-repeat по всему объему данных.
На мой взгляд, подобные комментарии стоило бы подкреплять ссылками на примеры реализации. Или хотя бы более подробным описанием схемы решения ;-)
Ну вот например: binarymuse.github.com/ngInfiniteScroll/demo_basic.html
Это про подгрузку данных. Я бы еще дополнил это решение заменой элементов «сильно выше экрана» на какую-то пустышку, с возвратом нужных элементов обратно когда скроллим вверх.
Сам по себе пример хороший (+1). Только если подняться к Вашему же примеру на jsfiddle и отрисовать изначально 5000 элементов и сделать кнопку «Add 100», то торможений особых тоже не замечаю. Это опять же к вопросу, сначала разобраться, что в точности тормозит, а потом это и оптимизировать.
Не совсем понял. При 5000 элементах любое изменение одиночного поля в каком-либо элементе будет тормозить.
Мдя, надо делать поправки на комп. У меня только на 10 000 начинает немного притормаживать :)
«Тормозить» — это я имею в виду больше 50 мс на действие, что обычно считается задержкой, заметной глазу.
Хм, что-то я невнимательно изначально посмотрел — они же там просто новые элементы в конец дописывают. И в ng-repeat все также массив со всеми элементами.
Потому я и написал выше: «Я бы еще дополнил это решение заменой элементов «сильно выше экрана» на какую-то пустышку, с возвратом нужных элементов обратно когда скроллим вверх.»
Почитал ваш диалог с aav. На самом деле вы сейчас ведете разговор об архитектурных ограничениях Angular, а конкретнее, об ограничениях связанных с dirty checking, и я уверен, что и вы об этих ограничениях тоже не сразу узнали.

Вот абстрагируйтесь от Angular и поясните, почему в объекте, которую контролирует модель, вместо всех 5000 событий должно лежать только 50 текщих отображаемых? Почему, если я хочу отобразить список этих событий, я должен морочится с копирование туда/сюда 50 событий? Это имеет смысл только в контексте Angular, потому что на dirty checking 5000 событий уходит много времени. Как я писал выше, первый прототип приложения на jQuery легко пережовывал и 50 000 и 80 000 событий в этом списке, потому что список-то простой и отображаемая нода в DOM-дереве была простая. Хоть и не было там модели и контроллера. И, скорее всего, то же самое на backbone решилось бы без всяких вопросов: «А где храненить 5000 событий?».
Я хоть и не очень одобряю, начинать оптимизацию с этих «туда/сюда 50 событий», т.к. считаю, что оптимизацию надо начинать с самых узких мест (и я вообще-то об этом больше стараюсь говорить, чем непосредственно об архитектурных ограничениях). Но по абзацу с абстрагированием имею кое-что сказать: если Вы решаете задачу, в которой возможности AngularJS практически не используются — надо отобразить очень много статичного содержимого, не надо никаких биндингов, тестирования и т.п., то начинает закрадываться ощущение о неправильно выбранном инструменте.

При этом, если в своем приложении Вы вообще-то достаточно серьезно задействовали AngularJS, то и эту задачу на нем можно будет решить, просто не настолько прямолинейно. bindStatic должен помочь, возможно и «туда/сюда 50 событий» придется подключить, т.к., я думаю, Вы и сами понимаете, что dirty checking и проверка, что там надо дорисовать, для массива из 80000 элементов — это перебор.

Кстати, между делом bindStatic уже накидал. Вот, пусть уже будет:
.directive('bindStatic', function(){
    return function(scope, elem, attrs) {
        elem.text(scope.$eval(attrs.bindStatic));
    }
});


Использовал тут: jsfiddle.net/7sj76/9/
Angular очень хорошо подошел для этого проекта, за исключением обсуждаемого списка, но так как это было первое его серьезное применение, то обучение шло параллельно и, соответственно, проблемы решались как решались. Теперь уже, вникнув в особенности фремворка, конечно же, некоторые момент можно сделать более правильно.

Вот, кстати, полезный набор watch-fighter директив, аналогичный по сути вашему bindStatic.
Вот абстрагируйтесь от Angular и поясните, почему в объекте, которую контролирует модель, вместо всех 5000 событий должно лежать только 50 текщих отображаемых?

Потому что scope — это не модель. Об этом заявляется, например, вот здесь в документации, и подробно подчеркивается в официальном видео Best Practices. Постарайтесь вникнуть в эту мысль, она все объясняет. В модели могут жить 50 тысяч, 100 тысяч объектов любой сложности, и ограничивает это только память. А scope — это некоторая транзитная сущность для связки модели и отображения, можем называть ее моделью отображения (View-Model). Модель отображения не должна хранить ничего, что не требуется отображать. Это не ограничения фреймворка, это в целом принципиальный подход к построению приложения, его терминология. Документация и различные обучающие материалы это объясняют достаточно доходчиво, я лично не столкнулся с проблемой понимания смысла scope, когда изучал angular. Возможно, вы просто чуть раньше углубились в написание собственного сложного кода, чем следовало бы.

Знаете, я много пишу под Андроид, и там у меня тоже никогда не возникало вопроса, почему я не должен хранить в текущем отображаемом адаптере списка все элементы этого списка, чтобы скроллинг был плавным. Это само собой разумеющееся понятие в чуть более низкоуровневном программировании интерфейсов, просто в вебе все привыкли к несколько иным (простым и плохим) подходам.
И, скорее всего, то же самое на backbone решилось бы без всяких вопросов: «А где храненить 5000 событий?».

Не скажу насчет Backbone, но вот если вы в Knockout решите воспользоваться таким брутальным подходом к решению вопроса и создадите на каждый из 50 тысяч элементов собственный observable, то все умрет гораздо раньше, чем в Angular :)
Смысл то в том, что в этом списке не нужны observers, потому что он не изменен, он только отображается, а Angular вотчеры содает автоматом при использовании ngRepeat. Т.е. асли бы была у этой директивы встроенная опция staticBind, то этого диалога, возможно, вообще бы не было (:
Я до сих пор убежден, что в директиве ng-static-repeat отсутствует всякий смысл, и решение некоторой проблемы с ее помощью — это неправильное решение. Видимо, разработчики angular считают так же.

Боюсь, вы до сих пор не улавливаете разницу между scope и моделью.
Ну погнали вникать… Вы пишите:
Потому что scope — это не модель. Об этом заявляется, например, вот здесь в документации, и подробно подчеркивается в официальном видео Best Practices. Постарайтесь вникнуть в эту мысль, она все объясняет. В модели могут жить 50 тысяч, 100 тысяч объектов любой сложности, и ограничивает это только память. А scope — это некоторая транзитная сущность для связки модели и отображения, можем называть ее моделью отображения (View-Model). Модель отображения не должна хранить ничего, что не требуется отображать.

Из ваших слов я понимаю, что есть некая Модель и какой-то Scope, Модель — это контейнер всех данных, а Scope — это такая штуковина, в которую я засовываю либо какие-то данные из Модели, либо ссылки на какие-то данные, которые отображаются на экране.

Идем по первой ссылке, читаем:
In Angular, a model is any data that is reachable as a property of an angular Scope object. The name of the property is the model identifier and the value is any JavaScript object (including arrays and primitives).

The only requirement for a JavaScript object to be a model in Angular is that the object must be referenced by an Angular scope as a property of that scope object.

А вот здесь написано, что Модель в Angular — это свойство объекта Scope, будь то объект, массив или примитив.

То есть все что я указал в Scope является моделями для отображения.

И если я хочу отобразить список из 5 000 элементов, я кладу в Scope массив из 5 000 элементов. Это моя модель для отображения.

Далее… В презентации Miško написано:
Treat scope is read-only in templates & write-only in controllers
— The purpose of the scope is to refer to the model not to be a model
— The model is your JavaScript objects

Этот слайд он сопровождает примерно следующими словами:
Scope has references to the model. So you create your own model object, manually put the reference to the model object to the scope. In view you say «model dot whatever property in a model you want to access». Similary if you have a form, you should have a model «form» and in a scope basically say «model dot property X» and view is updating property in a model, not a propery of a scope.

Первый пункт слайда — это точь-в-точь то, о чем вы пишите.

А Miško говорит примерно следующее: «так быть не должно…
$scope.firstName = "Bob";
$scope.lastName = "Dylan";
$scope.age = 71;

…должно быть так
$scope.form = {
  firstName: "Bob",
  lastName: "Dylan",
  age: 71
}

Из этого монолога я опять делаю вывод, что любая единичная сущность в Scope — это модель.
Этот слайд он сопровождает примерно следующими словами:

Прямо перед вашей цитатой вы пропустили самое важное первое предложение:

People oftentimes think that the scope is the model and that's not the case. Scope has references to the model.

Люди часто думают, что scope является моделью, а это не так. Scope ссылается на модель.


И если я хочу отобразить список из 5 000 элементов, я кладу в Scope массив из 5 000 элементов. Это моя модель для отображения.

Совершенно неверно. Вы не хотите отображать 5000 элементов на странице. Вы просто не можете этого сделать, они не влезут.

У вас есть Model (50 тысяч элементов), View (шаблон страницы, котором есть слоты для отображения 50 элементов) и View-Model (50 соответствующих элементов из Model для заполнения слотов). M-V-VM. Именно поэтому Angular называют MVVM-фреймворком.

Вопрос для размышления: какую роль в MVVM-парадигме отводится контроллеру.

Из этого монолога я опять делаю вывод, что любая единичная сущность в Scope — это модель.

Все, что там говорится про вложенные в $scope объекты — это исключительно речь про workaround хитрой «багофичи» поведения прототипного наследования объектов в яваскрипте (а scope наследуются именно таким образом), из-за которого children.var = 'abc' не будет менять значение parent.var, если children наследуется от parent, но в случае children.someobj.var = 'abc' соответствующее значение parent.someobj.var изменится правильно. Поэтому Мишко и говорит «всегда имейте точку в ваших выражениях, чтобы избежать таких ситуаций». Больше никаких выводов из этого делать не стоит.
Тогда вам тоже вопрос для размышление: если бы Angular позволял без вреда для производительности держать в Scope массив в 5000 элементов, мы бы сейчас вели этот разговор? И мааленький подвопросик: что вообще такое для современного компьютера массив из 5000 элементов?
Не обижайтесь, но мне это кажется типичным синдромом кодера, избалованного высокоуровневыми языками в плохом смысле — у нас дофига памяти, дофига цпу, поэтому плевать на правильные решения, к черту архитектуру и алгоритмы, давайте решать все задачи брутальными способами «в лоб» и не париться! Добрый совет: ни к чему хорошему это не приведет, меняйте мышление, пока не поздно.
Не обижайтесь, но мне кажется у вас приступ отрицания, потому что на самом-то деле вы хорошо понимаете, что 5000 элементов в массиве для современной ЭВМ это пшик, ничто! Откройте консоль и сгенерируйте этот массив, посмотрите, сколько это займет времени у вашего компьютера. Мы ж не о десяти миллионах речь ведем ей богу… Оптимизировать 5000? Не смешите меня. Используя VanillaJS я даже не задумывался об этом, взял Angular и уперся в это ограничение. И вы мне будете говорить о плохой архитектуре? Это просто ограничение инструмента и я это воспринимаю как должное. На нет, как говорится, и суда нет.
По-моему, вы подменяете понятия. Мы либо говорим о всём сложном комплексе действий в механизме дата-байндинга, либо о простых сравнениях. Если второе, то 5000 простых сравнений и для angular — это тоже пшик. Вот пруф: 5000 вотчеров у меня отрабатывают за 5-6 мс. Это, кстати, также доказывает, что «Angular позволяет без вреда для производительности держать в Scope массив в 5000 элементов».
На этом и закончим.
Что такое «в scope набирается»? Я не очень понимаю эту терминологию. Проблема в памяти? Или проблема в торможениях из-за большого количества watcher-ов? Объйекты в scope != watcher-ы.
Т.е. я имею в виду, что не для всех объектов в scope-ах обязательно есть watcher-ы. А в $digest вычисляются и сравниваются именно watcher-ы, а не все подряд объекты в scope-ах.
Я имел в виду следующее. Если при использовании ng-repeat выводить данные текущего объекта через {{}}, то, как известно, для каждого такого выражения будет создан вотчер. Поэтому держать в scope весь набор данных из триллионов элементов неправильно, ведь для каждого из них будет создан вотчер, если массив скормить в ng-repeat. В целом в scope без использования в шаблонах его держать, конечно, можно, но зачем оно там тогда нужно?
Ну тут как раз вопрос в точности формулировок. Нет проблемы в scope держать все что угодно, если кому-то это по какой-то причине удобно. Не из-за этого проблемы появляются. Проблемы могут появиться, когда данные начинают использоваться в angular-выражениях и в явном виде в первом аргументе $watch().

Это может показаться придиркой к словам, но я пишу это не столько для Вас, сколько для тех кто еще не очень глубоко в теме. Чтобы они не восприняли как догму, что в scope чего-то там держать нельзя. Можно. Но надо понимать механизмы.
Это настолько близкие понятия, что для начинающих пользователей angular я лично не вижу необходимости понимать эти механизмы, а просто можно взять за правило: держать в scope только то, что требуется в данную секунду в шаблонах. Остальное держать в сервисах и не трогать. И проблем не будет.
Вы так говорите, как будто на значения в сервисах нельзя повесить watch.

На мой взгляд, начинающим надо не бездумно применять «правила». А в случае столкновения с какой-то проблемой, разбираться с причинами этой проблемы и адекватно их устранять. Хотя, дело, конечно, хозяйское. На то мы тут и предъявили два взгляда на одно и тоже. Каждый прочтет и сам для себя решит.
Вы так говорите, как будто на значения в сервисах нельзя повесить watch.
Ну, я бы это и сказал, на самом деле. Никакого собственного scope у сервисов нет, а задействовать $rootScope в этих целях — кощунство.
Я имел в виду в контроллере сделать watch на значение, хранимое в сервисе.
Я не думаю, что те, кто сталкиваются с такими простейшими базовыми проблемами, знают, что у Scope.$watch() первый аргумент может быть не только строкой :)
Секунду назад еще не знали, а потом натолкнулись на пример в stackoverflow или гугл-группе.

Проблемы то базовые, но, на мой взгляд, не такие уж простейшие. И, опять же на мой взгляд, лучше пусть новички натолкнутся на проблему и получат шанс разобраться и взять level-up, чем просто будут следовать правилам, написанным Артёмом или пусть даже Miško Hevery.

Для полноты картины дискуссии переделал ваш пример для сложного объекта и увеличил размер массива до 3000. Теперь уже значение меняется с заметным запозданием.
Разумеется. Если увеличить до 100000, то все вообще перестанет шевелиться :) Но дело в том, что 100000 элементов информации на странице — это нонсенс.

Да, и кстати, усложнять структуру элемента в данном примере нет смысла, потому что количество вотчеров остается тем же самым (по два на каждую итерацию). Для проверки этого попробуйте запихать в массив sections каждого элемента сотню дополнительных лишних элементов — вы увидите, что быстродействие не меняется.
Это уже все обсуждено выше, приведены хорошие ссылки. Я думаю наши с вами комментарии к топику будут весьма полезны кому-то в будущем: есть, что почитать, посмотреть и пощупать руками.
Тогда для полноты картины потомкам я хочу процитировать здесь ключевую мысль из поста Misko Hevery:

You can't really show more than about 2000 pieces of information to a human on a single page. Anything more than that is really bad UI, and humans can't process this anyway.

So the real question is this: can you do 2000 comparisons in 50 ms even on slow browsers?
Проблема HTML в том, что он — текстоориентированый. Это значит, что всё в документе является текстом, а тэги (структуру) мы выделяем особым способом. А реалии современных приложений таковы, что первичную роль играет, как раз, структура. А если учтем, что это мультиязычное приложение, то в документе и вовсе нету текста — он локализированный лежит в отдельных ресурсах. Поэтому хочется иметь разметку, где принцип противоположный html — всё структура, а текст выделяем особым способом. Вот и имеем десятки подобных решений.
$('#toggleShowHide).change(function() {
toggle();
});

Может быть все-таки:

$('#toggleShowHide).change(toggle);

?
и вместо
if (isChecked) {
            specialParagraph.show();
        } else {
            specialParagraph.hide();
        }


 specialParagraph.toggle(isChecked)

да и если это всего в одном месте — то можно и не создавать функцию toggle.

итого:
$(function() {
    $('#toggleShowHide).change(function() {
        var isChecked = $('#toggleShowHide).is(':checked');
        $('#specialParagraph').toggle(isChecked); 
    });
});
Статья хорошая сам окунулся в angularjs. Хотелось бы видеть больше статей и примеров.
Расскажите — как передавать начальные данные в контроллеры или сервисы?

Например, есть страница поиска (первая страница рендерится на сервере, а все остальные — с использованием ajax). Нужно в контроллер передать значения выставленных фильтров
Также непонятно, почему ng-submit не передает $event (т.е. нельзя написать ng-submit=«ajaxSubmit($event)»)

Например, есть форма поиска, которая должна работать при включенном javascript — через ajax, при выключенном — обычным образом. Проблема в том, ng-submit делает preventDefault для события submit только если у формы нет атрибута action. А поскольку $event не передается в ajaxSubmit — единственный способ предотвратить стандартную отправку формы — добавить к форме атрибут onsubmit=«return false». Может кто-то знает элегантное решение без такого костыля?

<div ng-app ng-controller="SearchCtrl">
<form action="/search" ng-submit="ajaxSubmit($event)">
     <input type="text" ng-model="query" name="query">
     <input type="submit">
</form>
</div>


module.controller('SearchCtrl', ['$scope', function ($scope) {
    $scope.ajaxSubmit = function($event) {
        //$event === undefined
    }
}]);
P.S.
У него замечательная документация

Где вот в этой «замечательной» документации написано про такое поведение ngSubmit?
Это не ngSubmit делает preventDefault, а form/ngForm (пруф). А ngSubmit очень простая директива.

Так что, можно сделать свою. Можно отправить им патч. Если в документации есть огрехи, тоже можно заслать pull-request (их там много для документации сторонние разработчики шлют).
Начинающие программисты, чтобы сложить два числа в JavaScript подключали jQuery, сейчас будут подключать AngularJS.
А чобынет, очень просто же (:

<div>{{2+2}}</div>
Эмм… а зачем?
ну в переменную чтобы записать)
Это реально работающая схема в полном согласии с принципами MVC, обеспечивающая очень чистое разделение между представлением, контроллером и моделью.

MVVM в полном согласии с принципами MVC?
А где там MVVM?
Тогда уж не MVVM, а MVW:
And for this reason, I hereby declare AngularJS to be MVW framework — Model-View-Whatever. Where Whatever stands for «whatever works for you».
Так можно всё, что угодно назвать. Вы знаете JS-фреймворк, в которых нет Model и View?
Так что я лучше буду думать более конкретно:
it's now closer to MVVM
Если вы приводите код jQuery для сравнения в качестве примера «длинной» записи, постарайтесь заранее убедиться в грамотности этого кода…

$("input.toggle").change(function(){ $("div.toggle").toggle(); });

… и одним аргументом становится меньше.
Это неверный пример. Переключение «в противоположное состояние» не соответствует тому поведению, которое реализовано в Angular.
Причём тут Angular?
Я так понял, написали правильный короткий вариант той функции, которая в статье написана на JQuery в качестве примера
Что делает код из комментария выше? Он просто каждый раз меняет видимость элемента на противоположное при нажатии на инпут.
Что делает код Angular из статьи? Он делает привязку состояния инпута и флага видимости элемента.

Это принципиально разные вещи. Это можно понять, если подумать, что произойдет при перезагрузке страницы по F5, например.
Вот только атрибутов ng-model, ng-show и прочих ng-custom-shit в HTML не существует. Зачем плодить невалидный код?
В какой из версий HTML не существует? В пятой нет запрета на создание своих собственных тегов, атрибутов и прочих радостей XML, потому что нет ни DTD, ни XSD. А про какую валидацию можно говорить, если нет описания структуры документа?
Если б оно выглядело как data-ng-model, тогда да.
Давайте читать спецификацию

User agents must treat elements and attributes that they do not understand as semantically neutral; leaving them in the DOM (for DOM processors), and styling them according to CSS (for CSS processors), but not inferring any meaning from them.


Что хочешь, то и вставляй, хоть новые теги, хоть атрибуты. Они не будут невалидными с точки зрения спецификации HTML5
Но если Вам вдруг очень сложно переступить через себя и начать использовать ng-model, то Вы можете использовать любой из вариантов: data-ng-model, x-ng-model, ng:model, ng_model,.
На сколько я понял идеологически он схож с knockoutjs. Если так то какие у него преимущества по сравнению с ним?
Общение с REST сервером «из коробки». Также у них разный механизм связывания шаблонов и модели, на хабре была статья.
Большое спасибо за ссылку и ответ. Обязательно прочту. Сейчас использую knockoutjs. Очень нравится. Хоть и не подходит под все нужды.
Еще одно ключевое отличие в том, что, как и в случае jquery, knockout — это библиотека, а angular — фреймворк.
Выглядит красиво, но как обычно заточено под одну конкретную модель: элемент-событие-обработчик
Раскройте немного, пожалуйста, тему. Что у Вас не получилось реализовать в AngularJS и как Вы реализовывали это по-другому?
На самом деле есть задачи, которые тяжело решать событийным способом. Например, событие должно сработать только на определенном элементе, который первый удовлетворяет каким-то условиям. Селективная модель работает гораздо лучше в данном случае.
Извините, не срабатывает у меня pattern-mathcing :) Не могли бы Вы привести описание какого-нить примера, который бы можно было реализовать в рамках jsfiddle, т.е. чтобы не требовалось много времени на реализацию.
jsfiddle.net/wRyvu/2/

Есть некая структура. Необходимо удалить все элементы, которые идут после активного, исключая скрытые.
Зарытую собаку сходу не разглядел :)
Для затравки что-то типа такого: jsfiddle.net/RP2Wf/
Вносите пояснения/уточнения.
В целом, никаких претензий нет. Вы пошли по пути унификации шаблона. Однако удаляете одной транзакцией, в одном месте. Делать какие-то дополнительные действия перед удалением элемента уже не получится.
Шаблоны вобщем-то могут быть разные, например, для разных типов элементов. Да и с выполнением действий сходу проблем не вижу, например, опять же для каждого типа элемента.
Попробуйте каждой ноде назначить ряд функций, которые они должны делать при удалении. Количество функций и кому они назначены — не известно.

Есть функции, кому, какие и в каком количестве назначть — неизвестно. И когда это и как определяется?
Что-то мне задача не очень ясна, но, как вариант на скорую руку: jsfiddle.net/RP2Wf/1/
Отлично ясна. Спасибо за код, я увидел то, что хотел. Да, из-за модели задача, что описана выше, решается не сильно заумно. В моем коде модели нет, некому брать на себя обработку события destroy, ее обработчики сами на себя берут, поэтому тяжелее решать задачи селективного подхода
Вариантов много, например, чтобы проще было для понимания, убираем из этой цепочки «элемент» и вводим много разных «событие-обработчик», например так:

событие1->обработчик1->событие2->обработчик2… и гдето там в глубине, возврат к первому событию

Кажется всё просто, да? А теперь представьте что вместо каждого 'событиеN' где N>1 у ваc не одно конкретное событие а несколько, может быть совместно происходящих, может происходящих по каким-то условиям, то есть 'событиеN' на самом деле 'событиеNi' где i>1

Где нужно? Да в любом более менее сложном ajax проекте.
Недораскрыли :) Честно говоря, больше интересуют основания, на которых было заявлено «как обычно заточено под одну конкретную модель». Интересно, посмотреть ограничения технологии на чужом опыте. Именно это я пытался спросить с помощью «Что у Вас не получилось реализовать в AngularJS и как Вы реализовывали это по-другому?»
Основание я вам написал. А насчет «неполучилось» это вы загнули, естественно я могу это реализовать и без AngularJS и c AngularJS (только зачем он при этом непонятно, если половина «функциональности» либы с подхватыванием имён идёт мимо без объекта). И функционально, и через ООП.

Вопрос в другом, если я вижу что для реализации этой задачи больше подходит совершенно другой механизм — я реализую другим механизмом.
Не хотите Вы делиться опытом и выходить на конкретику :)
То, что Вы написали — это сферический конь в вакууме. Его можно придумать сколь угодно сложным и запутанным. Но из него непонятно, что Вы там пытаетесь реализовать (примерно понятно только «как»). Зависимости между элементами интерфейса, что-то типа датабиндинга в AngularJS (у него, естественно, не монополия на датабиндинг)? Выстраиваете последовательность из нескольких ajax-вызовов, что, как вариант, делается через promise-ы? Что-то другое?

Ну и непонятно, какие инструменты Вы в коце концов используете.
«непонятно что но понятно как» это здорово конечно :) как вы можете понять «как» если не можете понять «что»?

Задача написана вполне четко, если сложно мысленно подложить произвольные события, вот пример:

Вначале стартуем таймер, таймер запускает асинхронный аяксзапрос, аяксзапрос возвращает некий результат, по результатам запроса может нужно стартовать иной таймер, а может нужно сделать некие вычисления и послать новый аяксзапрос на которой…

не забудьте, что запросы могут не выполнится, и логика обработки запроса не простая «всегда одно поведение» а зависит от того что там в ответе. Степень ветвистости я вам описал.
«непонятно что но понятно как» это здорово конечно :) как вы можете понять «как» если не можете понять «что»?

Ну потому что задачи, способы и инструменты — это разные вещи. Связанные, но не одно и тоже.

То, что Вы здесь описали, вполне неплохо решается с помощью deferred/promise. На мой взгляд, даже лучше, чем событиями.
Отвечаю сразу и 'arthh' тоже.

Смотря что считать «решением», если просто закодить, то оно решается и без них, даже они мешаются. А если «решение» это ещё и «легкость поддержки, расширямости и простота», то однозначно deffered идут мимо.
Действительно. Ведь какие же могут быть сомнения в том, что велосипед, изобретенный одним товарищем, будет гораздо лучше поддерживаться и расширяться, чем распространенный и используемый в сотнях крупных проектов паттерн, который знает каждый уважающий себя js-разработчик.
Действительно, какие могут быть сомнения, что один паттерн подходит для всего, что на него похоже? :)

Паттерн flows (не путать с js библиотекой, она его реализует не в том понимании) гораздо больше подходит в данном случае.
Можно ссылку на описание «паттерна flows»?
Рекомендую ознакомиться с паттерном промисов, вы сразу поймете, как решать такие задачи гораздо легче, чем делаете вы, и не мешать в одну кучу логику работы приложения и события в дереве DOM. Это распространенный тип задач, и я бы сказал, вы усиленно разрабатываете с нуля методику, которая уже давно и успешно используется везде где можно (внизу страницы по ссылке есть готовые реализации, Angular использует облегченную версию библиотеки Q).
Мне кажется, что вы подходите к событиям с функциональным подходом. Вам важна цепочка вызовов, а не событийная абстракция. Событийная логика не подразумевает обратной связи от обработчиков.

Да и ветвление событий можно из многомерного сделать линейным, если захотеть.
Честно говоря, я 8 лет работаю с пользовательскими интерфейсами — десктопными, мобильными, вебовыми — и еще ни разу не встречал модели, которая бы работала по какой-то иной схеме, чем «событие — обработчик».
Ваша правда, событие есть всегда и обработчик есть всегда. Но вот я пытаюсь абстрагироваться от «нативных» событий (clicked, changed ...) применяя qt философию signal/slot. В своем шаблонном движке переопределяю событие на сигнал.
 button x-signal="click: sendMail"; 

И контроллер реализует этот слот:
//...
slots: {
    sendMail: function() { },
    // ...
}

Надеюсь уже сразу понятно в чем здесь преимущества.
  • избавились от «безликих» событий
  • контроллер может ничего не знать о разметке/классах
  • сигнал, как и событие может передаваться по дереву контроллеров
  • из view может ещё N-колличество элементов посылать этот сигнал
  • по необходимости, мы можем сделать слот или сигнал не активным


В результате получили гибрид, смесь дом событий и ng-click=«handler». View не должен ничего знать о контроллере/(-ах), просто определяем какие сигналы он посылает — и это дает более целостное понимание представления. Да и в контроллере мы тоже чётко группируем функции.

Но повторюсь, что это все тот же «событие — обработчик» — только немного другая «философия» ;)
jsfiddle.net/bQN9h/

$emit, $broadcast, $on.

избавились от «безликих» событий

Или я не понял, в чем особенность, или — ng-click=«sendMail()». Не вижу принципального отличия в «безликости» по сравнению с x-signal="click: sendMail".

контроллер может ничего не знать о разметке/классах

В AngularJS контроллер тоже ничего не знает про разметку/классы. Ну если Вы ему только эти знания специально не впихнете, что является плохой практикой.

сигнал, как и событие может передаваться по дереву контроллеров

Смотря зачем. Про изменения данных в AngularJS все, кому надо, узнают за счет датабиндинга. Но если сильно надо что-то другое, пример я привел.

из view может ещё N-колличество элементов посылать этот сигнал

С методами тоже вроде особых проблем нет.

по необходимости, мы можем сделать слот или сигнал не активным

Не очень понял этот пункт. В начало обработчика тоже можем засунуть return;
  • emit/on — да, это почти тоже самое, что и сигналы: передаются по дереву, можем подписаться/отписаться. Различия только в а) семантике: оперировать понятиями event/signal, проще чем — event/custom event ( хотя это вполне субъективно и возможно проблема высосана мной из пальца только потому, что мне просто так больше нравится:)) и б) возможный декларативный биндинг — то есть как в случае с ng-click, в контроллере нам не нужно явного вызова .on(..), а все слоты мы складываем в объекте «slots» нашего контроллера. И еще один плюс в декларативном биндинге, что в контроллере мы явно разделаем «методы контроллера»/«слоты».
  • недостаток ng-click в том, что мы как бы исполняем выражение по событию непосредственно из view. То есть мы из view лезем в current scope и выполняем от туда метод контроллера. А это как-то грязновато, поэтому и называется «Model-View-Whatever»
  • :) this.slots('sendMail').disable() — a) слот мы не сможем вызвать (пока не .enable(...) ), и б) если слотов больше нет у сигнала в данном или выше по дереву контроллере, то соответствующей кнопке/пункту меню присвоится :disabled. И не надо создавать лишних биндингов к кнопке.


Но оговорюсь, что это все очень похоже друг с другом и возможно такие различия в нюансах важны только мне:) !?
В принципе, при желании через свои директивы, скорее всего, разницу можно свети практически к нулю. Правда, на мой взгляд, немного будет напоминать буханку черного хлеба и троллейбус. :)
Был бы благодарен за статью на тему динамических связанных списков, за подробное описание работы AngularJS с AJAX на конкретном примере.
Абсолютная жалкая пропаганда и проба попустить jQuery…
Зачем писать этот бред:
$(function() {
     function toggle() {
        var isChecked = $('#toggleShowHide).is(':checked');
        var specialParagraph = $('#specialParagraph');
        if (isChecked) {
            specialParagraph.show();
        } else {
            specialParagraph.hide();
        }
    }
    $('#toggleShowHide).change(function() {
        toggle();
    });
    toggle();
});

Если можно:
$('#checkBox').change(function() {
  $('#content').toggle(300);
});


На офф сайте видео с кодом:
image
Когда можно
image

Я понимаю что есть и свои плюсы! Но зачем пытаясь попустить писать так много кода…
А слова мол:
возьми этот элемент и его атрибут, посмотри на его значение, сделай так-то и так-то.

Все тоже самое делает под капотом Ангулар.

А еще очень не здорово выполнять код если он не нужен… Мне лично не нравиться подход когда кто без моего ведома и нужды что-то биндит или перебирает циклами теги дабы проверить правильно ли отображается контент… Это все не нужно в большинстве ситуаций, а ресурсы то кушает…
Ваш код не будет работать аналогично примеру из статьи. См. ответ на комментарий выше.

Все тоже самое делает под капотом Ангулар.
Разумеется. Так же, как например браузер под капотом выполняет императивные инструкции по рендерингу страницы, что не лишает нас удобной возможности определять ее внешний вид в декларативном HTML, оставляя его парсинг заботам браузера.
Код не будет только если «А вот там нужно именно вот такой своеобразный функционал».
Я считаю для реализации 99% задач для проектов хватит и моего просто кода.
Это не своеобразный функционал, это data binding, который в современных приложениях используется не в пример чаще, чем дубовая логика «переключить инпут в противоположное состояние при клике». У вас, вероятно, просто еще мало опыта в разработке сложных js-приложений — рекомендую прислушаться к советам в статье, а не спорить с ними.
Ага, лучше jquery еще ничего не придумали, это факт.
На сколько я понял, использовать AngularJS для разработки сайтов не совсем удобно?
Для jQuery существует огромное количество плагинов, начиная со стилизации элементов форм и заканчивая разнообразными галереями. Практически на каждом проекте требуется подключить какую нибудь галерею или popup окна…
Подключать jQuery и AngularJS? ИХМО жесть. Только эти вопросы меня удерживают от перехода на AngularJS.
Судя по этому коду складывается впечатление, что либо с JQuery вы не знакомы, либо показательно раздули код, т.е. наговнокодили:

$(function() {
     function toggle() {
        var isChecked = $('#toggleShowHide).is(':checked');
        var specialParagraph = $('#specialParagraph');
        if (isChecked) {
            specialParagraph.show();
        } else {
            specialParagraph.hide();
        }
    }
    $('#toggleShowHide).change(function() {
        toggle();
    });
    toggle();
});


вот как это должно выглядеть:
$('#toggleShowHide').on('click', function() {
                $('#specialParagraph').toggle(this.checked);
            });
Как указано в заголовке, эта статья — перевод. Оставить сообщение автору поста можно здесь: www.artandlogic.com/blog/2013/03/angularjs-for-jquery-developers/

Кроме того, вы можете почитать аналогичное обсуждение в комментариях выше.
Зарегистрируйтесь на Хабре , чтобы оставить комментарий

Публикации

Истории