angular.element vs document.getElementById lub selektor jQuery z kontrolką spin (zajęty)

157

Używam wersji Angularized kontrolki Spin, zgodnie z dokumentacją tutaj: http://blog.xvitcoder.com/adding-a-weel-progress-indicator-to-your-angularjs-application/

Jedną z rzeczy, które nie podobają mi się w przedstawionym rozwiązaniu, jest użycie jQuery w usłudze, która skutecznie łączy kontrolę spinów z elementem DOM. Wolałbym używać konstrukcji kątowych, aby uzyskać dostęp do elementu. Chciałbym również uniknąć „zakodowania” identyfikatora elementu, do którego spinner musi dołączyć w ramach usługi, i zamiast tego użyć dyrektywy, która ustawia identyfikator w usłudze (singleton), tak aby inni użytkownicy usługi lub sama usługa nie musi tego wiedzieć.

Zmagam się z tym, co daje nam angular.element, a co document.getElementById na tym samym elemencie, który daje nam identyfikator. na przykład. To działa:

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

Żadne z tych nie działa:

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

Wyraźnie robię coś, co powinno być dość oczywistym złem! Czy ktoś może pomóc?

Zakładając, że powyższe zadziała, mam podobny problem z próbą zastąpienia dostępu jQuery do elementu: np. $(target).fadeIn('fast'); Działa angular.element('#appBusyIndicator').fadeIn('fast')lub angular.element('appBusyIndicator').fadeIn('fast')nie działa

Czy ktoś może wskazać mi dobry przykład dokumentacji wyjaśniającej użycie „elementu” Angular w porównaniu z elementem DOM? Angular oczywiście „otacza” element własnymi właściwościami, metodami itp., Ale często trudno jest uzyskać oryginalną wartość. Na przykład, jeśli mam <input type='number'>pole i chcę uzyskać dostęp do oryginalnej zawartości, która jest widoczna w interfejsie użytkownika, gdy użytkownik wpisze „-” (bez cudzysłowów), nic nie otrzymuję, prawdopodobnie dlatego, że „type = number” oznacza, że ​​Angular odrzuca dane wejściowe, mimo że są widoczne w interfejsie użytkownika i chcę je zobaczyć, aby móc je przetestować i wyczyścić.

Wszelkie wskazówki / odpowiedzi mile widziane.

Dzięki.

irascian
źródło
5
angular.element to obiekt jQlite lub obiekt jQuery, jeśli ładujesz jQuery.
Neil,

Odpowiedzi:

226

To może działać tak:

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

W wywołaniu Document.querySelector()zawijasz natywne wywołanie Javascriptangular.element() . Dlatego zawsze otrzymujesz element w obiekcie jqLite lub jQuery , w zależności od tego, czy jQueryjest dostępny / załadowany.

Oficjalna dokumentacja angular.element:

Jeśli jQuery jest dostępne, angular.elementjest aliasem dla jQueryfunkcji. Jeśli jQuery nie jest dostępny, angular.elementdelegaci kątowej s wbudowaną w podgrupie jQuery, która nazywa się „jQuery Lite” lub jqLite .

Wszystkie odwołania do elementów w Angularsą zawsze opakowane w jQuery lub jqLite(na przykład argument elementu w funkcji kompilacji dyrektyw lub funkcji łączenia). Nigdy nie są surowymi DOModniesieniami.

Jeśli zastanawiasz się, dlaczego użyć document.querySelector(), przeczytaj tę odpowiedź .

kajzer
źródło
41
Czy jest jakiś powód, dla którego ktoś wolałby document.querySelector ('# ...') zamiast po prostu używania document.getElementById ()?
Kato
4
Możesz to również zrobić na elemencie kątowym: var elDivParent = angular.element (elem [0] .querySelector ('# an-id')); elem jest elementem kątowym. W ten sposób możesz szukać wewnątrz elementu. Przydatne do testów jednostkowych.
unludo
4
@Kato Więcej informacji w tej odpowiedzi . Mam nadzieję, że to pomoże.
kaiser
pracy z Google Maps API, i to działało: var autocomplete = new google.maps.places.Autocomplete( $document[0].querySelector('#address'), { types: ['geocode'], componentRestrictions: {country: 'us'} } );. Dzięki za wskazówkę @kaiser. Z jakiegoś powodu interfejs API map narzekał, że to, co przekazałem w pierwszym parametrze, nie było wejściem, gdy użyłem angular.element(document.querySelector('#address')), ale wszystko dobrze, co kończy się dobrze.
nymo
49

