angular.element vs document.getElementById ou sélecteur jQuery avec contrôle spin (busy)

157

J'utilise la version "angularisée" du contrôle Spin, comme documenté ici: http://blog.xvitcoder.com/adding-a-weel-progress-indicator-to-your-angularjs-application/

L'une des choses que je n'aime pas dans la solution présentée est l'utilisation de jQuery dans le service qui attache efficacement le contrôle de rotation à l'élément DOM. Je préférerais utiliser des constructions angulaires pour accéder à l'élément. Je voudrais également éviter de "coder en dur" l'identifiant de l'élément auquel le spinner doit s'attacher dans le service et utiliser à la place une directive qui définit l'identifiant dans le service (singleton) afin que les autres utilisateurs du service ou le service lui-même n'a pas besoin de le savoir.

J'ai du mal avec ce que angular.element nous donne par rapport à ce que document.getElementById sur le même élément nous donne. par exemple. Cela marche:

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

Aucun de ceux-ci ne:

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

Je fais clairement quelque chose qui devrait être assez évident mal! Quelqu'un peut-il aider?

En supposant que je puisse faire fonctionner ce qui précède, j'ai un problème similaire en essayant de remplacer l'accès jQuery à l'élément: par exemple, $(target).fadeIn('fast'); fonctionne angular.element('#appBusyIndicator').fadeIn('fast')ou angular.element('appBusyIndicator').fadeIn('fast')ne fonctionne pas

Quelqu'un peut-il me montrer un bon exemple de documentation qui clarifie l'utilisation d'un "élément" angulaire par rapport à l'élément DOM? Angular "enveloppe" évidemment l'élément avec ses propres propriétés, méthodes, etc., mais il est souvent difficile d'obtenir la valeur d'origine. Par exemple, si j'ai un <input type='number'>champ et que je veux accéder au contenu original qui est visible dans l'interface utilisateur lorsque l'utilisateur tape "-" (sans les guillemets), je n'obtiens rien, probablement parce que le "type = nombre" signifie qu'Angular rejette l'entrée même si elle est visible dans l'interface utilisateur et que je veux la voir afin de pouvoir la tester et l'effacer.

Tous les pointeurs / réponses appréciés.

Merci.

irascien
la source
5
angular.element est un objet jQlite ou jQuery si vous chargez jQuery.
Neil

Réponses:

226

Cela peut fonctionner comme ça:

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

Vous encapsulez l' appel Document.querySelector()Javascript natif dans l' angular.element()appel. Ainsi, vous obtenez toujours l'élément dans un objet jqLite ou jQuery , selon qu'il jQueryest disponible / chargé ou non .

Documentation officielle pour angular.element:

Si jQuery est disponible, angular.elementest un alias pour la jQueryfonction. Si jQuery n'est pas disponible, angular.elementdélègue au sous-ensemble intégré de AngularjQuery , appelé «jQuery lite» ou jqLite .

Toutes les références d'élément dans Angularsont toujours enveloppées avec jQuery ou jqLite(comme l'argument d'élément dans une fonction de compilation de directives ou de lien). Ce ne sont jamais des DOMréférences brutes .

Si vous vous demandez pourquoi utiliser document.querySelector(), veuillez lire cette réponse .

kaiser
la source
41
Une raison pour laquelle on préférerait document.querySelector ('# ...') à simplement utiliser document.getElementById ()?
Kato
4
Vous pouvez également le faire sur un élément angulaire: var elDivParent = angular.element (elem [0] .querySelector ('# an-id')) ;, elem étant un élément angulaire. De cette façon, vous pouvez rechercher à l'intérieur d'un élément. Utile pour les tests unitaires.
unludo
4
@Kato Plus d'informations dans cette réponse . J'espère que cela pourra aider.
kaiser
travailler avec l' API Google Maps, et cela a fonctionné: var autocomplete = new google.maps.places.Autocomplete( $document[0].querySelector('#address'), { types: ['geocode'], componentRestrictions: {country: 'us'} } );. Merci pour le conseil @kaiser. Pour une raison quelconque, l'api des cartes s'est plaint que ce que j'ai passé dans le premier paramètre n'était pas une entrée lorsque je l'ai utilisé angular.element(document.querySelector('#address')), mais tout va bien qui se termine bien.
nymo
49

