Diretiva isolar escopo com escopo de repetição de ng em AngularJS

Eu tenho uma diretiva com um escopo de isolamento (para que eu possa reutilizar a diretiva em outros locais), e quando eu uso essa diretiva com um ng-repeat , ele não funciona.

Eu li todas as documentações e respostas do Stack Overflow sobre este tópico e entendi os problemas. Eu acredito que evitei todos os truques usuais.

Então eu entendo que meu código falha por causa do escopo criado pela diretiva ng-repeat . Minha própria diretiva cria um isolate-scope e faz uma vinculação de dados bidirecional para um object no escopo pai. Minha diretiva irá atribuir um novo valor de object a essa variável vinculada 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 escopo ng-repeat e a variável pai não vê a mudança. Tudo isso é como esperado com base no que li.

Eu também li que quando há várias diretivas em um determinado elemento, apenas um escopo é criado. E que uma priority pode ser definida em cada diretiva para definir a ordem na qual as diretivas são aplicadas; as diretivas são classificadas por prioridade e, em seguida, suas funções de compilation são chamadas (pesquise a prioridade da palavra em http://docs.angularjs.org/guide/directive ).

Por isso, esperava poder usar prioridade para garantir que minha diretiva fosse executada primeiro e acabasse criando um escopo isolado, e quando execuções ng-repeat , ela reutilizava o escopo de isolamento, em vez de criar um escopo que herda de forma prototípica do escopo isolado. escopo pai. A documentação do ng-repeat indica que essa diretiva é executada no 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 o nível de prioridade 1 na minha diretiva, isso não fez diferença, então tentei 2000 . Mas isso torna as coisas piores: minhas ligações bidirecionais se tornam undefined e minha diretiva não exibe nada.

Eu criei um violino para mostrar o meu problema . Comentei a definição de priority na minha diretiva. Eu tenho uma lista de objects de nome e uma diretiva chamada name-row que mostra os campos de nome e sobrenome no object de nome. Quando um nome exibido é clicado, quero que ele defina uma variável selected no escopo principal. A matriz de nomes, a variável selected , é passada para a diretiva de name-row usando a vinculação de dados bidirecional.

Eu sei como fazer isso funcionar chamando funções no escopo principal. Eu também sei que se selected está dentro de outro object, e eu ligar ao object externo, as coisas funcionariam. Mas não estou interessado nessas soluções no momento.

Em vez disso, as perguntas que tenho são:

  • Como evitar que o ng-repeat crie um escopo que herda protótipo do escopo pai e, em vez disso, ele use o isolate-scope 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?

Ok, através de muitos comentários acima, descobri a confusão. Primeiro, alguns pontos de esclarecimento:

  • ngRepeat não afeta seu escopo isolado escolhido
  • os parâmetros passados ​​para o ngRepeat para uso nos atributos da sua diretiva usam um escopo herdado de forma prototípica
  • a razão pela qual sua diretiva não funciona não tem nada a ver com o escopo isolado

Aqui está um exemplo do mesmo código, mas com a diretiva removida:

 
  • {{$index}}: {{name.first}} {{name.last}}
  • Aqui está um JSFiddle demonstrando que não funcionará. Você obtém exatamente os mesmos resultados da sua diretiva.

    Por que isso não funciona? Porque os escopos no AngularJS usam inheritance prototípica. O valor selected em seu escopo pai é primitivo . Em JavaScript, isso significa que ele será sobrescrito quando um filho definir o mesmo valor. Há uma regra de ouro nos escopos AngularJS: os valores do modelo devem sempre ter um . neles. Ou seja, eles nunca deveriam ser primitivos. Veja esta resposta SO para mais informações.


    Aqui está uma imagem de como os escopos se parecem inicialmente.

    insira a descrição da imagem aqui

    Depois de clicar no primeiro item, os escopos agora se parecem com isso:

    insira a descrição da imagem aqui

    Observe que uma nova propriedade selected 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:

    insira a descrição da imagem aqui


    Então, seu problema não é causado pelo ngRepeat – é causado pela quebra de uma regra de ouro no AngularJS. A maneira de consertá-lo é simplesmente usar uma propriedade de object:

     $scope.state = { selected: undefined }; 
     
  • {{$index}}: {{name.first}} {{name.last}}
  • Aqui está um segundo JSFiddle mostrando que isso funciona também.

    Aqui está o que os escopos parecem inicialmente:

    insira a descrição da imagem aqui

    Depois de clicar no primeiro item:

    insira a descrição da imagem aqui

    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 seu problema), aqui está um JSFiddle para isso também, a visualização deve refletir o object. Você notará que a única mudança necessária foi usar um object em vez de um primitivo .

    Escopos inicialmente:

    insira a descrição da imagem aqui

    Escopos depois de clicar no primeiro item:

    insira a descrição da imagem aqui

    Para concluir: mais uma vez, seu problema não é com o escopo isolado e não é com o funcionamento do ngRepeat. Seu problema é que você está quebrando uma regra que é conhecida por levar a esse mesmo problema. Modelos no AngularJS devem sempre ter um . .

    Sem diretamente tentar evitar responder 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ê poderia estruturar sua diretiva para trabalhar com ng-repeat em vez de tentar substituí-la.

    Em seu modelo:

        

    Na sua diretiva:

      template: ' ' 

    Em resposta às suas perguntas:

    • ng-repeat irá criar um escopo, você realmente não deveria estar tentando mudar isso.
    • Prioridade nas diretivas não é apenas ordem de execução – veja: AngularJS: Como o compilador HTML organiza a ordem de compilation?
    • Em Batarang, se você marcar a guia de desempenho, poderá ver as expressões associadas a cada escopo e verificar se isso corresponde às suas expectativas.