angular.element vs document.getElementById ou seletor jQuery com controle de rotação (ocupado)

157

Estou usando a versão "Angularised" do controle Spin, conforme documentado aqui: http://blog.xvitcoder.com/adding-a-weel-progress-indicator-to-your-angularjs-application/

Uma das coisas que eu não gosto na solução mostrada é o uso do jQuery no serviço que efetivamente anexa o controle de rotação ao elemento DOM. Eu preferiria usar construções angulares para acessar o elemento. Eu também gostaria de evitar "codificar" o ID do elemento ao qual o girador precisa se conectar ao serviço e, em vez disso, usar uma diretiva que defina o ID no serviço (singleton) para que outros usuários do serviço ou o serviço em si não precisa saber disso.

Estou lutando com o que angular.element nos fornece contra o que document.getElementById no mesmo elemento que o id nos fornece. por exemplo. Isso funciona:

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

Nenhuma delas faz:

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

Estou claramente fazendo algo que deve ser bastante óbvio errado! Alguém pode ajudar?

Supondo que eu possa fazer o trabalho acima, eu tenho um problema semelhante ao tentar substituir o acesso jQuery ao elemento: por exemplo, $(target).fadeIn('fast'); funciona angular.element('#appBusyIndicator').fadeIn('fast')ou angular.element('appBusyIndicator').fadeIn('fast')não

Alguém pode me indicar um bom exemplo de documentação que esclarece o uso de um "elemento" Angular versus o elemento DOM? Angular obviamente "envolve" o elemento com suas próprias propriedades, métodos, etc., mas geralmente é difícil obter o valor original. Por exemplo, se eu tenho um <input type='number'>campo e quero acessar o conteúdo original que é visível na interface do usuário quando o usuário digita "-" (sem as aspas), não recebo nada, provavelmente porque "type = number" significa que Angular está rejeitando a entrada, embora seja visível na interface do usuário e eu queira vê-la para que eu possa testá-la e limpá-la.

Quaisquer sugestões / respostas apreciadas.

Obrigado.

irasciano
fonte
5
angular.element é um objeto jQlite ou jQuery se você estiver carregando o jQuery.
21413 Neil

Respostas:

226

Ele pode funcionar assim:

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

Você envolve a chamada Document.querySelector()Javascript nativa na angular.element()chamada. Portanto, você sempre obtém o elemento em um objeto jqLite ou jQuery , dependendo de estar ou não jQuerydisponível / carregado.

Documentação oficial para angular.element:

Se jQuery estiver disponível, angular.elementé um alias para a jQueryfunção. Se o jQuery não estiver disponível, angular.elementdelegue para o subconjunto interno do AngularjQuery , chamado "jQuery lite" ou jqLite .

Todas as referências de elementos Angularsempre são agrupadas com jQuery ou jqLite(como o argumento do elemento em uma função de compilação ou link de diretivas). Eles nunca são DOMreferências brutas .

Caso você se pergunte por que usar document.querySelector(), leia esta resposta .

kaiser
fonte
41
Alguma razão para preferir document.querySelector ('# ...') a simplesmente usar document.getElementById ()?
Kato
4
Você também pode fazer isso em um elemento angular: var elDivParent = angular.element (elem [0] .querySelector ('# an-id'));; elem sendo um elemento angular. Dessa forma, você pode pesquisar dentro de um elemento. Útil para testes de unidade.
unludo
4
@Kato Mais informações nesta resposta . Espero que ajude.
Kaiser
trabalhando com o Google Maps API, e isso funcionou: var autocomplete = new google.maps.places.Autocomplete( $document[0].querySelector('#address'), { types: ['geocode'], componentRestrictions: {country: 'us'} } );. Obrigado pela dica @kaiser. Por alguma razão, a API do mapa reclamou que o que eu passei no primeiro parâmetro não foi uma entrada quando eu usei angular.element(document.querySelector('#address')), mas tudo está bem quando acaba bem.
Nymo
49

Você deve ler a documentação do elemento angular , se ainda não o fez, para entender o que é suportado pelo jqLite e o que não é -jqlite é um subconjunto de jquery incorporado ao angular.

Esses seletores não funcionarão apenas com o jqLite, pois os seletores por ID não são suportados.

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

Então, qualquer um:

  • você usa o jqLite sozinho, mais limitado que o jquery, mas o suficiente na maioria das situações.
  • ou você inclui a biblioteca completa do jQuery no seu aplicativo e a usa como jquery normal, nos locais em que você realmente precisa do jquery.

Edit: Observe que o jQuery deve ser carregado antes do angularJS para ter precedência sobre o jqLite:

O jQuery real sempre tem precedência sobre o jqLite, desde que ele tenha sido carregado antes do evento DOMContentLoaded ser disparado.

Edit2: Eu perdi a segunda parte da pergunta antes:

O problema com <input type="number">, acho que não é um problema angular, é o comportamento pretendido do elemento numérico html5 nativo .

Ele não retornará um valor não numérico, mesmo se você tentar recuperá-lo com o jquery .val()ou com o .valueatributo bruto .

