Eu tenho uma diretiva com um escopo isolado (para que eu possa reutilizar a diretiva em outros lugares) e, quando uso essa diretiva com um ng-repeat
, ela não funciona.
Eu li toda a documentação e as respostas do Stack Overflow sobre este tópico e compreendo os problemas. Eu acredito que evitei todas as pegadinhas usuais.
Portanto, eu entendo que meu código falha devido ao escopo criado pela ng-repeat
diretiva. Minha própria diretiva cria um escopo isolado e faz uma vinculação de dados bidirecional a um objeto no escopo pai. Minha diretiva atribuirá um novo valor de objeto a esta variável associada e isso funciona perfeitamente quando minha diretiva é usada sem ng-repeat
(a variável pai é atualizada corretamente). No entanto, com ng-repeat
, a atribuição cria uma nova variável no ng-repeat
escopo e a variável pai não vê a mudança. Tudo isso é o esperado com base no que li.
Também li que, quando há várias diretivas em um determinado elemento, apenas um escopo é criado. E que um priority
pode ser definido em cada diretiva para definir a ordem em que as diretivas são aplicadas; as diretivas são classificadas por prioridade e, em seguida, suas funções de compilação são chamadas (procure a palavra prioridade em http://docs.angularjs.org/guide/directive ).
Então, eu esperava poder usar a prioridade para garantir que minha diretiva seja executada primeiro e acabe criando um escopo isolate e, quando for ng-repeat
executada, reutilize o escopo isolate em vez de criar um escopo que herda prototipicamente do escopo pai. A ng-repeat
documentação afirma que essa diretiva é executada em nível de prioridade 1000
. Não está claro se 1
é um nível de prioridade mais alto ou um nível de prioridade mais baixo. Quando usei nível de prioridade 1
na minha diretiva, não fez diferença, então tentei 2000
. Mas isso torna as coisas piores: minhas ligações bidirecionais se tornam undefined
e minha diretriz não exibe nada.
Eu criei um violino para mostrar meu problema . Eu comentei a priority
configuração em minha diretiva. Eu tenho uma lista de objetos de nome e uma diretiva chamada name-row
que mostra os campos de nome e sobrenome no objeto de nome. Quando um nome exibido é clicado, eu quero definir uma selected
variável no escopo principal. A matriz de nomes e a selected
variável são passadas para a name-row
diretiva usando vinculação de dados bidirecional.
Eu sei como fazer isso funcionar chamando funções no escopo principal. Também sei que se selected
estiver dentro de outro objeto e me vincular ao objeto externo, as coisas funcionarão. Mas não estou interessado nessas soluções no momento.
Em vez disso, as perguntas que tenho são:
- Como evito a
ng-repeat
criação de um escopo que herda prototipicamente do escopo pai e, em vez disso, faço com que ele use o escopo isolate da minha diretiva? - Por que o nível de prioridade
2000
na minha diretiva não está funcionando? - Usando o Batarang, é possível saber que tipo de escopo está em uso?
scope: true
para sua diretiva. Veja também (se ainda não o fez) stackoverflow.com/questions/14914213/… Além disso, só porque uma diretiva será usada em vários lugares não significa que devemos usar automaticamente um escopo isolado.ng-repeat
. Eu acho que é valioso poder misturar diretivas autônomas comng-repeat
. Continua ...ng-repeat
não deveria haver um escopo.ng-repeat
ter um escopo faz sentido para o caso de uso típico, portanto, não estou sugerindo que ele seja alterado. Em vez disso, como comentei na resposta de Alex Osborn, acho que vou criar uma diretiva de repetição baseada emng-repeat
que não cria seu próprio escopo. Isso pode ser usado para repetir diretivas que têm seus próprios escopos isolados. A ser continuado ...ng-repeat
ou a diretiva de repetição sem escopo personalizada. Acho que está tudo bem para o "chamador" saber disso, mas não é bom para um "receptor" (a diretiva sendo repetida) saber se ela está sendo repetida ou não. A ser continuado ...Respostas:
Ok, através de muitos dos comentários acima, descobri a confusão. Primeiro, alguns pontos de esclarecimento:
Aqui está um exemplo do mesmo código, mas com a diretiva removida:
Aqui está um JSFiddle demonstrando que não funcionará. Você obtém exatamente os mesmos resultados de sua diretiva.
Por que não funciona? Porque os escopos no AngularJS usam herança prototípica. O valor
selected
em seu escopo pai é um primitivo . Em JavaScript, isso significa que será sobrescrito quando um filho definir o mesmo valor. Há uma regra de ouro nos escopos AngularJS: os valores do modelo sempre devem ter um.
neles. Ou seja, eles nunca devem ser primitivos. Veja esta resposta do SO para mais informações.Aqui está uma imagem da aparência inicial dos osciloscópios.
Depois de clicar no primeiro item, os escopos agora ficam assim:
Observe que uma nova
selected
propriedade foi criada no escopo ngRepeat. O escopo do controlador 003 não foi alterado.Você provavelmente pode adivinhar o que acontece quando clicamos no segundo item:
Portanto, o seu problema não é realmente causado pelo ngRepeat - é causado pela quebra de uma regra de ouro no AngularJS. A maneira de corrigir isso é simplesmente usar uma propriedade de objeto:
Aqui está um segundo JSFiddle mostrando que isso também funciona.
Aqui está a aparência inicial dos escopos:
Depois de clicar no primeiro item:
Aqui, o escopo do controlador está sendo afetado, conforme desejado.
Além disso, para provar que isso ainda funcionará com sua diretiva com um escopo isolado (porque, novamente, isso não tem nada a ver com o seu problema), aqui está um JSFiddle para isso também, a visualização deve refletir o objeto. Você notará que a única mudança necessária foi usar um objeto em vez de um primitivo .
Escopos inicialmente:
Escopos depois de clicar no primeiro item:
Para concluir: mais uma vez, seu problema não é com o escopo isolado e não é como o ngRepeat funciona. O seu problema é que você está quebrando uma regra que é conhecida por causar esse mesmo problema. Os modelos no AngularJS devem sempre ter um
.
.fonte
.
regra de ouro só é necessária para escopos que têm herança prototípica. Com isolate-scopes, as variáveis nas quais você está interessado são definidas nascope: {}
definição da diretiva e, portanto, essas variáveis existirão no isolate-scope desde o início. Além disso, eles não mascaram as variáveis pai porque o escopo isolate não herda prototipicamente do escopo pai. ou seja, não há escopo "pai" para mascarar..
.Sem tentar evitar diretamente as suas perguntas, em vez disso, dê uma olhada no seguinte violino:
http://jsfiddle.net/dVPLM/
O ponto principal é que em vez de tentar lutar e mudar o comportamento convencional do Angular, você pode estruturar sua diretiva para trabalhar
ng-repeat
em vez de tentar substituí-la.Em seu modelo:
Em sua diretriz:
Em resposta às suas perguntas:
ng-repeat
irá criar um escopo, você realmente não deveria tentar mudar isso.fonte
ng-repeat
ou não. Talvez quando é escrito pela primeira vez, nunca seja usado comng-repeat
. E em algum momento no futuro, ele pode se acostumarng-repeat
e, nesse ponto, deve funcionar sem reescrever. Esperançosamente, uma versão futura do AngularJS tornará isso possível.ng-repeat
ou não. Mas parece que eu teria que passar uma função para a diretiva para que ela possa modificar as variáveis no escopo pai, em vez de ser capaz de modificar uma vinculação bidirecional no próprio escopo da diretiva. Encadernação bidirecional e diretivas reutilizáveis são minhas duas coisas favoritas no AngularJS eng-repeat
está provando ser uma mosca na sopa. Talvez eu possa escrever umng-repeat
equivalente que não crie seu próprio escopo.