Tengo una directiva con un alcance aislado (para poder reutilizar la directiva en otros lugares), y cuando uso esta directiva con un ng-repeat
, no funciona.
He leído toda la documentación y las respuestas de Stack Overflow sobre este tema y entiendo los problemas. Creo que he evitado todas las trampas habituales.
Entonces entiendo que mi código falla debido al alcance creado por la ng-repeat
directiva. Mi propia directiva crea un alcance aislado y realiza un enlace de datos bidireccional a un objeto en el alcance principal. Mi directiva asignará un nuevo valor de objeto a esta variable vinculada y esto funciona perfectamente cuando mi directiva se usa sin ng-repeat
(la variable principal se actualiza correctamente). Sin embargo, con ng-repeat
, la asignación crea una nueva variable en el ng-repeat
alcance y la variable principal no ve el cambio. Todo esto es el esperado según lo que he leído.
También he leído que cuando hay varias directivas en un elemento determinado, solo se crea un ámbito. Y que priority
se puede establecer un en cada directiva para definir el orden en que se aplican las directivas; las directivas se ordenan por prioridad y luego se llaman a sus funciones de compilación (busque la palabra prioridad en http://docs.angularjs.org/guide/directive ).
Así que esperaba poder usar la prioridad para asegurarme de que mi directiva se ejecute primero y termine creando un alcance aislado, y cuando se ng-repeat
ejecute, reutilice el alcance aislado en lugar de crear un alcance que herede prototípicamente del alcance principal. La ng-repeat
documentación indica que esa directiva se ejecuta a nivel de prioridad 1000
. No está claro si 1
es un nivel de prioridad más alto o un nivel de prioridad más bajo. Cuando usé el nivel de prioridad 1
en mi directiva, no hizo ninguna diferencia, así que lo intenté 2000
. Pero eso empeora las cosas: mis enlaces bidireccionales se vuelven undefined
y mi directiva no muestra nada.
He creado un violín para mostrar mi problema . He comentado la priority
configuración en mi directiva. Tengo una lista de objetos de nombre y una directiva llamada name-row
que muestra los campos de nombre y apellido en el objeto de nombre. Cuando se hace clic en un nombre mostrado, quiero que establezca una selected
variable en el ámbito principal. La matriz de nombres y la selected
variable se pasan a la name-row
directiva mediante enlace de datos bidireccional.
Sé cómo hacer que esto funcione llamando a funciones en el alcance principal. También sé que si selected
está dentro de otro objeto y me ato al objeto exterior, las cosas funcionarían. Pero no estoy interesado en esas soluciones en este momento.
En cambio, las preguntas que tengo son:
- ¿Cómo evito
ng-repeat
crear un alcance que hereda prototípicamente del alcance principal y, en su lugar, haga que use el alcance aislado de mi directiva? - ¿Por qué
2000
no funciona el nivel de prioridad en mi directiva? - Usando Batarang, ¿es posible saber qué tipo de visor está en uso?
scope: true
para su directiva. Consulte también (si aún no lo ha hecho) stackoverflow.com/questions/14914213/… Además, el hecho de que una directiva se use en varios lugares no significa que debamos usar automáticamente un alcance aislado.ng-repeat
. Creo que es valioso poder combinar directivas independientes conng-repeat
. Continuará ...ng-repeat
no debería tener un alcance.ng-repeat
tener un alcance tiene sentido para el caso de uso típico, por lo que no estoy sugiriendo que se cambie. En cambio, como comenté en la respuesta de Alex Osborn, creo que crearé una directiva de repetición basada enng-repeat
que no crea su propio alcance. Esto se puede utilizar para repetir directivas que tienen sus propios ámbitos de aislamiento. Continuará ...ng-repeat
o la directiva de repetición personalizada sin alcance. Creo que está bien que la "persona que llama" sepa esto, pero no está bien que una "persona que llama" (la directiva se repite) sepa si se está repitiendo o no. Continuará ...Respuestas:
Bien, a través de muchos de los comentarios anteriores, he descubierto la confusión. Primero, un par de puntos de aclaración:
Aquí hay un ejemplo del mismo código pero con la directiva eliminada:
Aquí hay un JSFiddle que demuestra que no funcionará. Obtiene exactamente los mismos resultados que en su directiva.
¿Por qué no funciona? Porque los ámbitos en AngularJS usan herencia prototípica. El valor
selected
en su alcance principal es primitivo . En JavaScript, esto significa que se sobrescribirá cuando un niño establezca el mismo valor. Hay una regla de oro en los ámbitos de AngularJS: los valores del modelo siempre deben tener un.
en ellos. Es decir, nunca deberían ser primitivos. Consulte esta respuesta SO para obtener más información.Aquí hay una imagen de cómo se ven inicialmente los osciloscopios.
Después de hacer clic en el primer elemento, los ámbitos ahora se ven así:
Observe que
selected
se creó una nueva propiedad en el ámbito ngRepeat. El alcance del controlador 003 no se modificó.Probablemente pueda adivinar lo que sucede cuando hacemos clic en el segundo elemento:
Entonces, su problema en realidad no es causado por ngRepeat en absoluto, es causado por romper una regla de oro en AngularJS. La forma de solucionarlo es simplemente usar una propiedad de objeto:
Aquí hay un segundo JSFiddle que muestra que esto también funciona.
Así es como se ven inicialmente los ámbitos:
Después de hacer clic en el primer elemento:
Aquí, el alcance del controlador se ve afectado, como se desea.
Además, para demostrar que esto seguirá funcionando con su directiva con un alcance aislado (porque, de nuevo, esto no tiene nada que ver con su problema), aquí también hay un JSFiddle para eso, la vista debe reflejar el objeto. Notarás que el único cambio necesario fue usar un objeto en lugar de una primitiva .
Alcances inicialmente:
Ámbitos después de hacer clic en el primer elemento:
Para concluir: una vez más, su problema no es con el alcance aislado ni con cómo funciona ngRepeat. Su problema es que está rompiendo una regla que se sabe que conduce a este mismo problema. Los modelos en AngularJS siempre deben tener una extensión
.
.fuente
.
regla de oro solo es necesaria para los ámbitos que tienen herencia prototípica. Con isolate-scopes, las variables que le interesan se definen en lascope: {}
definición de la directiva y, por lo tanto, esas variables existirán en isolate-scope desde el principio. Además, no enmascaran las variables principales porque el ámbito aislado no hereda prototípicamente del ámbito principal. es decir, no hay un ámbito "principal" para enmascarar..
.Sin tratar directamente de evitar responder a sus preguntas, en su lugar, eche un vistazo al siguiente violín:
http://jsfiddle.net/dVPLM/
El punto clave es que en lugar de tratar de luchar y cambiar el comportamiento convencional de Angular, podría estructurar su directiva para trabajar
ng-repeat
en lugar de intentar anularla.En tu plantilla:
En su directiva:
En respuesta a sus preguntas:
ng-repeat
creará un alcance, realmente no debería intentar cambiar esto.fuente
ng-repeat
. Quizás cuando se escribe por primera vez, nunca se usa conng-repeat
. Y en algún momento en el futuro, podría acostumbrarseng-repeat
, y en ese momento, debería funcionar sin una reescritura. Con suerte, una versión futura de AngularJS lo hará posible.ng-repeat
o no. Pero parece que tendría que pasar una función a la directiva para que pueda modificar las variables en el ámbito principal, en lugar de poder mutar un enlace bidireccional en el propio ámbito de la directiva. Las directivas reutilizables y de enlace bidireccional son mis dos cosas favoritas de AngularJS yng-repeat
está demostrando ser una mosca en el ungüento. Tal vez pueda escribir unng-repeat
equivalente que no cree su propio alcance.