garst
fonte
2
Temos a biblioteca jQuery completa - caso contrário, meu cenário $ explicado acima não funcionaria. Minha pergunta era por que o $ funciona, mas o angular.element não - mesmo que o jQuery completo esteja disponível.
irascian
1
Você está incluindo o jQuery antes ou depois do angular.js? Deve ser incluído antes do angular. Seletores de id fazer o trabalho com jQuery disponíveis: jsbin.com/ohumiy/1/edit
Garst
1
Eles estão trabalhando em outra diretiva (ou seja, angular.element para acessar algo por id). O problema parece ser o modo como esse controle funciona, anexando-se a esse elemento, embora eu não entenda por que o mesmo equivalente jQuery deve funcionar diretamente. Usando o jQuery diretamente, minha diretiva tem uma marca de fechamento - a marca de fechamento automático não funciona (sem erros, apenas uma tela em branco), o que me faz suspeitar do controle.
irascian
2
No meu segundo ponto, há também algo como view $ value, que pode ser usado para recuperar o conteúdo de um elemento, mas no caso de input type = number em que o usuário inseriu "-", este está vazio. Eu quero uma propriedade algo como $ rawInputValue no elemento para ver o conteúdo ANTES que o Angular entrou porque o "-" ainda está na interface do usuário, mesmo que eu não tenha acesso a ele programaticamente na minha diretiva.
irascian
27
var target = document.getElementById('appBusyIndicator');

é igual a

var target = $document[0].getElementById('appBusyIndicator');
Dasun
fonte
1
É melhor usar o segundo exemplo para garantir que suas dependências sejam explícitas e possam ser ridicularizadas, certo?
Lucas
deveria ser, mas acho que no angular 2+, contamos apenas com mais recursos de texto datilografado. Já não têm de se preocupar com essas coisas :)
Dasun
17

Eu não acho que é o caminho certo para usar angular. Se um método de estrutura não existir, não o crie! Isso significa que a estrutura (aqui angular) não funciona dessa maneira.

Com o angular, você não deve manipular o DOM dessa maneira (a maneira jquery), mas usar um auxiliar angular como

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

Ou crie sua própria diretiva (seu próprio componente DOM) para ter controle total sobre ela.

BTW, você pode ver aqui http://caniuse.com/#search=queryselector querySelector é bem suportado e, portanto, pode ser usado com segurança.

Thomas Decaux
fonte
2
Você está absolutamente correto. Mais de 90% dos casos que pensei em manipular manualmente o DOM, acabei refatorando o código para remover a necessidade e, no final, o código é muito mais limpo.
Stephen Chung
17

Se alguém usando gulp, ele mostra um erro se usarmos document.getElementById()e sugere usar, $document.getElementById()mas não funciona.

Usar -

$document[0].getElementById('id')
Kanchan
fonte
Por que angular está injetando uma matriz aqui?
Peter
16

Você pode acessar elementos usando $ document ($ document precisa ser injetado)

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

ou com elemento angular, os elementos especificados podem ser acessados ​​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
fonte
1
find () - Limitado a pesquisas por nome de tag
RJV 17/10
angular.element (document) .find ('. someClass'); funciona perfeitamente bem.
Nishant Baranwal
10

Você deve injetar $ document em seu controlador e usá-lo em vez do objeto de documento original.

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

Se você não precisa do wrapper do elemento do estilo jquery, $ document [0] .querySelector ('# MyID') fornecerá o objeto DOM.

Adamy
fonte
1
Você também pode referenciar window.document diretamente, em vez de usar a sobrecarga do serviço de documento Angulars $.
MatBee
5
Claro que você pode, se você não se preocupe com zombando $ documento em sua unittest
Adamy
Não está claro para mim: recebo este aviso: você deve usar o serviço de documento $ em vez do objeto de documento padrão, mas o que isso significa? Algum documento sobre isso? Obrigado!
realnot
@realnot, você pode acessar o objeto de documento HTML DOM de qualquer lugar no seu javascript. w3schools.com/jsref/dom_obj_document.asp
Adamy
6

Isso funcionou bem para mim.

angular.forEach(element.find('div'), function(node)
{
  if(node.id == 'someid'){
    //do something
  }
  if(node.className == 'someclass'){
    //do something
  }
});
susrut316
fonte
3
elemento não está definido? angular.element.find não é uma função para eu experimentar o seu sem o JQuery.
desembarcou
3
Por favor, não faça isso. Use um dos outros exemplos. Isso não usa otimizações de pesquisa de tabela de hash.
MatBee
4

Melhoria na resposta do kaiser:

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

Não se esqueça de injetar $documentna sua diretiva

Rob H
fonte
21
Como mencionado na documentação angular para element.find : find() - Limited to lookups by tag name, ele não funciona em ids. Além disso, você tem um fechamento extra ).
paaat
3

Talvez eu esteja atrasado aqui, mas isso funcionará:

var target = angular.element(appBusyIndicator);

Observe que não há appBusyIndicator, é um valor de ID simples.

O que está acontecendo nos bastidores: (supondo que seja aplicado em uma div) (extraído da linha angular.js no: 2769 em diante ...)

/////////////////////////////////////////////
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 padrão, se não houver jQuery na página, o jqLite será usado. O argumento é entendido internamente como um ID e o objeto jQuery correspondente é retornado.

Vishal Anand
fonte
Eu tentei isso agora com o Angular 1.5.8. Um ID simples retorna o resultado vazio. angular.element("#myId")funciona bem.
Gosha U.
talvez você tenha jQuery na sua página?
Vishal Anand