Problemas de alcance con modal de interfaz de usuario angular

80

Tengo problemas para entender / usar los alcances para una interfaz de usuario modal angular.

Si bien no es evidente de inmediato aquí, tengo los módulos y todo configurado correctamente (por lo que puedo decir), pero estos ejemplos de código en particular son donde estoy encontrando el error.

index.html (la parte importante)

<div class="btn-group">
    <button class="btn dropdown-toggle btn-mini" data-toggle="dropdown">
        Actions
        <span class="caret"></span>
    </button>
    <ul class="dropdown-menu pull-right text-left">
        <li><a ng-click="addSimpleGroup()">Add Simple</a></li>
        <li><a ng-click="open()">Add Custom</a></li>
        <li class="divider"></li>
        <li><a ng-click="doBulkDelete()">Remove Selected</a></li>
    </ul>
</div>

Controller.js (nuevamente, la parte importante)

MyApp.controller('AppListCtrl', function($scope, $modal){
    $scope.name = 'New Name';
    $scope.groupType = 'New Type';

    $scope.open = function(){
        var modalInstance = $modal.open({
            templateUrl: 'partials/create.html',
            controller: 'AppCreateCtrl'
        });
        modalInstance.result.then(function(response){

            // outputs an object {name: 'Custom Name', groupType: 'Custom Type'}
            // despite the user entering customized values
            console.log('response', response);

            // outputs "New Name", which is fine, makes sense to me.                
            console.log('name', $scope.name);

        });
    };
});

MyApp.controller('AppCreateCtrl', function($scope, $modalInstance){
    $scope.name = 'Custom Name';
    $scope.groupType = 'Custom Type';

    $scope.ok = function(){

        // outputs 'Custom Name' despite user entering "TEST 1"
        console.log('create name', $scope.name);

        // outputs 'Custom Type' despite user entering "TEST 2"
        console.log('create type', $scope.groupType);

        // outputs the $scope for AppCreateCtrl but name and groupType
        // still show as "Custom Name" and "Custom Type"
        // $scope.$id is "007"
        console.log('scope', $scope);

        // outputs what looks like the scope, but in this object the
        // values for name and groupType are "TEST 1" and "TEST 2" as expected.
        // this.$id is set to "009" so this != $scope
        console.log('this', this);

        // based on what modalInstance.result.then() is saying,
        // the values that are in this object are the original $scope ones
        // not the ones the user has just entered in the UI. no data binding?
        $modalInstance.close({
            name: $scope.name,
            groupType: $scope.groupType
        });
    };
});

create.html (en su totalidad)

<div class="modal-header">
    <button type="button" class="close" ng-click="cancel()">x</button>
    <h3 id="myModalLabel">Add Template Group</h3>
</div>
<div class="modal-body">
    <form>
        <fieldset>
            <label for="name">Group Name:</label>
            <input type="text" name="name" ng-model="name" />           
            <label for="groupType">Group Type:</label>
            <input type="text" name="groupType" ng-model="groupType" />
        </fieldset>
    </form>
</div>
<div class="modal-footer">
    <button class="btn" ng-click="cancel()">Cancel</button>
    <button class="btn btn-primary" ng-click="ok()">Add</button>
</div>

Entonces, mi pregunta es: ¿por qué el alcance no tiene un doble vínculo con la interfaz de usuario? y ¿por qué thistiene los valores personalizados, pero $scopeno los tiene?

Intenté agregar ng-controller="AppCreateCtrl"al cuerpo div en create.html, pero arrojó un error: "Proveedor desconocido: $ modalInstanceProvider <- $ modalInstance", así que no hubo suerte.

En este punto, mi única opción es devolver un objeto con this.namey en this.groupTypelugar de usar $scope, pero se siente mal.

coblr
fuente
Buena discusión sobre el alcance modal aquí: github.com/mgcrea/angular-strap/issues/14
steampowered

Respuestas:

60

Conseguí que el mío funcione así:

var modalInstance = $modal.open({
  templateUrl: 'partials/create.html',
  controller: 'AppCreateCtrl',
  scope: $scope // <-- I added this
});

Sin nombre de formulario, no $parent. Estoy usando AngularUI Bootstrap versión 0.12.1.

I fue advertido a esta solución por este .

