Al escribir una directiva en AngularJS, ¿cómo decido si no necesito un nuevo alcance, un nuevo alcance secundario o un nuevo alcance aislado?

265

Estoy buscando algunas pautas que uno pueda usar para ayudar a determinar qué tipo de alcance usar al escribir una nueva directiva. Idealmente, me gustaría algo similar a un diagrama de flujo que me guíe a través de un montón de preguntas y muestre la respuesta correcta: no hay un nuevo alcance nuevo, un nuevo alcance secundario o un nuevo alcance aislado, pero es probable que eso sea pedir demasiado. Aquí está mi conjunto actual de pautas insignificantes:

Soy consciente de que el uso de una directiva con un alcance aislado en un elemento obliga a todas las demás directivas en ese mismo elemento a usar el mismo (un) alcance aislado, entonces, ¿no limita esto severamente cuando se puede usar un alcance aislado?

Espero que algunos del equipo de Angular-UI (u otros que hayan escrito muchas directivas) puedan compartir sus experiencias.

No agregue una respuesta que simplemente diga "use un alcance aislado para componentes reutilizables".

Mark Rajcok
fuente
por "ámbito secundario" quiere decir crear ámbito en la función de enlace por "ámbito. $ new ()"? Como lo sé, la directiva puede tener un alcance aislado o no tenerlo (por lo que
usaré el
3
La configuración @ValentynShybanov scope: truecreará un ámbito secundario utilizando $scope.new()automáticamente.
Josh David Miller
2
@Valentyn, lo que dijo Josh: entonces, las tres posibilidades son scope: false(el valor predeterminado, sin nuevo alcance), scope: true(nuevo alcance que hereda prototípicamente) y scope: { ... }(nuevo alcance aislado).
Mark Rajcok
1
Sí, gracias. He echado de menos esa diferencia entre "verdadero" y "{}". Bueno saber.
Valentyn Shybanov
Hay un cuarto caso que las personas generalmente tienden a ignorar ... ese es el "controlador directivo" ... Creo que la pregunta debería ampliarse para incluirlos también ... +1 a la pregunta ...
ganaraj

Respuestas:

291

¡Qué gran pregunta! Me encantaría escuchar lo que otros tienen que decir, pero aquí están las pautas que uso.

La premisa de gran altitud: el alcance se usa como el "pegamento" que usamos para comunicarnos entre el controlador principal, la directiva y la plantilla de la directiva.

Alcance principal: scope: false por lo tanto, no hay ningún alcance nuevo

No uso esto muy a menudo, pero como dijo @MarkRajcok, si la directiva no accede a ninguna variable de alcance (¡y obviamente no establece ninguna!), Entonces esto está bien en lo que a mí respecta. Esto también es útil para las directivas secundarias que solo se usan en el contexto de la directiva principal (aunque siempre hay excepciones a esto) y que no tienen una plantilla. Básicamente, cualquier cosa con una plantilla no pertenece compartiendo un alcance, porque expones inherentemente ese alcance para acceso y manipulación (pero estoy seguro de que hay excepciones a esta regla).

Como ejemplo, recientemente creé una directiva que dibuja un gráfico vectorial (estático) usando una biblioteca SVG que estoy en proceso de escribir. Tiene $observedos atributos ( widthy height) y los usa en sus cálculos, pero no establece ni lee ninguna variable de alcance y no tiene plantilla. Este es un buen caso de uso para no crear otro ámbito; no necesitamos uno, entonces ¿por qué molestarse?

Pero en otra directiva SVG, sin embargo, requería un conjunto de datos para usar y además tenía que almacenar un poco de estado. En este caso, usar el ámbito primario sería irresponsable (nuevamente, en términos generales). Así que en vez...

Alcance del niño: scope: true

Las directivas con un ámbito secundario tienen en cuenta el contexto y están destinadas a interactuar con el ámbito actual.

Obviamente, una ventaja clave de esto sobre un alcance aislado es que el usuario es libre de usar la interpolación en cualquier atributo que desee; por ejemplo, el uso class="item-type-{{item.type}}"de una directiva con un ámbito aislado no funcionará de forma predeterminada, pero funciona bien en uno con un ámbito secundario porque lo que sea que se interpola puede encontrarse de forma predeterminada en el ámbito primario. Además, la propia directiva puede evaluar con seguridad los atributos y expresiones en el contexto de su propio alcance sin preocuparse por la contaminación o el daño a los padres.

Por ejemplo, una información sobre herramientas es algo que simplemente se agrega; un alcance aislado no funcionaría (por defecto, ver más abajo) porque se espera que usemos otras directivas o atributos interpolados aquí. La información sobre herramientas es solo una mejora. Pero la información sobre herramientas también necesita establecer algunas cosas en el alcance para usar con una sub-directiva y / o plantilla y obviamente para administrar su propio estado, por lo que sería bastante malo usar el alcance principal. Lo estamos contaminando o dañándolo, y ninguno de los dos es bueno.

Me encuentro usando ámbitos secundarios con más frecuencia que los ámbitos aislados o primarios.

Alcance de aislamiento: scope: {}

Esto es para componentes reutilizables. :-)

