javascript - form - modal angularjs example
El objeto de formulario de diálogo modal AngularJS no está definido en el controlador (5)
La respuesta a la pregunta "¿Por qué?" es "scoping" tl; dr creó un nuevo ámbito con el diálogo modal que ocultó el objeto de formulario del alcance de su controlador.
Si simplificamos su código, obtenemos aproximadamente lo siguiente:
<div ng-ctrl="organizeCtrl">
<modal-dialog>
<form name="invitationForm">
<input type="email" ng-model="invitation.email" placeholder="Enter email..." />
<input type="submit" ng-click="sendInvitation()" text="Invite!" />
<input type="button" ng-click="cancel()" text="Cancel :(" />
</form>
</modal-dialog>
</div>
(Esta es una versión muy simplificada que debería tener todos los componentes principales.) Ahora, veamos dónde se crean los ámbitos y qué se inyecta en ellos.
<div ng-ctrl="sendInvitationController">
<!-- scope created above with "invitation" and "sendInvitation" from sendInvitationController -->
<modal-dialog>
<!-- scope created above for the modal dialog transclude -->
<form name="invitationForm">
<!-- add "invitationForm" to the modal dialog''s scope -->
<input type="email" ng-model="invitation.email" placeholder="Enter email..." />
<input type="submit" ng-click="sendInvitation()" text="Invite!" />
<input type="button" ng-click="cancel()" text="Cancel :(" />
</form>
</modal-dialog>
</div>
Aquí puede ver que hay un nuevo ámbito hijo creado en el elemento <modal-dialog>
y que es donde realmente se agrega el objeto invitationForm
. Es por eso que no puede ver el objeto en el sendInvitationController
pero puede verlo en los botones para ng-disabled
. Si desea poder acceder a la construcción del formulario fuera del elemento <modal-dialog>
(por ejemplo, en sendInvitationController
), tendrá que pasarlo en la llamada a la función:
<div ng-ctrl="organizeCtrl">
<modal-dialog>
<form name="invitationForm">
<input type="email" ng-model="invitation.email" placeholder="Enter email..." />
<input type="submit" ng-click="sendInvitation(invitationForm)" text="Invite!" />
<input type="button" ng-click="cancel()" text="Cancel :(" />
</form>
</modal-dialog>
</div>
Con el controlador aceptando el formulario de invitación como un parámetro para la función sendInvitation
:
app.controller(''sendInvitationController'', [''$targetOrganisationId'', ''$scope'', ...,
function ($targetOrganisationId, $scope, ...) {
$scope.invitation = {
targetOrganisation: {
id: $targetOrganisationId
}
};
$scope.sendInvitation = function (form) {
if (form.$invalid) {
return false;
}
// send the invitation...
};
}]);
@Robin identificó la otra solución, específicamente para crear un objeto enraizado en el alcance del sendInvitationController
y luego adjuntar el formulario directamente a ese objeto, confiando en el mecanismo transversal de alcance de Angular para encontrar el objeto de form
en el alcance fuera del <modal-dialog>
y adjunte el objeto de formulario a eso. Tenga en cuenta que si no especificó $scope.form = {}
en sendInvitationController
, Angular habría creado un nuevo objeto para el form
en el alcance para el <modal-dialog>
y aún no habría podido acceder a él en el sendInvitationController
.
Esperamos que esto lo ayude a usted u otras personas a aprender sobre el alcance angular.
Tenemos una página que abre un cuadro de diálogo modal con un formulario como a continuación. Sin embargo, cuando accionamos el controlador que debe manejar la acción de forma, el objeto de forma no está definido y soy demasiado novato en angular para entender por qué ...
Este es el controlador de página principal que tiene la función para abrir el diálogo modal:
app.controller(''organisationStructureController'', [''$scope'', ..., ''$modal'', function ($scope, ..., $modal) {
$scope.openInvitationDialog = function (targetOrganisationId) {
$modal.open({
templateUrl: ''send-invitation.html'',
controller: ''sendInvitationController'',
resolve: {$targetOrganisationId: function () {
return targetOrganisationId;
}
}
}
);
};
en una página como esta:
// inside a loop over organisations
<a ng-click="openInvitationDialog({{organisation.id}})">Invite new member</a>
el html de invitación-diálogo se ve así:
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<!-- ... -->
</div>
<div class="modal-body">
<form name="invitationForm">
<div class="form-group">
<label for="email" style="color:white;">Email</label>
<input type="email" class="form-control" autocomplete="off" placeholder="New member email" id="email" name="email" ng-model="invitation.email" required="true"/>
<span class="error animated fadeIn" ng-show="invitationForm.email.$dirty && invitationForm.email.$error.required">Please enter an email address!</span>
<span class="error animated fadeIn" ng-show="invitationForm.email.$error.email">Invalid email</span>
</div>
<!-- ... -->
<div class="modal-footer">
<button type="button" class="btn btn-default" ng-click="cancel()">Cancel</button>
<button type="submit" class="btn btn-primary" ng-click="sendInvitation()">Invite</button>
</div>
</form>
</div>
</div>
</div>
El controlador que debe manejar la invitación está en otro lugar:
app.controller(''sendInvitationController'', [''$targetOrganisationId'', ''$scope'', ...,
function ($targetOrganisationId, $scope, ...) {
$scope.invitation = {
// ...
targetOrganisation: {
id: $targetOrganisationId
}
};
$scope.sendInvitation = function () {
// $scope.invitationForm is undefined
if ($scope.invitationForm.$invalid) {
return false;
}
// send the invitation...
};
}]);
Entonces, ¿cuál es la forma correcta de obtener el alcance del formulario en el controlador?
¿Tal vez necesito inyectar $modal
en sendInvitationController
y agregar la función sendInvitation
a él? Pero cuando lo hago, la acción nunca entra en el controlador. ¿O debo agregar la función que maneja la acción de envío a $modal.open({ ...
lugar de hacer referencia al controlador? Aunque preferiría tener sendInvitationController en su propio archivo y alcance).
¡Gracias por cualquier ayuda!
EDITAR
Encontramos varias cosas que nos ayudaron a construir una solución y que podrían ayudar a alguien a responder la pregunta en sí:
- el objeto
$scope.invitation
no está indefinido ensendInvitationController
pero contiene los datos correctos, mientras que$scope.invitationForm
permanece indefinido. - dentro de send-invitation.html podemos acceder
$scope.invitationForm.$invalid
y hacer la validación allí mismo:<button type="button" ng-click="sendInvitation()" ng-disabled="invitationForm.$invalid">Invite</button>
Entonces, la pregunta es: ¿por qué no se envía el enlace del objeto invitationForm
al $scope
mientras el modelo de formulario se une correctamente?
Tengo el mío para trabajar así:
$modal.open({
templateUrl: ''send-invitation.html'',
controller: ''sendInvitationController'',
scope: $scope // <-- I added this
}
Sin nombre de formulario, no $parent
. Estoy usando AngularUI Bootstrap versión 0.12.1.
Me informaron sobre esta solución por this .
Tuve el mismo problema y pude resolverlo definiendo el objeto de formulario en el alcance del controlador de modales. Para que su código funcione $scope.form = {};
, por ejemplo, $scope.form = {};
al comienzo de su controlador y cambie su etiqueta de formulario a <form name="form.invitation">
. Después $scope.form.invitation.$invalid
debe ser llenado.
Actualización de noviembre de 2014 : comenzando desde angular-ui-bootstrap 0.12.0
alcance de la transclusión 0.12.0
se fusiona con el alcance del controlador. No hay necesidad de hacer nada.
Antes de 0.12.0 :
Para poner invitationForm
directamente en el ámbito de su controlador principal, debe omitir el ámbito transcluido de esta manera:
<form name="$parent.invitationForm">
Arriba se creará automáticamente un objeto de formulario en su controlador principal. No hay necesidad de material previo a la inicialización, rutas largas de objetos o eventos pasados. Simplemente acceda con $scope.invitationForm
una vez que se abra modal.
$mdDialog.show({
locals: {alert:"display meassage"},
controller: DialogController,
templateUrl: ''views/dialog.html'',
parent: angular.element(document.body),
clickOutsideToClose:true,
backdrop: true,
keyboard: true,
backdropClick: true,
})