Jeśli jeszcze tego nie zrobiłeś, powinieneś przeczytać dokumentację dotyczącą elementów kątowych , abyś mógł zrozumieć, co jest obsługiwane przez jqLite, a co nie -jqlite jest podzbiorem jquery wbudowanym w angular.

Te selektory nie będą działać z samym jqLite, ponieważ selektory według identyfikatora nie są obsługiwane.

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

Zatem albo:

  • używasz samego jqLite, bardziej ograniczonego niż jquery, ale wystarczającego w większości sytuacji.
  • lub dołączasz pełną bibliotekę jQuery do swojej aplikacji i używasz jej jak normalnego jquery, w miejscach, w których naprawdę potrzebujesz jquery.

Edycja: Zauważ, że jQuery powinno zostać załadowane przed angularJS, aby mieć pierwszeństwo przed jqLite:

Prawdziwe jQuery ma zawsze pierwszeństwo przed jqLite, pod warunkiem, że zostało załadowane przed wywołaniem zdarzenia DOMContentLoaded.

Edit2: Wcześniej przegapiłem drugą część pytania:

Problem z <input type="number">, myślę, że nie jest to problem kątowy, jest to zamierzone zachowanie natywnego elementu liczbowego html5.

Nie zwróci wartości nienumerycznej, nawet jeśli spróbujesz ją pobrać za pomocą jquery .val()lub z .valueatrybutem raw .

garst
źródło
2
Mamy pełną bibliotekę jQuery - w przeciwnym razie mój scenariusz $ opisany powyżej nie zadziała. Moje pytanie dotyczyło tego, dlaczego $ działa, ale angular.element nie - mimo że dostępne jest pełne jQuery.
irascian
1
Czy dołączasz jQuery przed czy po angular.js? Powinien być dołączony przed kątownikiem. Selektory autorstwa id zrobić pracę z jQuery dostępnych: jsbin.com/ohumiy/1/edit
Garst
1
Pracują w innej dyrektywie (np. Angular.element, aby uzyskać dostęp do czegoś przez id). Wydaje się, że problemem jest sposób, w jaki ta kontrolka działa, dołączając się do tego elementu, chociaż nie rozumiem, dlaczego ten sam odpowiednik jQuery powinien działać bezpośrednio. Używając bezpośrednio jQuery, moja dyrektywa MUSI mieć tag zamykający - tag samozamykający nie działa (brak błędów, tylko pusty ekran), co sprawia, że ​​podejrzewam, że kontrolka.
irascian
2
W moim drugim punkcie jest również coś w rodzaju view $ value, które może być użyte do pobrania zawartości elementu, ale w przypadku input type = number, gdzie użytkownik wprowadził "-", jest to puste. Chcę, aby właściwość taka jak $ rawInputValue na elemencie wyświetlała zawartość PRZED wkroczeniem Angulara, ponieważ „-” jest nadal w interfejsie użytkownika, mimo że nie mam do niego programowego dostępu w mojej dyrektywie.
irascian
27
var target = document.getElementById('appBusyIndicator');

jest równe

var target = $document[0].getElementById('appBusyIndicator');
Dasun
źródło
1
Lepiej jest użyć drugiego przykładu, aby upewnić się, że twoje zależności są jawne i można je wyszydzać, prawda?
Łukasz
powinno być, ale wydaje mi się, że w Angular 2+ bardziej polegamy na cechach maszynopisu. Nie musisz się już martwić o te rzeczy :)
Dasun
17

Myślę, że to nie jest właściwy sposób używania kątów. Jeśli metoda ramowa nie istnieje, nie twórz jej! Oznacza to, że szkielet (tutaj kątowy) nie działa w ten sposób.

Z angularem nie powinieneś manipulować DOM w ten sposób (sposób jQuery), ale użyj pomocnika kątowego takiego jak

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