Pero en serio, pienso en los "componentes reutilizables" como "componentes autónomos". La intención es que se usen para un propósito específico, por lo que combinarlos con otras directivas o agregar otros atributos interpolados al nodo DOM inherentemente no tiene sentido.

Para ser más específicos, todo lo necesario para esta funcionalidad independiente se proporciona a través de atributos específicos evaluados en el contexto del ámbito principal; son cadenas unidireccionales ('@'), expresiones unidireccionales ('&') o enlaces de variables bidireccionales ('=').

En componentes autocontenidos, no tiene sentido aplicar otras directivas o atributos porque existe por sí mismo. Su estilo se rige por su propia plantilla (si es necesario) y puede tener el contenido apropiado translúcido (si es necesario). Es independiente, por lo que lo ponemos en un ámbito aislado también para decir: "No te metas con esto. Te estoy dando una API definida a través de estos pocos atributos".

Una buena práctica es excluir la mayor cantidad posible de elementos basados ​​en plantillas del enlace directivo y las funciones del controlador. Esto proporciona otro punto de configuración "similar a la API": ¡el usuario de la directiva puede simplemente reemplazar la plantilla! La funcionalidad permaneció igual, y su API interna nunca se tocó, pero podemos meternos con el estilo y la implementación DOM tanto como sea necesario. ui / bootstrap es un gran ejemplo de cómo hacerlo bien porque Peter y Pawel son increíbles.

Los ámbitos de aislamiento también son excelentes para usar con transclusión. Tomar pestañas; no son solo la funcionalidad completa, sino que lo que sea que esté dentro de él se puede evaluar libremente desde el ámbito principal, dejando las pestañas (y paneles) para hacer lo que quieran. Las pestañas claramente tienen su propio estado , que pertenece al alcance (para interactuar con la plantilla), pero ese estado no tiene nada que ver con el contexto en el que se utilizó: es completamente interno a lo que hace que una directiva de pestañas sea una directiva de pestañas. Además, no tiene mucho sentido usar otras directivas con las pestañas. Son pestañas, ¡y ya tenemos esa funcionalidad!

Rodéelo con más funcionalidad o transcluya más funcionalidad, pero la directiva es lo que ya es.

Dicho todo esto, debo tener en cuenta que existen algunas formas de evitar algunas de las limitaciones (es decir, las características) de un ámbito de aislamiento, como insinuó @ProLoser en su respuesta. Por ejemplo, en la sección de alcance secundario, mencioné la interpolación en la ruptura de atributos no directivos cuando se usa un alcance de aislamiento (por defecto). Pero el usuario podría, por ejemplo, simplemente usar class="item-type-{{$parent.item.type}}"y volvería a funcionar. Entonces, si hay una razón convincente para usar un alcance aislado sobre un alcance secundario pero le preocupan algunas de estas limitaciones, sepa que puede solucionarlas prácticamente todas si lo necesita.

Resumen

Las directivas sin alcance nuevo son de solo lectura; son completamente confiables (es decir, internos de la aplicación) y no tocan el conector. Las directivas con un ámbito secundario agregan funcionalidad, pero no son la única funcionalidad. Por último, los ámbitos de aislamiento son para directivas que son el objetivo completo; son independientes, por lo que está bien (y la mayoría es "correcto") dejar que se vuelvan rebeldes.

