angular.element vs document.getElementById o jQuery selector con control de giro (ocupado)

157

Estoy usando la versión "Angularizada" del control Spin, como se documenta aquí: http://blog.xvitcoder.com/adding-a-weel-progress-indicator-to-your-angularjs-application/

Una de las cosas que no me gustan de la solución mostrada es el uso de jQuery en el servicio que une efectivamente el control de giro al elemento DOM. Preferiría usar construcciones angulares para acceder al elemento. También me gustaría evitar "codificar" el identificador del elemento que el spinner necesita adjuntar dentro del servicio y, en su lugar, usar una directiva que establezca el identificador en el servicio (singleton) para que otros usuarios del servicio o el servicio en sí no necesita saber eso.

Estoy luchando con lo que angular.element nos da frente a lo que document.getElementById en el mismo elemento id nos da. p.ej. Esto funciona:

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

Ninguno de estos hace:

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

¡Claramente estoy haciendo algo que debería ser bastante obvio mal! ¿Alguien puede ayudar?

Suponiendo que puedo hacer que funcione lo anterior, tengo un problema similar al intentar reemplazar el acceso de jQuery al elemento: por ejemplo, $(target).fadeIn('fast'); funciona angular.element('#appBusyIndicator').fadeIn('fast')o angular.element('appBusyIndicator').fadeIn('fast')no

¿Alguien puede señalarme un buen ejemplo de documentación que aclare el uso de un "elemento" angular frente al elemento DOM? Angular obviamente "envuelve" el elemento con sus propias propiedades, métodos, etc., pero a menudo es difícil obtener el valor original. Por ejemplo, si tengo un <input type='number'>campo y quiero acceder a los contenidos originales que están visibles en la interfaz de usuario cuando el usuario escribe "-" (sin las comillas) no obtengo nada, presumiblemente porque el "tipo = número" significa que Angular está rechazando la entrada a pesar de que es visible en la interfaz de usuario y quiero verla para poder probarla y borrarla.

Cualquier puntero / respuesta apreciada.

Gracias.

irasciano
fuente
55
angular.element es un objeto jQlite o un objeto jQuery si está cargando jQuery.
Neil

Respuestas:

226

Se puede trabajar así:

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

Envuelve la llamada Document.querySelector()nativa de Javascript en la angular.element()llamada. Por lo tanto, siempre obtiene el elemento en un objeto jqLite o jQuery , dependiendo de si jQueryestá disponible / cargado o no .

Documentación oficial para angular.element:

Si jQuery está disponible, angular.elementes un alias para la jQueryfunción. Si jQuery no está disponible, angular.elementdelega en el subconjunto incorporado de AngularjQuery , que se llama "jQuery lite" o jqLite .

Todas las referencias de elementos Angularsiempre están envueltas con jQuery o jqLite(como el argumento del elemento en una compilación de directivas o una función de enlace). Nunca son DOMreferencias en bruto .

En caso de que se pregunte por qué usar document.querySelector(), lea esta respuesta .

emperador
fuente
41
¿Alguna razón por la que uno preferiría document.querySelector ('# ...') a simplemente usar document.getElementById ()?
Kato
44
También puede hacer esto en un elemento angular: var elDivParent = angular.element (elem [0] .querySelector ('# an-id')); siendo elem un elemento angular. De esta manera puede buscar dentro de un elemento. Útil para pruebas unitarias.
deshacer
44
@Kato Más información en esta respuesta . Espero que ayude.
kaiser
trabajar con la API de Google Maps, y esto trabajado: var autocomplete = new google.maps.places.Autocomplete( $document[0].querySelector('#address'), { types: ['geocode'], componentRestrictions: {country: 'us'} } );. Gracias por el consejo @kaiser. Por alguna razón, la API de mapas se quejó de que lo que pasé en el primer parámetro no fue una entrada cuando lo usé angular.element(document.querySelector('#address')), sino que todo termina bien.
nymo
49

Debería leer los documentos del elemento angular si aún no lo ha hecho, para que pueda comprender qué es compatible con jqLite y qué no es -jqlite es un subconjunto de jquery integrado en angular.

Esos selectores no funcionarán solo con jqLite, ya que los selectores por id no son compatibles.

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

Entonces, tampoco:

  • usa jqLite solo, más limitado que jquery, pero suficiente en la mayoría de las situaciones.
  • o incluye la versión completa de jQuery lib en su aplicación, y la usa como jquery normal, en los lugares donde realmente necesita jquery.

Editar: Tenga en cuenta que jQuery debe cargarse antes de angularJS para tener prioridad sobre jqLite:

JQuery real siempre tiene prioridad sobre jqLite, siempre que se haya cargado antes de que se active el evento DOMContentLoaded.

Edit2: me perdí la segunda parte de la pregunta antes:

El problema con <input type="number">, creo que no es un problema angular, es el comportamiento previsto del elemento numérico html5 nativo .

No devolverá un valor no numérico incluso si intenta recuperarlo con jquery .val()o con el .valueatributo sin formato.

