Вопрос

Как работает привязка данных в AngularJS рамки?

Я не нашел технических подробностей о их сайт.Более или менее ясно, как это работает, когда данные передаются из представления в модель.Но как AngularJS отслеживает изменения свойств модели без сеттеров и геттеров?

Я обнаружил, что существуют Наблюдатели за JavaScript это может выполнить эту работу.Но они не поддерживаются в Internet Explorer 6 и Internet Explorer 7.Итак, как AngularJS узнает, что я изменил, например, следующее, и отразил это изменение в представлении?

myobject.myproperty="new value";
Это было полезно?

Решение

angularjs запоминает значение и сравнивает его с предыдущим значением. Это базовая грязная проверка. Если есть изменение стоимости, то он запускает событие изменений.

Метод $apply(), который вы звоните, когда вы переходите из мира без Angularjs в мир Angularjs, называет $digest(). Дайджест просто простая старая грязная проверка. Работает на всех браузерах и полностью предсказуемо.

Чтобы контрастировать грязные проверки (Angularjs) против смены слушателей ( knockoutjs и packbone.js ): во время грязной проверки может показаться простым, и даже неэффективным (я обращу на это позже), оказывается, что это семантически правильно Время, в то время как смены слушателей имеют много странных угловых случаев и нуждаются в таких точках, как отслеживание зависимостей, чтобы сделать его более семантически правильным. Отслеживание зависимостей Knockoutjs - это умная функция для проблемы, которую angularjs не имеет.

Вопросы с изменением слушателей:

    .
  • Синтаксис Zrocious, поскольку браузеры не поддерживают его в родом. Да, есть прокси, но они не имеют семантически правильные во всех случаях, и, конечно, нет прокси на старых браузерах. Суть в том, что грязная проверка позволяет делать pojo , тогда как knockoutjs и backbone.js заставляют вас наследовать Классы и получить доступ к вашим данным через Accessors.
  • Изменить коалесценцию. Предположим, у вас есть массив предметов. Скажем, вы хотите добавить элементы в массив, так как вы зацикливаете, чтобы добавить, каждый раз, когда вы добавляете, что вы стреляете в изменение событий, что делает пользовательский интерфейс. Это очень плохо для производительности. То, что вы хотите, это обновить UI только один раз, в конце. События изменений слишком мелкозернистые.
  • Сменяйте слушателей сразу на сетте, что является проблемой, поскольку прослушиватель изменений может дополнительно изменить данные, которые зажигают больше событий изменения. Это плохо, поскольку на вашем стеке, у вас может быть несколько событий смены сразу. Предположим, у вас есть две массивы, которые необходимо хранить в синхронизации по любой причине. Вы можете добавить только к одному или другому, но каждый раз, когда вы добавляете вас уволить событие с изменением, которое теперь имеет непоследовательный взгляд на мир. Это очень похожая проблема для блокировки потоков, которую JavaScript избегает, поскольку каждый обратный вызов выполняет исключительно и до завершения. Изменение событий нарушают это, поскольку загадки могут иметь далеко идущие последствия, которые не предназначены и не очевидно, что создает проблему по ниве снова. Оказывается, что вы хотите сделать, это отложить выполнение слушателя и гарантировать, что только один слушатель работает одновременно, поэтому любой код может свободно изменять данные, и он знает, что ни один другой код не работает во время этого ,

Как насчет производительности?

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

Люди:

    .
  • медленно - что-то быстрее, чем 50 мс незаметно для людей, и, таким образом, можно рассматривать как «мгновенный».

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

Так что реальный вопрос в том, что это: сколько сравнений можно сделать на браузере в 50 мс? Это тяжелый вопрос, чтобы ответить, что многие факторы вступают в игру, но вот тестовый случай: http://jsperf.com/angularjs -Игест / 6 , который создает 10000 наблюдателей. На современном браузере это занимает чуть менее 6 мс. On Internet Explorer 8 Требуется около 40 мс. Как видите, это не проблема даже на медленные браузеры в наши дни. Существует предостережение: сравнения должны быть простыми в соответствие с ограничением времени ... К сожалению, это слишком легко добавить медленное сравнение на Angularjs, поэтому легко создавать медленные приложения, когда вы не знаете, что вы делаем. Но мы надеемся получить ответ, предоставив модуль приборов, который покажет вам, какие медленные сравнения.