Quería expresar mis pensamientos iniciales, pero a medida que pienso en más cosas, actualizaré esto. Pero mierda, esto es largo para una respuesta TAN ...


PD: Totalmente tangencial, pero como estamos hablando de ámbitos, prefiero decir "prototípico", mientras que otros prefieren "prototípico", que parece ser más preciso, pero no sale del todo bien. :-)

Josh David Miller
fuente
Gracias Josh, gran respuesta. Quería / esperaba respuestas largas para esto. Dos cosas que no seguí: 1) ámbito secundario: "el usuario puede usar la interpolación en cualquier atributo que desee". 2) aislar el alcance: "o no todos, en el caso de '?'" ¿Puedes explicar un poco más sobre eso? (Siéntase libre de editar su publicación en lugar de escribir comentarios si eso es más fácil.)
Mark Rajcok
@MarkRajcok Para (1), lo cambié para que sea un poco menos nebuloso; avíseme si no tuve éxito. Para (2), esa fue una combinación de un error tipográfico y una redacción deficiente; Reescribí ese párrafo para que sea más claro. También agregué un ejemplo adicional o dos, aclaré algunas cosas más y arreglé algunos errores tipográficos.
Josh David Miller
Como se menciona en la respuesta, bootstrap para angular es un gran ejemplo de combinación de estos. El ejemplo de acordeón me pareció particularmente útil - GitHub - Accordion
CalM
Mencionaste que usas más los ámbitos infantiles, pensé que el patrón reutilizable de las directivas era el más común y he evitado escribir directivas que solo debían usarse una vez. ¿Es esto innecesario? A veces, cuando mi HTML se vuelve demasiado grande, tengo ganas de mover esa sección a una directiva, pero solo se usará una vez, así que solo la dejo en el html.
user2483724
2
@ user2483724 Un error muy común es que las directivas "reutilizables" son aquellas que usan un ámbito aislado; no tan. Si nos fijamos en las directivas preempaquetadas, casi ninguna de ellas utiliza ámbitos de aislamiento, algunos ni siquiera un ámbito secundario, ¡pero les aseguro que son reutilizables! La regla debe estar en cómo se usa el alcance dentro de la directiva. Si solo se trata de ahorrar espacio en un archivo, no estoy seguro de que una directiva sea el mejor enfoque. Aumenta el tiempo de procesamiento por el bien del desarrollador. Pero si debe hacerlo, entonces hágalo. O usa un ngInclude. O hazlo como parte de tu construcción. ¡Muchas opciones!
Josh David Miller
52

Mi política personal y experiencia:

Aislado: un cajón de arena privado

Quiero crear una gran cantidad de métodos y variables de alcance que SOLO use mi directiva y que el usuario nunca vea o acceda directamente. Quiero incluir en la lista blanca qué datos de alcance están disponibles para mí. Puedo usar la transclusión para permitir que el usuario regrese al ámbito primario (no afectado) . Yo no quiero que mis variables y métodos accesibles en niños transcluídas.

Niño: una subsección de contenido

Quiero crear métodos y variables de alcance a los que PUEDE acceder el usuario, pero que no son relevantes para los ámbitos circundantes (hermanos y padres) fuera del contexto de mi directiva. También me gustaría permitir que TODOS los datos del ámbito primario se filtren de manera transparente.

Ninguna: directivas simples de solo lectura

Realmente no necesito meterme con métodos o variables de alcance. Probablemente estoy haciendo algo que no tiene que ver con ámbitos (como mostrar complementos jQuery simples, validación, etc.).

Notas

  • No debe permitir que ngModel u otras cosas afecten directamente su decisión. Puede eludir comportamientos extraños haciendo cosas como ng-model=$parent.myVal(niño) o ngModel: '='(aislado).
  • Isolate + transclude restaurará todo el comportamiento normal a las directivas entre hermanos y regresa al ámbito principal, así que tampoco permita que eso afecte su juicio.
  • No te metas con el alcance en ninguno porque es como poner datos en el alcance para la mitad inferior del DOM pero no para la mitad superior, lo que tiene sentido 0.
  • Preste atención a las prioridades de la directiva (no tenga ejemplos concretos de cómo esto puede afectar las cosas)
  • Inyecte servicios o use controladores para comunicarse entre directivas con cualquier tipo de ámbito. También puede hacer require: '^ngModel'para buscar en los elementos principales.
