Estou procurando algumas diretrizes que podem ser usadas para ajudar a determinar qual tipo de escopo usar ao escrever uma nova diretiva. Idealmente, eu gostaria de algo semelhante a um fluxograma que me guie por várias perguntas e apareça a resposta correta - nenhum novo novo escopo, novo escopo filho ou novo escopo isolado - mas isso provavelmente está pedindo muito. Aqui está meu atual conjunto insignificante de diretrizes:
- Não use um escopo isolado se o elemento que usará a diretiva usar o ng-model.
Consulte Posso usar o ng-model com escopo isolado? e
Por que os formatadores não funcionam com escopo isolado? - Se a diretiva não modificar nenhuma propriedade de escopo / modelo, não crie um novo escopo
- Os escopos de isolamento parecem funcionar bem se a diretiva estiver encapsulando um conjunto de elementos DOM ( a documentação diz "uma estrutura DOM complexa") e a diretiva será usada como um elemento, ou sem outras diretivas no mesmo elemento.
Estou ciente de que o uso de uma diretiva com um escopo isolado em um elemento força todas as outras diretivas desse mesmo elemento a usar o mesmo (um) escopo isolado, portanto, isso não limita severamente quando um escopo isolado pode ser usado?
Espero que alguns membros da equipe da Angular-UI (ou outros que escreveram muitas diretivas) possam compartilhar suas experiências.
Por favor, não adicione uma resposta que simplesmente diga "use um escopo isolado para componentes reutilizáveis".
fonte
scope: true
criará um escopo filho usando$scope.new()
automaticamente.scope: false
(o padrão, sem novo escopo),scope: true
(novo escopo que herda prototipicamente) escope: { ... }
(novo escopo isolado).Respostas:
Que ótima pergunta! Eu amo ouvir o que os outros têm a dizer, mas aqui estão as diretrizes que eu uso.
A premissa de alta altitude: escopo é usada como a "cola" que usamos para nos comunicar entre o controlador pai, a diretiva e o modelo de diretiva.
Escopo pai:,
scope: false
portanto, nenhum novo escopoEu não uso isso com muita frequência, mas como o @MarkRajcok disse, se a diretiva não acessa nenhuma variável de escopo (e obviamente não define nenhuma!), Então isso é bom para mim. Isso também é útil para diretivas filho que são usadas apenas no contexto da diretiva pai (embora sempre haja exceções a isso) e que não tenham um modelo. Basicamente, qualquer coisa com um modelo não pertence ao compartilhamento de um escopo, porque você está expondo inerentemente esse escopo para acesso e manipulação (mas tenho certeza de que há exceções a essa regra).
Como exemplo, criei recentemente uma diretiva que desenha um gráfico vetorial (estático) usando uma biblioteca SVG que estou escrevendo. Ele possui
$observe
dois atributos (width
eheight
) e os utiliza em seus cálculos, mas não define nem lê nenhuma variável de escopo e não possui modelo. Este é um bom caso de uso para não criar outro escopo; nós não precisamos de um, então por que se preocupar?Porém, em outra diretiva SVG, eu exigi um conjunto de dados para usar e, adicionalmente, tive que armazenar um pouquinho de estado. Nesse caso, o uso do escopo pai seria irresponsável (novamente, de um modo geral). Então, ao invés ...
Escopo da criança:
scope: true
As diretivas com um escopo filho são sensíveis ao contexto e se destinam a interagir com o escopo atual.
Obviamente, uma vantagem importante disso em relação a um escopo isolado é que o usuário é livre para usar a interpolação em qualquer atributo que desejar; por exemplo, usar
class="item-type-{{item.type}}"
uma diretiva com um escopo isolado não funcionará por padrão, mas funciona bem em uma com um escopo filho, porque o que for interpolado ainda pode, por padrão, ser encontrado no escopo pai. Além disso, a própria diretiva pode avaliar com segurança atributos e expressões no contexto de seu próprio escopo sem se preocupar com poluição ou danos aos pais.Por exemplo, uma dica de ferramenta é algo que apenas é adicionado; um escopo isolado não funcionaria (por padrão, veja abaixo) porque é esperado que usemos outras diretivas ou atributos interpolados aqui. A dica de ferramenta é apenas um aprimoramento. Mas a dica de ferramenta também precisa definir algumas coisas no escopo para usar com uma sub-diretiva e / ou modelo e, obviamente, gerenciar seu próprio estado, portanto seria muito ruim usar o escopo pai. Nós estamos poluindo ou danificando, e nem é bueno.
Eu me pego usando escopos filhos com mais frequência do que isolados ou escopos parentais.
Isolar o escopo:
scope: {}
Isto é para componentes reutilizáveis. :-)
Mas, falando sério, penso em "componentes reutilizáveis" como "componentes independentes". A intenção é que eles sejam usados para uma finalidade específica, portanto, combiná-los com outras diretivas ou adicionar outros atributos interpolados ao nó DOM inerentemente não faz sentido.
Para ser mais específico, tudo o que é necessário para essa funcionalidade autônoma é fornecido por meio de atributos especificados avaliados no contexto do escopo pai; elas são cadeias de mão única ('@'), expressões de mão única ('&') ou ligações de variável de mão dupla ('=').
Em componentes independentes, não faz sentido precisar aplicar outras diretivas ou atributos nele, porque ele existe por si só. Seu estilo é regido por seu próprio modelo (se necessário) e pode ter o conteúdo apropriado transcluído (se necessário). É autônomo, portanto, colocamos em um escopo isolado também para dizer: "Não mexa com isso. Estou fornecendo uma API definida por esses poucos atributos".
Uma boa prática é excluir o máximo possível de itens baseados em modelos das funções de link e controlador de diretiva. Isso fornece outro ponto de configuração "semelhante à API": o usuário da diretiva pode simplesmente substituir o modelo! A funcionalidade permaneceu a mesma e sua API interna nunca foi tocada, mas podemos mexer com o estilo e a implementação do DOM o quanto for necessário. ui / bootstrap é um ótimo exemplo de como fazer isso bem, porque Peter e Pawel são incríveis.
Os escopos de isolamento também são ótimos para uso com transclusão. Tome guias; eles não são apenas toda a funcionalidade, mas tudo o que está dentro dela pode ser avaliado livremente no escopo pai, deixando as guias (e os painéis) para fazer o que quiserem. As guias claramente têm seu próprio estado , que pertence ao escopo (para interagir com o modelo), mas esse estado não tem nada a ver com o contexto em que foi usado - é inteiramente interno ao que faz de uma diretiva de guia uma diretiva de guia. Além disso, não faz muito sentido usar outras diretivas com as guias. São guias - e já temos essa funcionalidade!
Envolva-o com mais funcionalidade ou transclua mais funcionalidade, mas a diretiva é o que já é.
Dito isso, devo observar que existem maneiras de contornar algumas das limitações (ou seja, recursos) de um escopo isolado, como o @ProLoser sugeriu em sua resposta. Por exemplo, na seção escopo filho, mencionei a interpolação em atributos não-diretivos que quebram ao usar um escopo isolado (por padrão). Mas o usuário poderia, por exemplo, simplesmente usar
class="item-type-{{$parent.item.type}}"
e funcionaria mais uma vez. Portanto, se houver um motivo convincente para usar um escopo isolado em relação a um escopo filho, mas você estiver preocupado com algumas dessas limitações, saiba que pode contornar praticamente todas elas, se necessário.Resumo
Diretivas sem novo escopo são somente leitura; eles são totalmente confiáveis (ou seja, internos ao aplicativo) e não tocam no conector. Diretivas com escopo filho adicionam funcionalidade, mas elas não são a única funcionalidade. Por fim, os escopos isolados são para diretivas que são o objetivo inteiro; eles são independentes, então não há problema (e é mais "correto") deixá-los desonestos.
Eu queria expressar meus pensamentos iniciais, mas, ao pensar em mais coisas, atualizo isso. Mas caramba - isso é tempo para uma resposta SO ...
PS: Totalmente tangencial, mas como estamos falando de escopos, prefiro dizer "prototípico", enquanto outros preferem "prototípico", o que parece ser mais preciso, mas não sai nada bem da língua. :-)
fonte
ngInclude
. Ou faça isso como parte de sua compilação. Muitas opções!Minha política e experiência pessoal:
Isolado: uma caixa de areia privada
Quero criar muitos métodos e variáveis de escopo que são SOMENTE usados pela minha diretiva e nunca são vistos ou acessados diretamente pelo usuário. Quero colocar na lista branca quais dados do escopo estão disponíveis para mim. Eu posso usar a transclusão para permitir que o usuário volte ao escopo pai (não afetado) . Eu não quero que meus variáveis e métodos acessíveis em crianças transcluídas.
Filho: uma subseção de conteúdo
Eu quero criar métodos e variáveis de escopo que PODEM ser acessados pelo usuário, mas não são relevantes para os escopos circundantes (irmãos e pais) fora do contexto da minha diretiva. Eu também gostaria de permitir que TODOS os dados do escopo pai fiquem transparentes.
Nenhuma: diretivas simples, somente leitura
Eu realmente não preciso mexer com métodos ou variáveis de escopo. Provavelmente estou fazendo algo que não tem a ver com escopos (como exibir plugins jQuery simples, validação etc.).
Notas
ng-model=$parent.myVal
(criança) oungModel: '='
(isolado).require: '^ngModel'
para procurar nos elementos-pai.fonte
Depois de escrever muitas diretivas, decidi usar menos
isolated
escopo. Embora seja legal e você encapsule os dados e não vaze dados para o escopo pai, isso limita severamente a quantidade de diretivas que você pode usar em conjunto. Assim,Se a diretiva que você vai escrever vai se comportar inteiramente por conta própria e você não está indo para compartilhá-lo com outras directivas, ir para escopo isolado . (como um componente, você pode simplesmente conectá-lo, sem muita personalização para o desenvolvedor final) (fica muito mais complicado quando você tenta escrever subelementos que possuem diretivas)
Se a diretiva que você escreverá fará apenas manipulações dom que não precisam de um estado interno de escopo ou alterações explícitas de escopo (principalmente coisas muito simples); vá para nenhum novo escopo . (tais como
ngShow
,ngMouseHover
,ngClick
,ngRepeat
)Se a diretiva que você vai escrever precisar alterar alguns elementos no escopo pai, mas também precisar lidar com algum estado interno, vá para o novo escopo filho . (como
ngController
)Não deixe de conferir o código-fonte para obter diretrizes: https://github.com/angular/angular.js/tree/master/src/ng/directive
Isso ajuda muito em como pensar sobre elas
fonte
require
, mantendo assim suas diretrizes ainda dissociadas. Então, como isso limita as possibilidades? Torna ainda mais diretivas mais específicas (então declare do que você depende). Portanto, deixaria apenas uma regra: se sua diretiva tiver estado ou precisar de alguns dados do escopo em que é usado - use o escopo isolado. Caso contrário, não use o escopo. E sobre "escopos infantis" - também escrevi muitas diretivas e nunca precisei desse recurso. Se "precisar alterar alguns elementos no escopo pai" - use as ligações.$parent
hack sujo ). Então, na verdade, "escopos filho" para diretivas é algo que parece que deve ser usado bastante na retaguarda - comongRepeat
isso cria novos escopos filhos para cada item repetir (mas também cria usandoscope.$new();
e não)scope: true
.ngClick
etc.) A exigência cria um tipo de dissociação, eu concordo, mas você ainda precisa conhecer a diretiva pai. A menos que seja como um componente , sou contra o isolamento. As diretivas (pelo menos, a maioria delas) devem ser altamente reutilizáveis e o isolamento quebra isso.Apenas pensei em adicionar meu entendimento atual e como ele se relaciona com outros conceitos de JS.
Padrão (por exemplo, não declarado ou escopo: false)
Isso é filosoficamente equivalente ao uso de variáveis globais. Sua diretiva pode acessar tudo no controlador pai, mas também está afetando-os e sendo afetados ao mesmo tempo.
escopo:{}
É como um módulo, qualquer coisa que ele queira usar precisa ser passada explicitamente. Se TODAS as diretivas usadas forem um escopo isolado, pode ser o equivalente a criar TODAS as limas JS que você escreve seu próprio módulo com muita sobrecarga ao injetar todas as dependências.
escopo: filho
Este é o meio termo entre variáveis globais e passagem explícita. É semelhante à cadeia de protótipos do javascript e apenas estende uma cópia do escopo pai. Se você criar um escopo isolado e transmitir todos os atributos e funções do escopo pai, ele será funcionalmente equivalente a isso.
A chave é que QUALQUER diretiva pode ser escrita de qualquer maneira. As diferentes declarações de escopo estão aqui apenas para ajudá-lo a organizar. Você pode transformar tudo em um módulo ou apenas usar todas as variáveis globais e ter muito cuidado. Para facilitar a manutenção, é preferível modularizar sua lógica em partes logicamente coerentes. Existe um equilíbrio entre um campo aberto e uma prisão fechada. A razão pela qual isso é complicado, acredito, é que, quando as pessoas aprendem sobre isso, pensam que estão aprendendo sobre como as diretivas funcionam, mas na verdade estão aprendendo sobre organização de código / lógica.
Outra coisa que me ajudou a descobrir como as diretivas funcionam é aprender sobre o ngInclude. O ngInclude ajuda a incluir parciais html. Quando comecei a usar diretivas, descobri que você poderia usar a opção de modelo para reduzir seu código, mas eu realmente não estava anexando nenhuma lógica.
É claro que entre as diretrizes da angular e o trabalho da equipe da angular-ui , ainda não tive que criar minha própria diretiva que faça algo substancial, de modo que minha visão sobre isso pode estar completamente errada.
fonte
Eu concordo com Umur. Em teoria, os escopos isolados soam maravilhosos e "portáteis", mas ao criar meu aplicativo para envolver funcionalidades não triviais, deparei-me com a necessidade de incorporar várias diretivas (algumas aninhadas dentro de outras ou adicionando atributos a elas) para poder escrever totalmente em meu próprio HTML, que é o objetivo de uma linguagem específica de domínio.
No final, é muito estranho ter que passar todos os valores globais ou compartilhados da cadeia com vários atributos em cada chamada de DOM de uma diretiva (conforme é necessário no escopo isolado). Parece estúpido escrever repetidamente tudo isso no DOM e parece ineficiente, mesmo que sejam objetos compartilhados. Também complica desnecessariamente as declarações da diretiva. A solução alternativa do uso de $ parent para "alcançar" e capturar valores da diretiva HTML parece muito ruim.
Eu também acabei alterando meu aplicativo para ter principalmente diretivas de escopo filho com muito poucos isolados - apenas aqueles que não precisam acessar QUALQUER COISA do pai, além do que podem ser passados por atributos simples e não repetitivos.
Tendo sonhado o sonho de idiomas específicos de domínio por décadas antes de existir algo assim, fico feliz que o AngularJS ofereça essa opção e sei que, à medida que mais desenvolvedores trabalharem nessa área, veremos aplicativos muito interessantes que também é fácil para seus arquitetos escrever, expandir e depurar.
- D
fonte