Оказывается, что видеоигры и графические процессоры используют грязно-проверку подход, в частности, поскольку он согласуется. Пока они преодолевают скорость обновления монитора (обычно 50-60 Гц, или каждые 16,6-20 мс), любая производительность над тем, что это отходы, поэтому вам лучше рисовать больше вещей, чем получить FPS выше. .

Другие советы

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

Как указано в Misko, около 2000 привязки - это то, где вы начинаете видеть проблемы, но в любом случае у вас не должно быть более 2000 кадров информации на странице. Это может быть правдой, но не каждый связывание данных виден для пользователя. После того, как вы начнете построить любой виджет или сетку данных с двусторонним привязкой, вы можете легко ударили 2000 привязки, не имея плохого UX.

Рассмотрим, например, в поле «Комбо», где вы можете ввести текст для фильтрации доступных вариантов. Такой контроль может иметь ~ 150 предметов и все еще быть очень полезным. Если у него есть дополнительная функция (например, определенный класс в текущей выбранной опции), вы начинаете получать 3-5 привязки на опцию. Поместите три из этих виджетов на странице (например, один, чтобы выбрать страну, другую, чтобы выбрать город в указанной стране, и третий выберите отель), и вы уже где-то от 1000 до 2000 привязки.

Или рассмотрите рассмотрение данных в корпоративном веб-приложении. 50 рядов на страницу не являются необоснованными, каждая из которых может иметь 10-20 столбцов. Если вы построете это с NG-Repeats, и / или иметь информацию в некоторых ячеек, которые используют некоторые привязки, вы можете приближаться к 2000 привязки только с этой сеткой.

Я считаю, что это проблема огромная проблема при работе с angularjs, и единственным решением, которые я смог найти до сих пор, - это построить виджеты без использования двусторонней привязки, а не использовать Ngonce, Reeregistering Watchers и подобные хитрости или построить директивы, которые строят DOM с манипуляцией jQuery и DOM. Я чувствую, что это поражает цель использования угловых в первую очередь.

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

<Сильный> TL; DR

Связывание данных может вызвать проблемы с производительностью на сложных страницах.

Грязной проверкой $scope объект

Angular поддерживает простой array наблюдателей в $scope объекты.Если вы проверите какой-либо $scope вы обнаружите, что он содержит array называется $$watchers.

Каждый наблюдатель является object который содержит среди прочего

  1. Выражение, за которым следит наблюдатель.Это может быть просто attribute имя или что-то более сложное.
  2. Последнее известное значение выражения.Это можно проверить по текущему вычисленному значению выражения.Если значения различаются, наблюдатель активирует функцию и отметит $scope такой же грязный.
  3. Функция, которая будет выполнена, если наблюдатель загрязнен.

Как определяются наблюдатели

В AngularJS существует много разных способов определения наблюдателя.

  • Вы можете явно $watch а attribute на $scope.

    $scope.$watch('person.username', validateUnique);
    
  • Вы можете разместить {{}} интерполяция в вашем шаблоне (для вас будет создан наблюдатель на текущем $scope).

    <p>username: {{person.username}}</p>
    
  • Вы можете задать такую ​​директиву, как ng-model чтобы определить наблюдателя для вас.

    <input ng-model="person.username" />
    

А $digest цикл проверяет всех наблюдателей на соответствие их последнему значению

Когда мы взаимодействуем с AngularJS через обычные каналы (ng-model, ng-repeat и т. д.), директива запускает цикл дайджеста.

