angular.element vs document.getElementById или селектор jQuery с управлением spin (busy)

157

Я использую «Angularized» версию элемента управления Spin, как описано здесь: http://blog.xvitcoder.com/adding-a-weel-progress-indicator-to-your-angularjs-application/

Одна из вещей, которые мне не нравятся в показанном решении, - это использование jQuery в сервисе, который эффективно прикрепляет управление вращением к элементу DOM. Я бы предпочел использовать угловые конструкции для доступа к элементу. Я также хотел бы избежать «жесткого кодирования» идентификатора элемента, к которому должен подключиться счетчик, внутри службы и вместо этого использовать директиву, которая устанавливает идентификатор в службе (singleton), чтобы другие пользователи службы или Сам сервис не должен знать это.

Я борюсь с тем, что angular.element дает нам против того, что document.getElementById на том же элементе id дает нам. например. Это работает:

  var target = document.getElementById('appBusyIndicator');

Ни один из них не делает:

  var target = angular.element('#appBusyIndicator');
  var target = angular.element('appBusyIndicator');

Я явно делаю что-то, что должно быть совершенно очевидно неправильно! Может ли кто-нибудь помочь?

Предполагая, что я могу работать выше, у меня есть похожая проблема с попыткой заменить jQuery доступ к элементу: например, $(target).fadeIn('fast'); работает angular.element('#appBusyIndicator').fadeIn('fast')или angular.element('appBusyIndicator').fadeIn('fast')нет

Может кто-нибудь указать мне хороший пример документации, которая разъясняет использование углового «элемента» против элемента DOM? Очевидно, что Angular «оборачивает» элемент своими свойствами, методами и т. Д., Но зачастую трудно получить исходное значение. Например, если у меня есть <input type='number'>поле и я хочу получить доступ к исходному содержимому, которое видно в пользовательском интерфейсе, когда пользователь вводит «-» (без кавычек), я ничего не получаю, предположительно, потому что «type = number» означает, что Angular отклоняет ввод, даже если он виден в пользовательском интерфейсе, и я хочу его увидеть, чтобы я мог проверить его и очистить.

Любые указатели / ответы приветствуются.

Спасибо.

irascian
источник
5
angular.element - это объект jQlite или объект jQuery, если вы загружаете jQuery.
Нил

Ответы:

226

Это может работать так:

var myElement = angular.element( document.querySelector( '#some-id' ) );

Вы включаете Document.querySelector()нативный вызов Javascript в angular.element()вызов. Таким образом, вы всегда получаете элемент в объекте jqLite или jQuery , в зависимости от того jQuery, доступен он или загружен.

Официальная документация для angular.element:

Если jQuery доступен, angular.elementэто псевдоним для jQueryфункции. Если jQuery недоступен, angular.elementделегирует встроенное подмножество AngularjQuery , которое называется «jQuery lite» или jqLite .

Все ссылки на Angularэлементы всегда заключаются в jQuery или jqLite(например, аргумент элемента в компиляции директив или в функцию link). Они никогда не являются необработанными DOMссылками.

Если вы не знаете, зачем пользоваться document.querySelector(), прочитайте этот ответ .

кайзер
источник
41
Любая причина, по которой вы бы предпочли document.querySelector ('# ...') простому использованию document.getElementById ()?
Като
4
Вы также можете сделать это для углового элемента: var elDivParent = angular.element (elem [0] .querySelector ('# an-id'));; elem - угловой элемент. Таким образом, вы можете искать внутри элемента. Полезно для юнит-тестов.
unludo
4
@Kato Больше информации в этом ответе . Надеюсь, это поможет.
Кайзер
работы с Google Maps API, и это сработало: var autocomplete = new google.maps.places.Autocomplete( $document[0].querySelector('#address'), { types: ['geocode'], componentRestrictions: {country: 'us'} } );. Спасибо за подсказку @kaiser. По какой-то причине API карты жаловалось, что то, что я передал в первом параметре, не было вводом, когда я использовал angular.element(document.querySelector('#address')), но все хорошо, что хорошо кончается.
Ним
49

