Як я ігнорую початкове навантаження під час перегляду змін моделі в AngularJS?


184

У мене є веб-сторінка, яка служить редактором для однієї сутності, яка є глибинним графіком у властивості $ range.fieldcontainer. Після отримання відповіді від мого API REST (через $ ресурс) я додаю годинник до «fieldcontainer». Я використовую цей годинник, щоб визначити, чи сторінка / сутність "брудна". Зараз я змушую відскокнути кнопку збереження, але дуже хочу зробити кнопку збереження невидимою, поки користувач не забруднить модель.

Що я отримую, це один пусковий механізм годинника, який, на мою думку, відбувається, тому що призначення .fieldcontainer = ... відбувається відразу після того, як я створю свій годинник. Я думав просто використати властивість "dirtyCount" для поглинання початкової помилкової тривоги, але це відчуває себе дуже хакітним ... і я зрозумів, що має бути "кутовий ідіоматичний" спосіб вирішення цього питання - я не єдиний використовуючи годинник для виявлення брудної моделі.

Ось код, де я встановив годинник:

 $scope.fieldcontainer = Message.get({id: $scope.entityId },
            function(message,headers) {
                $scope.$watch('fieldcontainer',
                    function() {
                        console.log("model is dirty.");
                        if ($scope.visibility.saveButton) {
                            $('#saveMessageButtonRow').effect("bounce", { times:5, direction: 'right' }, 300);
                        }
                    }, true);
            });

Я просто думаю, що для цього повинен бути більш чистий спосіб, ніж захист коду "забруднення інтерфейсу користувача" за допомогою "if (dirtyCount> 0)" ...


Мені також потрібно мати можливість перезавантажити $ range.fieldcontainer на основі певних кнопок (наприклад, завантаження попередньої версії сутності). Для цього мені потрібно було б призупинити годинник, перезавантажити, а потім відновити годинник.
Кевін Хоффман

Чи вважаєте ви змінити мою відповідь прийнятою відповіддю? Ті, хто потребує часу, щоб прочитати його до кінця, як правило, погоджуються, що це правильне рішення.
трикстур

@trixtur У мене така ж проблема з ОП, але в моєму випадку ваша відповідь не працює, оскільки старе значення не відповідає undefined. Він має значення за замовчуванням, яке необхідне, якщо в моїй моделі оновлення не зібралася вся інформація. Тож деякі значення не змінюються, а повинні запускатись.
oliholz

@oliholz ​​Отже, замість того, щоб перевіряти наявність невизначеного, вийміть "typeof" і перевірте old_fieldcontainer на значення за замовчуванням.
трикстур

@KevinHoffman, ви повинні позначити відповідь MW правильною відповіддю для інших людей, які знаходять це питання. Це набагато краще рішення. Дякую!
Dev01

Відповіді:


118

встановити прапор перед початковим завантаженням,

var initializing = true

і тоді, коли запускається перший $ watch, робіть

$scope.$watch('fieldcontainer', function() {
  if (initializing) {
    $timeout(function() { initializing = false; });
  } else {
    // do whatever you were going to do
  }
});

Прапор буде зірвано лише наприкінці поточного циклу дайджесту, тому наступна зміна не буде заблокована.


17
Так, це спрацює, але порівняння старого та нового значення підходу, запропонованого @MW. є і простішим, і більш кутовим ідіоматичним. Чи можете ви оновити прийняту відповідь?
gerryster

2
Завжди встановлення старого Value дорівнює newValue під час першого вогню було додано спеціально, щоб уникнути необхідності цього зламати прапор
Чарлі Мартін

2
Відповідь MW насправді неправильна, оскільки нове значення старого значення не обробляє випадок, коли старе значення не визначене.
трикстур

1
Для коментаторів: це не бога модель, ніщо не заважає вам помістити «ініціалізовану» змінну всередині рамки декоратора, а потім прикрасити нею свій «звичайний» годинник. У будь-якому випадку, це повністю виходить за рамки цього питання.
переписано