Vous devriez lire l' élément angulaire documentation de l' si vous ne l'avez pas encore fait, afin de comprendre ce qui est pris en charge par jqLite et ce qui n'est pas -jqlite est un sous-ensemble de jquery intégré à angular.

Ces sélecteurs ne fonctionneront pas avec jqLite seul, car les sélecteurs par identifiant ne sont pas pris en charge.

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

Alors, soit:

  • vous utilisez jqLite seul, plus limité que jquery, mais suffisant dans la plupart des situations.
  • ou vous incluez la bibliothèque jQuery complète dans votre application et vous l'utilisez comme une jquery normale, aux endroits où vous avez vraiment besoin de jquery.

Edit: Notez que jQuery doit être chargé avant angularJS afin d'avoir la priorité sur jqLite:

Le vrai jQuery a toujours la priorité sur jqLite, à condition qu'il ait été chargé avant le déclenchement de l'événement DOMContentLoaded.

Edit2: J'ai manqué la deuxième partie de la question avant:

Le problème avec <input type="number">, je pense que ce n'est pas un problème angulaire, c'est le comportement prévu de l' élément numérique html5 natif .

Il ne retournera pas de valeur non numérique même si vous essayez de le récupérer avec jquery .val()ou avec l' .valueattribut raw .

garst
la source
2
Nous avons la bibliothèque jQuery complète - sinon mon scénario $ expliqué ci-dessus ne fonctionnerait pas. Ma question portait sur la raison pour laquelle $ fonctionne mais pas angular.element - même si jQuery complet est disponible.
irascian
1
Incluez-vous jQuery avant ou après angular.js? Il doit être inclus avant angulaire. Id Selectors par faire le travail avec jQuery disponibles: jsbin.com/ohumiy/1/edit
Garst
1
Ils travaillent dans une autre directive (ie angular.element pour accéder à quelque chose par id). Le problème semble être la façon dont ce contrôle fonctionne en s'attachant à cet élément, même si je ne comprends pas pourquoi le même équivalent jQuery devrait fonctionner directement. En utilisant jQuery directement, ma directive DOIT avoir une balise de fermeture - la balise à fermeture automatique ne fonctionne pas (pas d'erreurs juste un écran vide) ce qui me fait suspecter le contrôle.
irascian
2
Sur mon deuxième point, il y a aussi quelque chose comme view $ value qui peut être utilisé pour récupérer le contenu d'un élément mais dans le cas de type d'entrée = nombre où l'utilisateur a entré "-" c'est vide. Je veux une propriété quelque chose comme $ rawInputValue sur element pour voir le contenu AVANT qu'Angular ne soit intervenu parce que le "-" est toujours dans l'interface utilisateur même si je n'y ai pas accès par programme dans ma directive.
irascian le
27
var target = document.getElementById('appBusyIndicator');

est égal à

var target = $document[0].getElementById('appBusyIndicator');
Dasun
la source
1
Il est préférable d'utiliser le deuxième exemple pour vous assurer que vos dépendances sont explicites et peuvent être moquées, non?
Luke
cela devrait l'être, mais je suppose que dans angular 2+, nous nous appuyons davantage sur les fonctionnalités dactylographiées. Vous n'avez plus à vous soucier de ces choses :)
Dasun
17

Je ne pense pas que ce soit la bonne façon d'utiliser angular. Si une méthode framework n'existe pas, ne la créez pas! Cela signifie que le cadre (ici angulaire) ne fonctionne pas de cette façon.

Avec angular, vous ne devriez pas manipuler le DOM comme ceci (la manière jquery), mais utiliser une aide angulaire telle que

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