Цикл дайджеста – это обход в глубину $scope и все его дети.Для каждого $scope object, мы перебираем его $$watchers array и оцените все выражения.Если новое значение выражения отличается от последнего известного значения, вызывается функция наблюдателя.Эта функция может перекомпилировать часть DOM, пересчитать значение $scope, вызвать AJAX request, все, что вам нужно, чтобы это сделать.

Просматривается каждая область действия, каждое контрольное выражение оценивается и проверяется по последнему значению.

Если наблюдатель срабатывает, $scope грязный

Если наблюдатель срабатывает, приложение узнает, что что-то изменилось, и $scope помечен как грязный.

Функции наблюдателя могут изменять другие атрибуты $scope или на родителе $scope.Если один $watcher функция была активирована, мы не можем гарантировать, что другие наши $scopes все еще чисты, поэтому мы снова выполняем весь цикл дайджеста.

Это связано с тем, что AngularJS имеет двустороннюю привязку, поэтому данные можно передавать обратно в $scope дерево.Мы можем изменить значение на более высокое $scope это уже переварено.Возможно, мы изменим значение на $rootScope.

Если $digest грязно, мы выполняем всю $digest цикл снова

Мы постоянно просматриваем $digest до тех пор, пока цикл дайджеста не станет чистым (все $watch выражения имеют то же значение, что и в предыдущем цикле), или мы достигнем предела дайджеста.По умолчанию этот предел установлен на уровне 10.

Если мы достигнем предела дайджеста, AngularJS выдаст ошибку в консоли:

10 $digest() iterations reached. Aborting!

Дайджест тяжел для машины, но легок для разработчика

Как видите, каждый раз, когда что-то меняется в приложении AngularJS, AngularJS проверяет каждого наблюдателя в $scope иерархию, чтобы увидеть, как реагировать.Для разработчика это огромное преимущество в производительности, поскольку теперь вам почти не нужно писать код подключения, AngularJS просто заметит, если значение изменилось, и приведет остальную часть приложения в соответствие с изменениями.

Однако с точки зрения машины это крайне неэффективно и замедлит работу нашего приложения, если мы создадим слишком много наблюдателей.Миско назвал цифру около 4000 наблюдателей, прежде чем ваше приложение станет медленным в старых браузерах.

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

Как избежать создания слишком большого количества наблюдателей

Каждый раз, когда ваш пользователь взаимодействует с вашим приложением, каждый наблюдатель в вашем приложении будет оцениваться хотя бы один раз.Большая часть оптимизации приложения AngularJS — это уменьшение количества наблюдателей в вашем приложении. $scope дерево.Один из простых способов сделать это — с помощью одноразовая привязка.

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

<p>{{::person.username}}</p>

или

<p ng-bind="::person.username"></p>

Привязка будет активирована только тогда, когда содержащий шаблон будет отображен и данные загружены в $scope.

Это особенно важно, когда у вас есть ng-repeat со многими предметами.

<div ng-repeat="person in people track by username">
  {{::person.username}}
</div>

Это мое основное понимание.Вполне возможно, что это неправильно!

  1. Предметы следят за передачей функции (возвращая вещь, которую нужно наблюдать) в $watch метод.
  2. Изменения в наблюдении должны быть внесены в блок кода, завернутый $apply метод.
  3. В конце $apply тот $digest метод вызывается, который проходит через каждую из часов и проверяет, изменились ли они с момента прошлого раза $digest побежал.
  4. Если обнаруживаются какие-либо изменения, дайджест вызывается снова, пока все изменения не стабилизируются.

При обычной разработке синтаксис привязки данных в HTML сообщает компилятору AngularJS создать для вас часы, а методы контроллера запускаются внутри. $apply уже.Так что для разработчика приложения все прозрачно.

Я и сам некоторое время задавался этим вопросом.Без сеттеров, как это работает AngularJS обратите внимание на изменения в $scope возражать?Опрашивает ли это их?