Вам следует прочитать документацию по угловым элементам, если вы еще этого не сделали, чтобы вы могли понять, что поддерживается jqLite, а что нет -jqlite - это подмножество jquery, встроенное в angular.

Эти селекторы не будут работать только с jqLite, так как селекторы по id не поддерживаются.

  var target = angular.element('#appBusyIndicator');
  var target = angular.element('appBusyIndicator');

Итак, либо:

  • вы используете только jqLite, более ограниченный, чем jquery, но достаточно в большинстве ситуаций.
  • или вы включаете полную библиотеку jQuery в свое приложение и используете ее как обычный jquery в тех местах, где вам действительно нужен jquery.

Изменить: Обратите внимание, что jQuery должен быть загружен до angularJS, чтобы иметь приоритет над jqLite:

Реальный jQuery всегда имеет приоритет над jqLite, если он был загружен до того, как сработало событие DOMContentLoaded.

Edit2: я пропустил вторую часть вопроса раньше:

Проблема с <input type="number">, я думаю, это не угловая проблема, это предполагаемое поведение нативного элемента номера html5.

Он не вернет нечисловое значение, даже если вы попытаетесь получить его с помощью jquery .val()или необработанного .valueатрибута.

Гарст
источник
2
У нас есть полная библиотека jQuery - иначе мой сценарий $, описанный выше, не сработает. Мой вопрос был о том, почему $ работает, а angular.element - даже если полная версия jQuery доступна.
иракский
1
Вы включаете jQuery до или после angular.js? Это должно быть включено до угловой. Селекторы по идентификатору сделать работу с JQuery доступным: jsbin.com/ohumiy/1/edit
Гарстую
1
Они работают в другой директиве (т.е. angular.element для доступа к чему-либо по id). Кажется, проблема в том, как этот элемент управления работает, привязывая себя к этому элементу, хотя я не понимаю, почему тот же эквивалент jQuery должен работать напрямую. Непосредственно используя jQuery, моя директива ДОЛЖНА иметь закрывающий тег - самозакрывающийся тег не работает (без ошибок, просто пустой экран), что заставляет меня заподозрить элемент управления.
иракский
2
На моем втором пункте также есть что-то вроде view $ value, которое можно использовать для получения содержимого элемента, но в случае ввода type = number, где пользователь вводит "-", это пусто. Я хочу, чтобы у элемента было что-то вроде $ rawInputValue, чтобы увидеть содержимое ДО того, как в него вступил Angular, потому что «-» все еще находится в пользовательском интерфейсе, хотя у меня нет доступа к нему программно в моей директиве.
иракский
27
var target = document.getElementById('appBusyIndicator');

равно

var target = $document[0].getElementById('appBusyIndicator');
Dasun
источник
1
Лучше использовать второй пример, чтобы убедиться, что ваши зависимости явные и могут быть смоделированы, верно?
Лука
так и должно быть, но я полагаю, что в Angular 2+ мы больше полагаемся на возможности машинописи. Больше не нужно беспокоиться об этих вещах :)
Dasun
17

Я не думаю, что это правильный способ использования угловых. Если метод фреймворка не существует, не создавайте его! Это означает, что каркас (здесь угловой) не работает таким образом.

С angular вы не должны манипулировать DOM, как это (способ jquery), а использовать угловой помощник, такой как

<div ng-show="isLoading" class="loader"></div>

Или создайте свою собственную директиву (свой собственный компонент DOM), чтобы иметь полный контроль над ней.

Кстати, вы можете увидеть здесь http://caniuse.com/#search=queryselector querySelector хорошо поддерживается и поэтому может безопасно использоваться.