ProLoser
fuente
1
Puede que haya entendido mal esta parte: "Aislar + transcluir restaurará todo el comportamiento normal a las directivas entre hermanos". Ver este saqueador . Tendrás que mirar en la consola.
Josh David Miller
1
Gracias ProLoser por sus ideas / respuesta. Eres una de las personas que esperaba ver esta publicación si agrego la etiqueta angularjs-ui.
Mark Rajcok
@JoshDavidMiller cuando se habla de directivas sobre el mismo elemento DOM, las cosas se vuelven más complicadas y, en su lugar, debe comenzar a mirar la propiedad prioritaria. La transclusión es más relevante para los contenidos infantiles.
ProLoser
1
@ProLoser Correcto, pero no estoy seguro de lo que quisiste decir con esa declaración. Obviamente afectan a los niños, pero ¿cómo afectan los alcances de las directivas a sus directivas de hermanos?
Josh David Miller
18

Después de escribir muchas directivas, he decidido usar menos isolatedalcance. Aunque es genial y encapsula los datos y se asegura de no filtrar datos al ámbito principal, limita severamente la cantidad de directivas que pueden usar juntas. Entonces,

Si la directiva que va a escribir va a comportarse completamente por sí sola y no la va a compartir con otras directivas, busque un alcance aislado . (como un componente, simplemente puede enchufarlo, sin mucha personalización para el desarrollador final) (se vuelve más complicado cuando intenta escribir subelementos que tienen directivas dentro)

Si la directiva que va a escribir va a acaba de realizar manipulaciones de DOM que se necesita ningún estado interno del ámbito o alcance alteraciones explícitas (en su mayoría cosas muy simples); ir a ningún nuevo ámbito . (tales como ngShow, ngMouseHover, ngClick, ngRepeat)

Si la directiva que va a escribir necesita cambiar algunos elementos en el ámbito primario, pero también necesita manejar algún estado interno, busque un nuevo ámbito secundario . (como ngController)

Asegúrese de consultar el código fuente de las directivas: https://github.com/angular/angular.js/tree/master/src/ng/directive
Es de gran ayuda saber cómo pensar en ellas.