Что он на самом деле делает, так это:Любое "нормальное" место, где вы изменяете модель, уже было вызвано из внутренностей AngularJS, поэтому он автоматически вызывает $apply для вас после запуска вашего кода.Допустим, у вашего контроллера есть метод, подключенный к ng-click на каком-то элементе.Потому что AngularJS связав вызов этого метода воедино для вас, у него есть шанс выполнить $apply в соответствующем месте.Аналогично, для выражений, которые отображаются прямо в представлениях, они выполняются с помощью AngularJS таким образом, это делает $apply.

Когда в документации говорится о необходимости вызова $apply вручную для ввода кода за пределами AngularJS, речь идет о коде, который при запуске не вытекает из AngularJS сам по себе в стеке вызовов.

Объяснение с картинками:

Связывание данных нуждается в сопоставлении

Ссылка в объеме не совсем эта ссылка в шаблоне. Когда вы передаете данные два объекта, вам нужен третий, который слушает первый и изменить другой.

 Введите описание изображения здесь

Здесь, когда вы измените генеракодицетагкод, вы касаетесь data-ref3 . И классическая обработка данных - связывание Mecanism изменится Data-ref4 . Так как остальные выражения Renacodicetacodcode будут двигаться?

События приводят к $ digest ()

 Введите описание изображения здесь

угловой указывает генеракодицетагкод и генеракодицетагкод каждого связывания. И после каждого углового события известный цикл <input> проверит список наблюдения, чтобы увидеть, изменятся ли что-то. Эти угловые события являются {{data}}, oldValue, newValue завершены ... Генеракодицетагкод будет петлей до тех пор, пока любой генеракодицетагкод отличается от генеракодицетагкода.

На предыдущей картинке заметит, что data-ref1 и data-ref2 изменились.

Выводы

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

Другой момент состоит в том, что вы можете легко понять влияние на глубину простого привязки к памяти и ЦП. Надеюсь, настольные столы достаточно жира, чтобы справиться с этим. Мобильные телефоны не так прочные.

Очевидно, что нет периодической проверки генеракодицетагкода, будь то какие-либо изменения в прикрепленных к нему объекты. Не все объекты, прикрепленные к объему, наблюдаются. Прототически охват прототически поддерживает $$ Watchers . Scope ITERATES через этот генеракодицетагCode, когда вызывается Scope.

Угловая добавляет наблюдателя для наблюдателей $$ для каждого из них

  1. {{Express}} - в ваших шаблонах (и где-нибудь еще, где есть выражение) или когда мы определяем NG-модель.
  2. $ case. $ Watch («Expression / Function») - в вашем JavaScript мы можем просто прикрепить объект области для угла для наблюдения.

$ WATCH Функция принимает три параметра:

  1. Первый - это функция наблюдения, которая просто возвращает объект, или мы можем просто добавить выражение.

  2. Второй - это функция слушателя, которая будет называться, когда есть изменение объекта. Все такие вещи, как изменения DOM будут реализованы в этой функции.

  3. Третий - дополнительный параметр, который принимает логию. Если это правда, угловые глубокие наблюдают за объектом, и если его ложное угловое число просто делает ссылку, наблюдая на объекте. Грубая реализация $ WAST выглядит так, как этот

Scope.prototype.$watch = function(watchFn, listenerFn) {
   var watcher = {
       watchFn: watchFn,
       listenerFn: listenerFn || function() { },
       last: initWatchVal  // initWatchVal is typically undefined
   };
   this.$$watchers.push(watcher); // pushing the Watcher Object to Watchers  
};
.

Есть интересная вещь в угловом углу, называемом переваринным циклом. Цикл $ digest начинается в результате вызова до $. $ Digest (). Предположим, что вы измените модель объема $ в функцию обработчика через директиву NG-Click. В этом случае Angularjs автоматически запускает цикл $ digest, вызывая $ digest (). В дополнение к ng-Щелкните, есть несколько других встроенных директив / служб, которые позволяют изменять модели (например, NG-модель, $ timeout и т. Д.) и автоматически вызвать цикл $ digest. Грубая реализация $ Digest выглядит так.

