Angularjs, passando escopo entre rotas

87

Tenho uma situação com um formulário que se estende por várias páginas (pode não ser o ideal, mas é assim). Eu gostaria de ter um escopo para todo o formulário que é preenchido conforme você avança, de modo que, se o usuário alternar entre as etapas, seja fácil lembrar o estado.

Portanto, preciso fazer, em muito pseudocódigo:

  1. Conjunto $scope.val = <Some dynamic data>
  2. Clique em um link e seja direcionado para um novo modelo (provavelmente com o mesmo controlador).
  3. $scope.val ainda deve ter o mesmo valor da última página.

De alguma forma, persistir dados para o escopo é a maneira certa de fazer isso, ou existe alguma outra maneira? Você pode até mesmo criar um controlador que tenha um escopo persistente entre as rotas, exceto para salvá-lo em um banco de dados, é claro.

Erik Honn
fonte
Apenas como um complemento ao ganaraj: aqui você encontra uma entrada de blog muito boa com um screencast sobre como fazer com que diferentes controladores se comuniquem. Existem também alguns jsfiddles úteis para brincar. onehungrymind.com/angularjs-communicating-between-controllers Espero que ajude.
F Lekschas

Respostas:

138

Você precisa usar um serviço, pois um serviço persistirá ao longo da vida do seu aplicativo.

Digamos que você queira salvar dados pertencentes a um usuário

É assim que você define o serviço:

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

Em seu primeiro 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
fonte
6
Ahhh! Eu tentei isso antes, mas não consegui fazer funcionar porque usei o mesmo controlador para ambas as rotas, portanto, em cada nova rota, os valores padrão sobrescreveriam os que eu inseri na página anterior. Com dois controladores, ou sem valores padrão, isso não acontece mais. Obrigado!
Erik Honn
1
E o Value Recipe docs.angularjs.org/guide/providers , como myApp.value('clientId', 'a12345654321x');se também fosse persistente entre as rotas?
The Bndr de
@TheBndr: Deveria ser. Os valores são apenas uma forma especial de serviços, como eu os entendo.
Robert Koritnik
3
Os serviços não irão persistir os dados se você recarregar a página ... Então se você estiver na página 4 do seu formulário e recarregar essa página, você perderá todos os dados preenchidos.
danielrvt
1
Boa resposta! Como um ponto lateral, um guia de estilo para aqueles que aceitam esta resposta - serviços que não são construtores (ou seja, usados ​​como novo XX) devem ser chamados de camelo em minúsculas: github.com/mgechev/angularjs-style-guide .
Matt Byrne
9

O serviço funcionará, mas uma maneira lógica de fazê-lo usando apenas escopos e controladores comuns é configurar seus controladores e elementos de forma que reflitam a estrutura do seu modelo. Em particular, você precisa de um elemento pai e controlador que estabeleça um escopo pai. As páginas individuais do formulário devem residir em uma visualização filha do pai. O escopo pai persiste mesmo quando a visualização filho é atualizada.

Suponho que você esteja usando ui-router para que possa ter visualizações nomeadas aninhadas. Então, em pseudocódigo:

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

Em seguida, WizardController define as variáveis ​​de escopo que você deseja preservar nas etapas do formulário de várias páginas (ao qual estou me referindo como um "assistente"). Então, suas rotas serão atualizadas stepViewapenas. Cada etapa pode ter seus próprios modelos, controladores e escopos, mas seus escopos são perdidos de página para página. Mas os escopos em WizardController são preservados em todas as páginas.

Para atualizar os escopos WizardController dos controladores filhos, você precisará usar sintaxe como $scope.$parent.myProp = 'value'ou definir uma função setMyPropem WizardController para cada variável de escopo. Caso contrário, se você tentar definir as variáveis ​​de escopo pai diretamente dos controladores filhos, você acabará apenas criando uma nova variável de escopo no próprio filho, sombreando a variável pai.

É meio difícil de explicar e peço desculpas pela falta de um exemplo completo. Basicamente, você deseja um controlador pai que estabeleça um escopo pai, que será preservado em todas as páginas do formulário.

tksfz
fonte
1
Isso funcionaria com o ui-router sim (e é uma maneira muito boa de criar uma página como essa), mas não fora da caixa. Fora da caixa, o método de serviço é o caminho a percorrer.
Erik Honn
1
Só para esclarecer, as propriedades do escopo pai sempre serão herdadas pelo escopo filho. No entanto, se você deseja definir uma propriedade de escopo pai a partir do escopo filho, você precisa acessá-la no escopo pai, caso contrário, você está criando uma nova propriedade com o mesmo nome no escopo filho. A configuração de uma propriedade de escopo pai pode ser feita indo:$scope.$parent.myProp = 'hello world';
mbursill
1
Esta é uma abordagem muito mais agradável do que usar um serviço. Os serviços são singletons, portanto, qualquer estado definido lá é global para o aplicativo. Se o usuário sair do assistente no meio e reiniciá-lo, haverá um estado de sobra no serviço da execução anterior - a menos que você certifique-se de limpar o serviço adequadamente em cada ponto de saída, mas isso pode facilmente se tornar uma dor de cabeça de manutenção substancial!
Dan King
Muito mais interessado nesta resposta
Kurai Bankusu
Os serviços são a primeira coisa que vem à mente se você não estiver usando o Angular UI Router. Se você estiver usando o UI Router, porém, ele tem resoluções aninhadas que podem persistir as informações entre as visualizações aninhadas. Acho que seria uma abordagem mais limpa do que tentar acessar o escopo pai. medium.com/opinionated-angularjs/…
losmescaleros
5

Os dados podem ser passados ​​entre controladores de duas maneiras

  1. $rootScope
  2. Modelo

O exemplo abaixo demonstra a passagem de valores usando o 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"
  }
}

// Modifique o valor no controlador A

Controller.js

function Controller($scope, SharedService){

 $scope.SharedService = SharedService;

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

// Acesse o valor no controlador dois

ControllerTwo.js

function ControllerTwo($scope, SharedService){

 $scope.SharedService = SharedService;

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

Espero que isto ajude.

DILIP KOSURI
fonte