Umur Kontacı
fuente
Si varios componentes necesitan comunicarse entre sí, pueden tener un alcance y uso aislados require, por lo que mantener sus directivas aún desacopladas. Entonces, ¿cómo limita las posibilidades? Incluso hace que las directivas sean más específicas (así que declara de qué dependes). Entonces, dejaría solo una regla: si su directiva tiene estado o necesita algunos datos del alcance donde se usa, use un alcance aislado. De lo contrario, no use el alcance. Y sobre los "ámbitos secundarios": también he escrito muchas directivas y nunca he necesitado esta función. Si "necesita cambiar algunos elementos en el ámbito primario", utilice enlaces.
Valentyn Shybanov
Y también sobre "necesita cambiar algunos elementos en el ámbito primario": si modifica algo en el ámbito secundario, los cambios no se rellenarán en el ámbito primario (a menos que use un $parenttruco sucio ). Entonces, en realidad, los "ámbitos secundarios" para las directivas es algo que parece que debería usarse bastante atrás, como ngRepeatque crea nuevos ámbitos secundarios para que cada elemento se repita (pero también lo crea usando scope.$new();y no scope: true.
Valentyn Shybanov
1
No puede solicitar varios ámbitos aislados dentro del mismo elemento, no puede acceder a las funciones en el ámbito principal, a menos que las vincule explícitamente. (Buena suerte con el uso de ngClicketc.). Requerir crea una especie de desacoplamiento. Estoy de acuerdo, pero aún debe conocer la directiva principal. A menos que sea como un componente , estoy en contra del aislamiento. Las directivas (al menos, la mayoría de ellas) están destinadas a ser altamente reutilizables y el aislamiento rompe esto.
Umur Kontacı
Tampoco uso los ámbitos secundarios en las directivas, pero dado que un ámbito secundario hereda prototípicamente del ámbito primario, si el acceso a una propiedad dentro de una propiedad en el ámbito primario, los cambios se completan. Los autores de Angular hablaron sobre ello en la reunión de MTV, es "bueno tener un punto en alguna parte" youtube.com/watch?v=ZhfUv0spHCY
Umur Kontacı
55
Primero, creo que eres demasiado duro con los ámbitos de aislamiento. Creo que tienen una aplicabilidad más amplia de la que les da crédito por tener y que hay formas de evitar muchos de los desafíos que (correctamente) señaló que enfrentamos al usarlos. También estoy en desacuerdo con "no mucha personalización para el desarrollador final" - vea mi respuesta para más detalles. Dicho esto, su respuesta no fue mala ni incorrecta y sí respondió a la pregunta, por lo que no estoy seguro de por qué fue rechazada. Entonces, +1.
Josh David Miller
9

Solo pensé que agregaría mi comprensión actual y cómo se relaciona con otros conceptos de JS.

Predeterminado (por ejemplo, no declarado o alcance: falso)

Esto es filosóficamente equivalente al uso de variables globales. Su directiva puede acceder a todo en el controlador principal, pero también los afecta y se ve afectado al mismo tiempo.

alcance:{}

Esto es como un módulo, cualquier cosa que quiera usar debe pasarse explícitamente. Si CADA directiva que usa es un ámbito aislado, puede ser el equivalente a hacer que CADA archivo JS escriba su propio módulo con mucha sobrecarga al inyectar todas las dependencias.

alcance: niño

Este es el punto medio entre las variables globales y el paso explícito. Es similar a la cadena de prototipos de javascript y solo le extiende una copia del alcance principal. Si crea un ámbito aislado y pasa todos los atributos y funciones del ámbito primario, es funcionalmente equivalente a esto.


La clave es que CUALQUIER directiva se puede escribir de CUALQUIER manera. Las diferentes declaraciones de alcance están ahí para ayudarlo a organizarse. Puede hacer de todo un módulo, o simplemente puede usar todas las variables globales y tener mucho cuidado. Para facilitar el mantenimiento, es preferible modularizar su lógica en partes lógicamente coherentes. Hay un equilibrio entre una pradera abierta y una cárcel cerrada. Creo que la razón por la que esto es complicado es que cuando las personas se enteran de esto, piensan que están aprendiendo sobre cómo funcionan las directivas, pero en realidad están aprendiendo sobre la organización del código / lógica.

Otra cosa que me ayudó a entender cómo funcionan las directivas es aprender sobre ngInclude. ngInclude te ayuda a incluir parciales html. Cuando comencé a usar directivas, descubrí que podía usar su opción de plantilla para reducir su código, pero realmente no estaba adjuntando ninguna lógica.

Por supuesto, entre las directivas de angular y el trabajo del equipo de angular-ui , todavía no he tenido que crear mi propia directiva que haga algo sustancial, por lo que mi punto de vista sobre esto puede ser completamente erróneo.

usuario2483724
fuente
2

Estoy de acuerdo con Umur. En teoría, los ámbitos aislados suenan maravillosos y "portátiles", pero al crear mi aplicación para involucrar una funcionalidad no trivial, me encontré con la necesidad de incorporar varias directivas (algunas anidadas dentro de otras o agregarles atributos) para poder escribir completamente en mi HTML propio, que es el propósito de un lenguaje específico de dominio.

Al final, es demasiado extraño tener que pasar todos los valores globales o compartidos en la cadena con múltiples atributos en cada invocación DOM de una directiva (como se requiere con el alcance aislado). Simplemente parece tonto escribir repetidamente todo eso en el DOM y se siente ineficiente, incluso si se trata de objetos compartidos. También complica innecesariamente las declaraciones de directivas. La solución alternativa del uso de $ parent para "alcanzar" y tomar valores de la directiva HTML parece muy mala.

Yo también terminé cambiando mi aplicación para tener principalmente directivas de alcance infantil con muy pocos aislamientos, solo aquellos que no necesitan acceder a CUALQUIER COSA de los padres que no sea lo que pueden pasar a través de atributos simples y no repetitivos.

Habiendo soñado el sueño de los lenguajes específicos de dominio durante décadas antes de que ocurriera tal cosa, estoy eufórico de que AngularJS brinde esta opción y sé que, a medida que más desarrolladores trabajen en esta área, veremos algunas aplicaciones muy interesantes que También es fácil para sus arquitectos escribir, expandir y depurar.

- D

Ungallery
fuente