Scope.prototype.$digest = function() {
      var dirty;
      do {
          dirty = this.$$digestOnce();
      } while (dirty);
}
Scope.prototype.$$digestOnce = function() {
   var self = this;
   var newValue, oldValue, dirty;
   _.forEach(this.$$watchers, function(watcher) {
          newValue = watcher.watchFn(self);
          oldValue = watcher.last;   // It just remembers the last value for dirty checking
          if (newValue !== oldValue) { //Dirty checking of References 
   // For Deep checking the object , code of Value     
   // based checking of Object should be implemented here
             watcher.last = newValue;
             watcher.listenerFn(newValue,
                  (oldValue === initWatchVal ? newValue : oldValue),
                   self);
          dirty = true;
          }
     });
   return dirty;
 };
.

Если мы используем функцию JavaScript STRIMEAUT () функцию для обновления модели области, угловой не имеет возможности знать, что вы можете изменить. В этом случае наша обязанность вызовов $ Prine () вручную, которая запускает цикл $ digest. Точно так же, если у вас есть директива, которая устанавливает слушатель события DOM и изменяет некоторые модели внутри функции обработчика, вам необходимо позвонить в $ Apply (), чтобы убедиться, что изменения вступают в силу. Большое представление о применении доллара заключается в том, что мы можем выполнить какой-то код, который не знает о угловом углу, этот код может все еще менять вещи на прицелах. Если мы обертываем этот код в $ Apply, он позаботится о звонке $ digest (). Грубая реализация $ prime ().

Scope.prototype.$apply = function(expr) {
       try {
         return this.$eval(expr); //Evaluating code in the context of Scope
       } finally {
         this.$digest();
       }
};
.

Angularjs обрабатывает механизм связывания данных с помощью трех мощных функций: $ watch () , $ digest () и $ prime () . Большую часть времени Angularjs позвонит $ Scope. $ Watch () и $ Case. $ Digest (), но В некоторых случаях вам, возможно, придется вызывать эти функции вручную, чтобы обновить с новыми значениями.

<Сильные> $ Смотреть () : -

Эта функция используется для наблюдения изменений в переменной на объеме $. Он принимает три параметра: выражение, слушатель и объект равенства, где объект слушателя и равенства являются необязательными параметрами.

<Сильные> $ digest () -

Эта функция итерации через все часы в объекте $ Scope, и его ребенок $ Scope объекты

(если у него есть). Когда $ digest () итерат над часами, он проверяет, имеет ли значение выражения измененный. Если значение изменилось, Angularjs вызывает слушателя с Новое значение и старое значение. Функция $ digest () называется Всякий раз, когда Angularjs думает, что это необходимо. Например, после кнопки Нажмите или после вызова AJAX. У вас могут быть некоторые случаи, когда angularjs не вызывает функцию $ digest () для вас. В таком случае вы должны Назовите это сами.

$ PROMENT () -

угловые UNTO Magically обновляет только те модельные изменения, которые Внутри ангулярных контекста. Когда вы делаете изменения в любой модели за пределами Угловой контекст (например, браузер DOM событий, Settimeout, XHR или третий партийные библиотеки), то вам нужно сообщить угловой из изменений Вызов $ pribe () вручную. Когда Function Function Function Function () Angularjs звонит $ digest () внутренне, поэтому все привязывания данных Обновлено.

Случилось так, что мне нужно было связать модель данных человека с формой, то, что я сделал, было прямым сопоставлением данных с формой.

Например, если бы в модели было что-то вроде:

$scope.model.people.name

Управляющий ввод формы:

<input type="text" name="namePeople" model="model.people.name">

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

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

  1. Односторонняя связывание данных - это подход, в котором значение взято из модели данных и вставлена в HTML-элемент.Нет возможности обновить модель с вида.Используется в классических шаблонах.Эти системы связывают данные только в одном направлении.

  2. Связывание данных в угловых приложениях - автоматическая синхронизация данных между компонентами модели и просмотра.

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

