switch directivas change angularjs angularjs-directive angularjs-scope angularjs-ng-repeat

angularjs - directivas - ng-change select



Ámbito de aislamiento de directivas con ámbito ng-repeat en AngularJS (2)

De acuerdo, a través de muchos de los comentarios anteriores, he descubierto la confusión. Primero, un par de puntos de aclaración:

  • ngRepeat no afecta el alcance aislado de su elección
  • los parámetros pasados ​​a ngRepeat para su uso en los atributos de su directiva usan un alcance heredado prototípicamente
  • el motivo por el que su directiva no funciona no tiene nada que ver con el alcance aislado

Aquí hay un ejemplo del mismo código pero con la directiva eliminada:

<li ng-repeat="name in names" ng-class="{ active: $index == selected }" ng-click="selected = $index"> {{$index}}: {{name.first}} {{name.last}} </li>

Aquí hay un JSFiddle que demuestra que no funcionará. Obtienes exactamente los mismos resultados que en tu 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 a . en ellos. Es decir, nunca deberían ser primitivos. Vea esta respuesta SO para obtener más información.

Aquí hay una imagen de cómo se ven inicialmente los ámbitos.

Después de hacer clic en el primer elemento, los ámbitos ahora se ven así:

Observe que se creó una nueva propiedad selected en el ámbito ngRepeat. El alcance del controlador 003 no fue alterado.

Probablemente puedas adivinar qué sucede cuando hacemos clic en el segundo elemento:

Por lo tanto, su problema no es causado por ngRepeat, sino que se debe a que se ha roto una regla de oro en AngularJS. La forma de solucionarlo es simplemente usar una propiedad de objeto:

$scope.state = { selected: undefined };

<li ng-repeat="name in names" ng-class="{ active: $index == state.selected }" ng-click="state.selected = $index"> {{$index}}: {{name.first}} {{name.last}} </li>

Aquí hay un segundo JSFiddle mostrando que esto también funciona.

Aquí es cómo se ven inicialmente los ámbitos:

Después de hacer clic en el primer elemento:

Aquí, el alcance del controlador se ve afectado, según lo deseado.

Además, para demostrar que esto seguirá funcionando con su directiva con un alcance aislado (porque, nuevamente, esto no tiene nada que ver con su problema), aquí también hay un JSFiddle , la vista debe reflejar el objeto. Notarás que el único cambio necesario fue usar un objeto en lugar de un primitivo .

Scopes inicialmente:

Ámbitos después de hacer clic en el primer elemento:

Para concluir: una vez más, su problema no está en el alcance aislado y no está relacionado con el funcionamiento de ngRepeat. Su problema es que está infringiendo una regla que se sabe que conduce a este problema. Los modelos en AngularJS siempre deben tener a . .

Tengo una directiva con un alcance aislado (para poder reutilizar la directiva en otros lugares) y cuando uso esta directiva con una 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 todos los problemas habituales.

Entonces entiendo que mi código falla debido al alcance creado por la directiva ng-repeat . Mi propia directiva crea un ámbito aislado y realiza un enlace de datos bidireccional a un objeto en el ámbito principal. Mi directiva asignará un nuevo valor-objeto a esta variable enlazada 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 ámbito ng-repeat y la variable primaria no ve el cambio. Todo esto es el esperado en base a lo que he leído.

También he leído que cuando hay varias directivas en un elemento dado, solo se crea un alcance. Y que se puede establecer una priority en cada directiva para definir el orden en que se aplican las directivas; las directivas se ordenan por prioridad y luego se llaman sus funciones de compilación (busque la prioridad de la palabra 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 ámbito aislado, y cuando se ejecuta ng-repeat , vuelva a utilizar el ámbito aislado en lugar de crear un ámbito que herede prototípicamente del alcance de los padres La documentación ng-repeat indica que esa directiva se ejecuta en el 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 utilicé el nivel de prioridad 1 en mi directiva, no marcó la diferencia, así que probé 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 configuración de priority 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 variable selected en el alcance principal. La matriz de nombres, la variable selected se pasan a la directiva de name-row utilizando enlace de datos bidireccional.

Sé cómo hacerlo funcionar llamando funciones en el alcance principal. También sé que si se selected dentro de otro objeto, y me enlace al objeto externo, las cosas funcionarían. Pero no estoy interesado en esas soluciones en este momento.

En cambio, las preguntas que tengo son:

  • ¿Cómo evito que ng-repeat cree un ámbito que herede prototípicamente del ámbito principal y, en su lugar, haga que use el ámbito aislado de mi directiva?
  • ¿Por qué el nivel de prioridad 2000 en mi directiva no funciona?
  • Usando Batarang, ¿es posible saber qué tipo de alcance está en uso?

Sin tratar de evitar responder a sus preguntas, 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 con ng-repeat en lugar de tratar de anularla.

En tu plantilla:

<name-row in-names-list="names" io-selected="selected"> </name-row>

En tu directiva:

template: '' <ul>'' + '' <li ng-repeat="name in inNamesList" ng-class="activeClass($index)" >'' + '' <a ng-click="setSelected($index)">'' + '' {{$index}} - {{name.first}} {{name.last}}'' + '' </a>'' + '' </li>'' + '' </ul>''

En respuesta a sus preguntas:

  • ng-repeat creará un alcance, realmente no debería intentar cambiar esto.
  • La prioridad en las directivas no es solo el orden de ejecución. Ver: AngularJS: ¿Cómo organiza el compilador HTML el orden de compilación?
  • En Batarang, si revisa la pestaña de rendimiento, puede ver las expresiones vinculadas a cada ámbito y verificar si esto coincide con sus expectativas.