Lub utwórz własną dyrektywę (własny komponent DOM), aby mieć nad nią pełną kontrolę.

BTW, można zobaczyć tutaj http://caniuse.com/#search=queryselector querySelector jest dobrze obsługiwany, a więc może być używany bezpiecznie.

Thomas Decaux
źródło
2
Masz całkowitą rację. Ponad 90% przypadków, w których uważałem, że muszę ręcznie manipulować DOM, skończyło się na refaktoryzacji kodu, aby usunąć tę potrzebę, i ostatecznie kod jest znacznie czystszy.
Stephen Chung
17

Jeśli ktoś używa łyka, pokazuje błąd, jeśli go używamy document.getElementById()i sugeruje użycie, $document.getElementById()ale to nie działa.

Posługiwać się -

$document[0].getElementById('id')
Kanchan
źródło
Dlaczego angular wstrzykuje tutaj tablicę?
Peter
16

Możesz uzyskać dostęp do elementów za pomocą $ document ($ document musi zostać wstrzyknięty)

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

lub z elementem kątowym, do określonych elementów można uzyskać dostęp jako:

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');
Nishant Baranwal
źródło
1
find () - Ograniczone do wyszukiwania według nazwy tagu
RJV
angular.element (document) .find ('. someClass'); działa doskonale.
Nishant Baranwal
10

Powinieneś wstrzyknąć $ document do swojego kontrolera i użyć go zamiast oryginalnego obiektu dokumentu.

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

Jeśli nie potrzebujesz zawijania elementu w stylu jquery, $ document [0] .querySelector ('# MyID') da ci obiekt DOM.

Adamy
źródło
1
Równie dobrze możesz po prostu odwołać się bezpośrednio do window.document zamiast używać narzutu usługi dokumentowej Angulars $.
MatBee
5
Oczywiście, że możesz, jeśli nie martwisz się o kpienie z $ document w swoim unittest
Adamy
Nie jest dla mnie jasne: otrzymuję to ostrzeżenie: powinieneś użyć usługi $ document zamiast domyślnego obiektu dokumentu, ale co to oznacza? Jakieś dokumenty na ten temat? Dzięki!
realnot
@realnot możesz uzyskać dostęp do obiektu dokumentu HTML DOM z dowolnego miejsca w swoim javascript. w3schools.com/jsref/dom_obj_document.asp
Adamy
6

To działało dobrze dla mnie.

angular.forEach(element.find('div'), function(node)
{
  if(node.id == 'someid'){
    //do something
  }
  if(node.className == 'someclass'){
    //do something
  }
});
susrut316
źródło
3
element jest niezdefiniowany? angular.element.find nie jest dla mnie funkcją próbowania twojego bez JQuery.
wylądował
3
Proszę, nie rób tego. Użyj jednego z innych przykładów. Nie używa to żadnych optymalizacji wyszukiwania w tablicy skrótów.
MatBee
4

Poprawa odpowiedzi Kaisera:

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

Nie zapomnij wstrzyknąć $documentdo swojej dyrektywy

Rob H.
źródło
21
Jak wspomniano w kątowym dokumentacji element.find : find() - Limited to lookups by tag nameto nie działa na identyfikatory. Masz również dodatkowe zamknięcie ).
paaat
3

Może jestem za późno, ale to zadziała:

var target = angular.element(appBusyIndicator);

Zauważ, że nie ma appBusyIndicator, to zwykła wartość identyfikatora.

Co dzieje się za kulisami: (zakładając, że jest zastosowane na div) (wzięte z linii angular.js nr: 2769 i dalej ...)

/////////////////////////////////////////////
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);
  }

Domyślnie, jeśli na stronie nie ma jQuery, zostanie użyty jqLite. Argument jest rozumiany wewnętrznie jako identyfikator i zwracany jest odpowiedni obiekt jQuery.

Vishal Anand
źródło
Wypróbowałem to teraz w Angular 1.5.8. Zwykły identyfikator zwraca pusty wynik. angular.element("#myId")działa dobrze.
Gosha U.
może masz jQuery na swojej stronie?
Vishal Anand