Вот пример привязки данных с помощью AngularJS с использованием поля ввода.Я объясню позже

HTML-код

<div ng-app="myApp" ng-controller="myCtrl" class="formInput">
     <input type="text" ng-model="watchInput" Placeholder="type something"/>
     <p>{{watchInput}}</p> 
</div>

AngularJS-код

myApp = angular.module ("myApp", []);
myApp.controller("myCtrl", ["$scope", function($scope){
  //Your Controller code goes here
}]);

Как вы можете видеть в примере выше, AngularJS использует ng-model слушать и наблюдать, что происходит с элементами HTML, особенно с input поля.Когда что-то происходит, делайте что-нибудь.В нашем случае ng-model привязан к нашему представлению, используя обозначение усов {{}}.Все, что набирается в поле ввода, мгновенно отображается на экране.И в этом прелесть привязки данных с использованием AngularJS в его простейшей форме.

Надеюсь это поможет.

См. рабочий пример здесь.Коден

AngularJS поддерживает Двусторонняя привязка данных.
Означает, что вы можете получить доступ к данным Просмотр -> Контроллер & Контроллер -> Просмотр

например

1)

// If $scope have some value in Controller. 
$scope.name = "Peter";

// HTML
<div> {{ name }} </div>

O/P

Peter

Вы можете привязать данные в ng-model Нравится:-
2)

<input ng-model="name" />

<div> {{ name }} </div>

Здесь, в приведенном выше примере, какой бы ввод ни ввел пользователь, он будет виден в <div> метка.

Если хотите привязать ввод из html к контроллеру:-
3)

<form name="myForm" ng-submit="registration()">
   <label> Name </lbel>
   <input ng-model="name" />
</form>

Здесь, если вы хотите использовать ввод name затем в контроллере,

$scope.name = {};

$scope.registration = function() {
   console.log("You will get the name here ", $scope.name);
};

ng-model связывает наш взгляд и отображает его в выражении {{ }}.
ng-model это данные, которые отображаются пользователю в представлении и с которыми пользователь взаимодействует.
Таким образом, легко привязывать данные в AngularJS.

angular.js создает наблюдатель для каждой модели, которую мы создаем в поле зрения.Всякий раз, когда модель изменяется, к модели прилагается класс «NG-грязный», поэтому наблюдатель будет соблюдать все модели, которые имеют класс «NG-Dryck» и обновляют их значения в контроллере и наоборот.

привязка данных:

Что такое привязка данных?

Всякий раз, когда пользователь меняет данные в представлении, происходит обновление этого изменения в модели области, и наоборот.

Как это возможно?

Короткий ответ :С помощью цикла дайджеста.

Описание :Angular js устанавливает наблюдателя в модели области, который запускает функцию прослушивателя, если в модели происходят изменения.

$scope.$watch('modelVar' , function(newValue,oldValue){

//Код обновления Dom новым значением

});

Итак, когда и как вызывается функция наблюдателя?

Функция Watcher вызывается как часть цикла дайджеста.

Цикл дайджеста называется автоматически запускаемым как часть angular js, встроенного в директивы/сервисы, такие как ng-model, ng-bind, $timeout, ng-click и другие.это позволит вам запустить цикл дайджеста.

Функция цикла дайджеста:

$scope.$digest() -> digest cycle against the current scope.
$scope.$apply() -> digest cycle against the parent scope 

то есть$rootScope.$apply()

Примечание:$apply() равен $rootScope.$digest(), это означает, что грязная проверка начинается прямо с корня, сверху или родительской области вплоть до всех дочерних $областей в приложении angular js.

Вышеупомянутые функции работают в браузерах IE для упомянутых версий, просто убедитесь, что ваше приложение является приложением angular js, что означает, что вы используете файл сценария angularjs framework, указанный в теге сценария.

Спасибо.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top