Томас Деко
источник
2
Ты совершенно прав. В 90% + случаев, когда я думал, что мне нужно вручную манипулировать DOM, я в конечном итоге реорганизовал код, чтобы устранить необходимость, и в итоге код стал намного чище.
Стивен Чунг
17

Если кто-то использует gulp, он показывает ошибку, если мы используем, document.getElementById()и предлагает использовать$document.getElementById() но это не работает.

Используйте -

$document[0].getElementById('id')
Канчан
источник
Почему угловая инъекция массива здесь?
Питер
16

Вы можете получить доступ к элементам, используя $ document (необходимо вставить $ document)

var target = $document('#appBusyIndicator');
var target = $document('appBusyIndicator');

или с угловым элементом, указанные элементы могут быть доступны как:

var targets = angular.element(document).find('div'); //array of all div
var targets = angular.element(document).find('p');
var target = angular.element(document).find('#appBusyIndicator');
Нишант Баранвал
источник
1
find () - Ограничен поиском по имени тега
RJV
angular.element (документ) .find (»SomeClass.); отлично работает
Нишант Баранвал
10

Вы должны вставить $ document в свой контроллер и использовать его вместо оригинального объекта документа.

var myElement = angular.element($document[0].querySelector('#MyID'))

Если вам не требуется перенос элемента стиля jquery, $ document [0] .querySelector ('# MyID') выдаст вам объект DOM.

Adamy
источник
1
Вы также можете просто ссылаться на window.document напрямую вместо использования служебных данных Angulars $ document.
MatBee
5
Конечно, вы можете, если вы не беспокоитесь о том, чтобы высмеивать документ $ в своем юнит-тесте
Адами
Мне не ясно: я получаю это предупреждение: вы должны использовать службу $ document вместо объекта документа по умолчанию, но что это значит? Есть документы по этому поводу? Спасибо!
Realnot
@realnot вы можете получить доступ к объекту документа HTML DOM из любой точки вашего JavaScript. w3schools.com/jsref/dom_obj_document.asp
Адами
6

Это сработало для меня хорошо.

angular.forEach(element.find('div'), function(node)
{
  if(node.id == 'someid'){
    //do something
  }
  if(node.className == 'someclass'){
    //do something
  }
});
susrut316
источник
3
элемент не определен? angular.element.find - это не функция, которую я пробую без JQuery.
приземлился
3
Пожалуйста, не делай этого. Используйте один из других примеров. Это вообще не использует оптимизацию поиска в хеш-таблице.
MatBee
4

Улучшение ответа кайзера:

var myEl = $document.find('#some-id');

Не забудьте ввести $documentв вашу директиву

Роб Х
источник
21
Как упоминалось в угловой документации для element.find :, find() - Limited to lookups by tag nameон не работает с идентификаторами. Также у вас есть дополнительное закрытие ).
paaat
3

Может быть, я слишком поздно здесь, но это будет работать

var target = angular.element(appBusyIndicator);

Обратите внимание, нет appBusyIndicator, это просто значение идентификатора.

Что происходит за кулисами: (при условии, что оно применяется к div) (взято из строки angular.js №: 2769 и далее ...)

/////////////////////////////////////////////
function JQLite(element) {     //element = div#appBusyIndicator
  if (element instanceof JQLite) {
    return element;
  }

  var argIsString;

  if (isString(element)) {
    element = trim(element);
    argIsString = true;
  }
  if (!(this instanceof JQLite)) {
    if (argIsString && element.charAt(0) != '<') {
      throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
    }
    return new JQLite(element);
  }

По умолчанию, если на странице нет jQuery, будет использоваться jqLite. Аргумент внутренне понимается как идентификатор, и возвращается соответствующий объект jQuery.

Вишал Ананд
источник
Я попробовал это прямо сейчас с Angular 1.5.8. Простой идентификатор возвращает пустой результат. angular.element("#myId")работает хорошо.
Гоша У.
может быть, у вас есть JQuery на вашей странице?
Вишал Ананд