Angularjs, pasando alcance entre rutas

87

Tengo una situación con un formulario que se extiende a lo largo de varias páginas (puede que no sea ideal, pero así es). Me gustaría tener un alcance para todo el formulario que se completa a medida que avanza, de modo que si el usuario va y viene entre los pasos, sea fácil recordar el estado.

Así que tengo que hacer, en muy pseudocódigo:

  1. Conjunto $scope.val = <Some dynamic data>
  2. Haga clic en un enlace y sea dirigido a una nueva plantilla (probablemente con el mismo controlador).
  3. $scope.val debería seguir siendo el mismo valor que tenía en la última página.

¿Es la persistencia de datos para el alcance la forma correcta de hacerlo o hay alguna otra forma? ¿Puede incluso crear un controlador que tenga un alcance persistente entre rutas, excepto para guardarlo en una base de datos, por supuesto?

Erik Honn
fuente
Solo como una adición a ganaraj: aquí encontrará una entrada de blog realmente agradable con un screencast sobre cómo hacer que los diferentes controladores se comuniquen. También hay un par de jsfiddles útiles para jugar. onehungrymind.com/angularjs-communicating-between-controllers Espero que te ayude.
F Lekschas

Respuestas:

138

Necesita utilizar un servicio, ya que este permanecerá durante toda la vida de su aplicación.

Supongamos que desea guardar datos pertenecientes a un usuario

Así es como se define el servicio:

app.factory("user",function(){
        return {};
});

En tu primer controlador:

app.controller( "RegistrationPage1Controller",function($scope,user){
    $scope.user = user;
    // and then set values on the object
    $scope.user.firstname = "John";
    $scope.user.secondname = "Smith";
});

app.controller( "RegistrationSecondPageController",function($scope,user){
        $scope.user = user;
        // and then set values on the object
        $scope.user.address = "1, Mars";

    });
ganaraj
fuente
6
¡Ahhh! Lo intenté antes, pero no pude hacer que funcionara porque usé el mismo controlador para ambas rutas, por lo que en cada nueva ruta, los valores predeterminados sobrescribían los que ingresé en la página anterior. Con dos controladores, o sin valores predeterminados, esto ya no sucede. ¡Gracias!
Erik Honn
1
¿Cuál es sobre Value Recipe docs.angularjs.org/guide/providers , como myApp.value('clientId', 'a12345654321x');es que también persistente entre las rutas?
The Bndr
@TheBndr: Debería serlo. Los valores son solo una forma especial de servicios según yo lo entiendo.
Robert Koritnik
3
Los servicios no conservarán los datos si vuelve a cargar la página ... Entonces, si está en la página 4 de su formulario y vuelve a cargar esa página, perderá todos los datos completados.
danielrvt
1
¡Buena respuesta! Como punto lateral, una guía de estilo para aquellos que adoptan esta respuesta: los servicios que no son constructores en sí mismos (es decir, se usan como nuevo XX) deben llamarse camel minúsculas: github.com/mgechev/angularjs-style-guide .
Matt Byrne
9

El servicio funcionará, pero una forma lógica de hacerlo utilizando solo alcances y controladores ordinarios es configurar sus controladores y elementos para que reflejen la estructura de su modelo. En particular, necesita un elemento principal y un controlador que establezcan un alcance principal. Las páginas individuales del formulario deben residir en una vista que sea secundaria para el padre. El ámbito principal persiste incluso cuando se actualiza la vista secundaria.

Supongo que está utilizando ui-router para que pueda tener vistas con nombre anidadas. Luego, en pseudocódigo:

<div ng-controller="WizardController">
  <div name="stepView" ui-view/>
</div>

Luego, WizardController define las variables de alcance que desea conservar en los pasos del formulario de varias páginas (al que me refiero como un "asistente"). Entonces tus rutas se actualizaránstepView solo . Cada paso puede tener sus propias plantillas, controladores y ámbitos, pero sus ámbitos se pierden de una página a otra. Pero los ámbitos de WizardController se conservan en todas las páginas.

Para actualizar los ámbitos de WizardController desde los controladores secundarios, necesitará usar una sintaxis como $scope.$parent.myProp = 'value'o definir una función setMyPropen WizardController para cada variable de ámbito. De lo contrario, si intenta establecer las variables de alcance principal directamente desde los controladores secundarios, terminará creando una nueva variable de alcance en el niño mismo, sombreando la variable principal.

Es un poco difícil de explicar y me disculpo por la falta de un ejemplo completo. Básicamente, desea un controlador principal que establezca un alcance principal, que se conservará en todas las páginas de su formulario.

tksfz
fuente
1
Esto funcionaría con ui-router sí (y es una forma bastante agradable de diseñar una página como esa), pero no de forma inmediata. Fuera de la caja, el método de servicio es el camino a seguir.
Erik Honn
1
Solo para aclarar, las propiedades del ámbito principal siempre serán heredadas por el ámbito secundario. Sin embargo, si desea establecer una propiedad de ámbito principal desde el ámbito secundario, debe acceder a ella en el ámbito principal; de lo contrario, está creando una nueva propiedad con el mismo nombre en el ámbito secundario. La configuración de una propiedad de alcance principal se puede hacer yendo a:$scope.$parent.myProp = 'hello world';
mbursill
1
Este es un enfoque mucho más agradable que utilizar un servicio. Los servicios son singleton, por lo que cualquier estado definido allí es global para la aplicación. Si el usuario sale del asistente a la mitad, luego lo reinicia, habrá un estado restante en el servicio de la ejecución anterior, a menos que se asegure de limpiar el servicio correctamente en cada punto de salida, pero esto puede ser bastante fácilmente se convierte en un importante dolor de cabeza de mantenimiento.
Dan King
Mucho más interesado en esta respuesta
Kurai Bankusu
Los servicios son lo primero que le viene a la mente si no está usando Angular UI Router. Sin embargo, si está utilizando UI Router, tiene resoluciones anidadas que pueden conservar información entre vistas anidadas. Creo que sería un enfoque más limpio que intentar acceder al alcance principal. medium.com/opinionated-angularjs/…
losmescaleros
5

Los datos se pueden pasar entre controladores de dos formas

  1. $rootScope
  2. Modelo

El siguiente ejemplo demuestra el paso de valores usando el modelo

app.js

angular.module('testApp')
  .controller('Controller', Controller)
  .controller('ControllerTwo', ControllerTwo)
  .factory('SharedService', SharedService);

SharedService.js

function SharedService($rootScope){

 return{
    objA: "value1",
    objB: "value2"
  }
}

// Modifica el valor en el controlador A

Controller.js

function Controller($scope, SharedService){

 $scope.SharedService = SharedService;

 $scope.SharedService.objA = "value1 modified";
}

// Accede al valor en controllertwo

ControllerTwo.js

function ControllerTwo($scope, SharedService){

 $scope.SharedService = SharedService;

 console.log("$scope.SharedService.objA"+$scope.SharedService.objA); // Prints "value1 modified"
}

Espero que esto ayude.

DILIP KOSURI
fuente