garst
fuente
2
Tenemos la biblioteca jQuery completa; de lo contrario, mi escenario $ explicado anteriormente no funcionaría. Mi pregunta era por qué el $ funciona pero angular.element no funciona, aunque jQuery completo está disponible.
irasciano
1
¿Incluye jQuery antes o después de angular.js? Debe incluirse antes de angular. Selectores de ID hacen el trabajo con jQuery disponibles: jsbin.com/ohumiy/1/edit
Garst
1
Están trabajando en otra directiva (es decir, elemento angular para acceder a algo por id). El problema parece ser la forma en que funciona este control al unirse a ese elemento, aunque no entiendo por qué el mismo equivalente de jQuery debería funcionar directamente. Al usar jQuery directamente, mi directiva TIENE que tener una etiqueta de cierre: la etiqueta de cierre automático no funciona (no hay errores, solo una pantalla en blanco), lo que me hace sospechar del control.
2013
2
En mi segundo punto también hay algo como ver $ value que se puede usar para recuperar el contenido de un elemento, pero en el caso de input type = number donde el usuario ha ingresado "-" esto está vacío. Quiero una propiedad como $ rawInputValue en el elemento para ver el contenido ANTES de que Angular haya intervenido porque el "-" todavía está en la interfaz de usuario, aunque no tengo acceso programático en mi directiva.
2013
27
var target = document.getElementById('appBusyIndicator');

es igual a

var target = $document[0].getElementById('appBusyIndicator');
Dasun
fuente
1
Es mejor usar el segundo ejemplo para asegurarse de que sus dependencias sean explícitas y puedan burlarse, ¿verdad?
Lucas
debería ser, pero supongo que en angular 2+ solo confiamos más en las características de mecanografía. Ya no tiene que preocuparse por estas cosas :)
Dasun
17

No creo que sea la forma correcta de usar angular. Si no existe un método de marco, ¡no lo cree! Esto significa que el marco (aquí angular) no funciona de esta manera.

Con angular, no debe manipular DOM de esta manera (la forma jquery), sino usar ayuda angular como

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

O cree su propia directiva (su propio componente DOM) para tener control total sobre ella.

Por cierto, puede ver aquí http://caniuse.com/#search=queryselector querySelector está bien soportado y, por lo tanto, puede usarse de manera segura.

Thomas Decaux
fuente
2
Estás absolutamente en lo correcto. Más del 90% de los casos que pensé que necesitaba manipular manualmente el DOM, terminé refactorizando el código para eliminar la necesidad y al final el código es mucho más limpio.
Stephen Chung el
17

Si alguien usa gulp, muestra un error si lo usamos document.getElementById()y sugiere usarlo, $document.getElementById()pero no funciona.

Utilizar -

$document[0].getElementById('id')
Kanchan
fuente
¿Por qué es angular inyectando una matriz aquí?
Peter
16

Puede acceder a los elementos usando $ document ($ document necesita ser inyectado)

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

o con elemento angular, se puede acceder a los elementos especificados como:

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
fuente
1
find () - Limitado a búsquedas por nombre de etiqueta
RJV
angular.element (document) .find ('. someClass'); Funciona perfectamente bien.
Nishant Baranwal
10

Debe inyectar $ document en su controlador y usarlo en lugar del objeto de documento original.

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

Si no necesita la envoltura del elemento de estilo jquery, $ document [0] .querySelector ('# MyID') le dará el objeto DOM.

Adamy
fuente
1
También puede hacer referencia a window.document directamente en lugar de usar los gastos generales del servicio de documentos Angulars $.
MatBee
55
Por supuesto que puedes si no te preocupas por burlarte de $ document en tu unittest
Adamy
No me queda claro: recibo esta advertencia: debe usar el servicio $ document en lugar del objeto de documento predeterminado, pero ¿qué significa? ¿Algún documento al respecto? ¡Gracias!
realnot
@realnot puede acceder al objeto de documento DOM HTML desde cualquier lugar de su javascript. w3schools.com/jsref/dom_obj_document.asp
Adamy
6

Esto funcionó bien para mí.

angular.forEach(element.find('div'), function(node)
{
  if(node.id == 'someid'){
    //do something
  }
  if(node.className == 'someclass'){
    //do something
  }
});
susrut316
fuente
3
elemento no está definido? angular.element.find no es una función para mí probar la tuya sin JQuery.
aterrizó
3
Por favor no hagas esto. Use uno de los otros ejemplos. Esto no utiliza optimizaciones de búsqueda de tabla hash en absoluto.
MatBee
4

Mejora a la respuesta de Kaiser:

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

No olvides inyectar $documenten tu directiva

Rob H
fuente
21
Como se menciona en la documentación angular de element.find :, find() - Limited to lookups by tag nameno funciona en identificadores. También tienes un cierre extra ).
Paaat
3

Tal vez llegue demasiado tarde aquí, pero esto funcionará:

var target = angular.element(appBusyIndicator);

Aviso, no hay appBusyIndicator, es un valor de identificación simple.

Lo que sucede detrás de escena: (suponiendo que se aplique en un div) (tomado de la línea angular.js no: 2769 en adelante ...)

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

Por defecto, si no hay jQuery en la página, se usará jqLite. El argumento se entiende internamente como un id y se devuelve el objeto jQuery correspondiente.

Vishal Anand
fuente
He intentado esto ahora con Angular 1.5.8. Una identificación simple devuelve un resultado vacío. angular.element("#myId")funciona bien.
Gosha U.
tal vez tienes jQuery en tu página?
Vishal Anand