Jason Swett
fuente
Esta es una solución mucho mejor. Mucho más limpio. Recientemente actualizamos a 0.12.1 y actualmente estamos arreglando los cambios importantes que introdujo. Puedo agregar esto a la lista. ¡¡Gracias!!
coblr
Acepto esta respuesta porque es la implementación actual y será más útil para aquellos que terminan aquí tratando de que las cosas funcionen. Lea el hilo completo a menos que esté utilizando la última versión (~ 0.12) de Angular UI.
coblr
El modal no parece poder cerrarse con $ modalStack si el estado ha cambiado si se establece el alcance
phazei
Hola, esto resolvió mi problema por un tiempo, pero noté algo extraño al cerrar el modal / diálogo, el contenido estaba cambiando y parecía interferir con algo en un objeto de alcance. agregar alcance: $ alcance, preserveScope: true // <- ¡importante de alguna manera ayudó! No estoy seguro de por qué, pero lo encontré en material.angularjs.org/latest/api/service/… Editar: Descubrí por qué. cuando establece alcance: para el cuadro de diálogo, y luego cierra el cuadro de diálogo "Este alcance se destruirá cuando se elimine la hoja inferior a menos que preserveScope esté establecido en verdadero".
Max
En mi controlador, estoy usando vm = this. No estoy usando $ scope. Entonces, ¿qué debo asignar al alcance?
Ankit Prajapati
66

Cuando se trata de ámbitos anidados, no vincule <input>s directamente a miembros del ámbito:

<input ng-model="name" /> <!-- NO -->

Únelos al menos a un nivel más profundo:

<input ng-model="form.name" /> <!-- YES -->

La razón es que los ámbitos heredan prototípicamente su ámbito principal. Entonces, al configurar miembros de primer nivel, estos se establecen directamente en el ámbito secundario, sin afectar al principal. En contraste con eso, cuando se vincula a campos anidados ( form.name), el miembro formse lee desde el ámbito principal, por lo que al acceder a la namepropiedad se accede al objetivo correcto.

Lea una descripción más detallada aquí .

Nikos Paraskevopoulos
fuente
14
Si bien cambiar esto a 'form.name' no hizo nada, cambiarlo a ng-model = "$ parent.name" solucionó el problema. ¡Gracias! (y gracias por el material de lectura también. Aún no he visto este.)
coblr
1
Si usa la controller assintaxis, no se encontrará con problemas de alcance anidado como este
rob
¿Cuál es la controller as sintaxis?
adrianboimvaser
1
Consulte docs.angularjs.org/api/ng.directive:ngController . Básicamente haciendo ng-controller = "MyCtrl as my" y luego haciendo referencia a un ng-model como my.someVar
Ryan Q
2
@Nikos Paraskevopoulos, ¿Qué representa "form.name"? ¡No se menciona en el código proporcionado en absoluto!
ŁukaszBachman
7

Actualización de noviembre de 2014 :

En realidad, su código debería funcionar después de actualizar a ui-bootstrap 0.12.0. Alcance transcluye se fusionó con el alcance del controlador de manera más necesidad de $parento form.cosas.

Antes de la 0.12.0 :

El modal usa la transclusión para insertar su contenido. Gracias a ngFormque puedes controlar el alcance por nameatributo. Entonces, para escapar del alcance transcluido, simplemente modifique el formulario de esta manera:

<form name="$parent">

o

<form name="$parent.myFormData">

Los datos del modelo estarán disponibles en el ámbito del controlador.

gertas
fuente
Para ser claros, ¿está diciendo que el alcance del controlador dentro del cual está llamando $modaldebería estar disponible para el controlador asignado al modal?
Jason Swett
No, el problema era que incluso el controlador de instancia modal no era fácilmente accesible para los formularios. Para acceder al controlador que abre el modal, simplemente scope:$scopeingrese $ modal.open params map.
gertas
¿Te refieres a Angular UI? ¿Es eso diferente de UI Bootstrap?
Noah
1
$scope.open = function () {

          var modalInstance = $uibModal.open({
              animation: $scope.animationsEnabled,
              templateUrl: 'myModalContent.html',
              controller: 'salespersonReportController',
              //size: size
              scope: $scope
            });

      };

funciona para mí alcance: $ alcance gracias Jason Swett

Ali Shoaib
fuente
1

Agrego alcance: $ alcance y luego funciona.

JBRandri
fuente