1
Див. Plnkr.co/edit/VrWrHHWwukqnxaEM3J5i для порівняння запропонованих методів, як налаштування спостерігачів у зворотному виклику .get (), так і щодо ініціалізації обсягу.
переписано

483

Коли перший слухач викликається, старе значення та нове значення будуть ідентичними. Тому просто зробіть це:

$scope.$watch('fieldcontainer', function(newValue, oldValue) {
  if (newValue !== oldValue) {
    // do whatever you were going to do
  }
});

Це власне так, як кутові документи рекомендують поводитися з ним :

Після того, як спостерігач зареєстрований в області застосування, слухач fn викликається асинхронно (через $ evalAsync), щоб ініціалізувати спостерігача. У рідкісних випадках це небажано, оскільки слухач викликається, коли результат watchExpression не змінився. Щоб виявити цей сценарій у fn listener, ви можете порівняти newVal та oldVal. Якщо ці два значення однакові (===), слухач викликався через ініціалізацію


3
цей спосіб є більш ідіоматичним, ніж відповідь @
rewritten

25
Це неправильно. Суть полягає в ігноруванні зміни з nullпочаткового завантаженого значення, а не в ігноруванні зміни, коли немає змін.
переписано

1
Що ж, функція слухача завжди буде спрацьовувати при створенні годинника. Це ігнорує той перший дзвінок, який я вважаю, що хотів шукати. Якщо він спеціально хоче ігнорувати зміни, коли старе значення було недійсним, він, звичайно, може порівнювати oldValue з null ... але я не думаю, що це він хотів зробити.
МВт.

ОП спеціально запитує про спостерігачів за ресурсами (від служб REST). Спочатку створюються ресурси, потім застосовуються спостерігачі, а потім записуються атрибути з відповіді, і лише потім Angular робить цикл. Пропуск першого циклу з прапором "ініціалізація" забезпечує спосіб застосувати спостерігач лише на ініціалізованих ресурсах, але все ще спостерігаючи за змінами з / в null.
переписано

7
Це неправильно, oldValueбуде недійсним під час першого обходу.
Кевін К.

42

Я усвідомлюю, що на це питання отримали відповідь, проте в мене є пропозиція:

$scope.$watch('fieldcontainer', function (new_fieldcontainer, old_fieldcontainer) {
    if (typeof old_fieldcontainer === 'undefined') return;

    // Other code for handling changed object here.
});

Використання прапорів працює, але має трохи кодового запаху , не думаєте?


1
Це шлях. У Coffeescript це так просто, як return unless oldValue?(? - екзистенційний оператор в CS).
Кевін К.

1
Реквізит на слово код запах! також перевірити бог об'єкт lol. занадто добре.
Меттью Харвуд

1
Хоча це не прийнята відповідь, це змусило мій код працювати при заповненні об'єкта з API, і я хочу спостерігати за різними станами збереження. Дуже хороша.
Лукас Реппе Веландер

1
Спасибі - це допомогло мені
Wand Maker

1
Це чудово, проте в моєму випадку я створив дані як порожній масив перед тим, як здійснити виклик AJAX до мого API, тому перевірте довжину цього, а не тип. Але ця відповідь безумовно показує найефективніший метод принаймні.
Суботниця

4

Під час початкового завантаження поточних значень старе значення значення не визначається. Тож наведений нижче приклад допомагає вам виключити початкові навантаження.

$scope.$watch('fieldcontainer', 
  function(newValue, oldValue) {
    if (newValue && oldValue && newValue != oldValue) {
      // here what to do
    }
  }), true;

Ви, ймовірно, хочете використовувати! == з тих пір, nullі undefinedбуде відповідати в цій ситуації, або ( '1' == 1тощо)
Джеймі Пате

взагалі ти маєш рацію. Але в цьому випадку newValue та oldValue не можуть бути такими кутовими значеннями через попередні перевірки. Обидва значення мають один і той же тип також тому, що належать до одного поля. Дякую.
Тахсін Туркоз

3

Щойно дійсний стан нового val:

$scope.$watch('fieldcontainer',function(newVal) {
      if(angular.isDefined(newVal)){
          //Do something
      }
});
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.