Ou créez votre propre directive (votre propre composant DOM) afin d'avoir un contrôle total dessus.

BTW, vous pouvez voir ici http://caniuse.com/#search=queryselector querySelector est bien pris en charge et peut donc être utilisé en toute sécurité.

Thomas Decaux
la source
2
Vous avez absolument raison. Plus de 90% des cas où je pensais avoir besoin de manipuler manuellement le DOM, j'ai fini par refactoriser le code pour supprimer le besoin et à la fin le code est beaucoup plus propre.
Stephen Chung
17

Si quelqu'un utilise gulp, il affiche une erreur si nous l'utilisons document.getElementById()et il suggère d'utiliser $document.getElementById()mais cela ne fonctionne pas.

Utilisation -

$document[0].getElementById('id')
Kanchan
la source
Pourquoi injecter angulaire un tableau ici?
Peter
16

Vous pouvez accéder aux éléments en utilisant $ document ($ document doit être injecté)

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

ou avec élément angulaire, les éléments spécifiés sont accessibles comme:

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
la source
1
find () - Limité aux recherches par nom de balise
RJV
angular.element (document) .find ('. someClass'); fonctionne parfaitement bien.
Nishant Baranwal
10

Vous devez injecter $ document dans votre contrôleur et l'utiliser à la place de l'objet document d'origine.

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

Si vous n'avez pas besoin du wrap d'élément de style jquery, $ document [0] .querySelector ('# MyID') vous donnera l'objet DOM.

Adamy
la source
1
Vous pouvez également référencer directement window.document au lieu d'utiliser la surcharge du service de document Angulars $.
MatBee du
5
Bien sûr, vous pouvez si vous ne vous inquiétez pas de vous moquer de $ document dans votre unittest
Adamy
N'est-ce pas clair pour moi: j'obtiens cet avertissement: vous devriez utiliser le service $ document au lieu de l'objet document par défaut, mais qu'est-ce que cela signifie? Des documents à ce sujet? Merci!
realnot
@realnot vous pouvez accéder à l'objet de document HTML DOM de n'importe où dans votre javascript. w3schools.com/jsref/dom_obj_document.asp
Adamy
6

Cela a bien fonctionné pour moi.

angular.forEach(element.find('div'), function(node)
{
  if(node.id == 'someid'){
    //do something
  }
  if(node.className == 'someclass'){
    //do something
  }
});
susrut316
la source
3
l'élément n'est pas défini? angular.element.find n'est pas une fonction pour moi d'essayer la vôtre sans JQuery.
atterri
3
Veuillez ne pas faire ça. Utilisez l'un des autres exemples. Cela n'utilise aucune optimisation de recherche de table de hachage.
MatBee
4

Amélioration de la réponse de Kaiser:

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

N'oubliez pas d'injecter $documentdans votre directive

Rob H
la source
21
Comme il est mentionné dans la documentation angulaire pour element.find : find() - Limited to lookups by tag name, il ne fonctionne pas sur ids. Vous avez également une fermeture supplémentaire ).
paaat
3

Peut-être que je suis trop tard ici mais cela fonctionnera:

var target = angular.element(appBusyIndicator);

Remarquez, il n'y a pas appBusyIndicator, c'est une valeur d'identification simple.

Ce qui se passe dans les coulisses: (en supposant qu'il soit appliqué sur un div) (pris à partir de la ligne angular.js n °: 2769 et suivantes ...)

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

Par défaut, s'il n'y a pas de jQuery sur la page, jqLite sera utilisé. L'argument est compris en interne comme un id et l'objet jQuery correspondant est renvoyé.

Vishal Anand
la source
J'ai essayé cela en ce moment avec Angular 1.5.8. Un ID simple renvoie un résultat vide. angular.element("#myId")fonctionne bien.
Gosha U.26
peut-être avez-vous jQuery sur votre page?
Vishal Anand