`ui-router` $ stateParams vs. $ state.params

116

Com o ui-router, é possível injetar $stateou $stateParamsem um controlador para obter acesso aos parâmetros na URL. No entanto, acessar parâmetros por meio de $stateParamsexpõe apenas os parâmetros pertencentes ao estado gerenciado pelo controlador que o acessa, e seus estados pais, enquanto $state.paramspossui todos os parâmetros, incluindo aqueles em quaisquer estados filho.

Dado o código a seguir, se carregarmos diretamente a URL http://path/1/paramA/paramB, é assim que acontece quando os controladores são carregados:

$stateProvider.state('a', {
     url: 'path/:id/:anotherParam/',
     controller: 'ACtrl',
  });

$stateProvider.state('a.b', {
     url: '/:yetAnotherParam',
     controller: 'ABCtrl',
  });

module.controller('ACtrl', function($stateParams, $state) {
   $state.params; // has id, anotherParam, and yetAnotherParam
   $stateParams;  // has id and anotherParam
}

module.controller('ABCtrl', function($stateParams, $state) {
   $state.params; // has id, anotherParam, and yetAnotherParam
   $stateParams;  // has id, anotherParam, and yetAnotherParam
}

A questão é: por que a diferença? E há diretrizes de práticas recomendadas sobre quando e por que você deve usar ou evitar usar qualquer um deles?

Merott
fonte
Uma pergunta tão bem ilustrada - obrigado por me dizer até mesmo o que eu estava tentando perguntar!
Peter Nixey

Respostas:

65

A documentação reitera suas descobertas aqui: https://github.com/angular-ui/ui-router/wiki/URL-Routing#stateparams-service

Se não me falha a memória, $stateParamsfoi apresentado depois do original $state.paramse parece ser um simples auxiliar injetor para evitar a escrita contínua $state.params.

Duvido que haja diretrizes de melhores práticas, mas o contexto vence para mim. Se você simplesmente deseja acessar os parâmetros recebidos na url, use $stateParams. Se você quiser saber algo mais complexo sobre o próprio estado, use $state.

Matt Way
fonte
1
Eu me vi usando $state.paramsin ACtrl, porque queria verificar se yetAnotherParamestá definido. Então, se não for, posso fazer alguma coisa. Não vou entrar nos detalhes de que algo como isso poderia justificar uma questão de sua própria. No entanto, sinto que posso estar fazendo um hack , verificando se há um parâmetro introduzido por um estado filho e não reconhecido pelo estado atual por meio de $stateParams. Eu encontrei uma abordagem alternativa desde então.
Merott
3
Na verdade, a diferença entre os dois é mais do que apenas uma questão de contexto. $ stateParams captura parâmetros baseados em url que $ state considera se aplicam a esse estado, mesmo se seu estado filho contiver mais parâmetros . $ state.params parece capturar todos os parâmetros url + não baseados em url do estado atual em que você está. Se você estiver no estado parent.child, então $stateParamsem parentControllerirá avaliar os parâmetros baseados em url de parent, mas não os de parent.child. Veja esta edição .
Amy.js
1
Por outro lado, $ stateParams pode preservar objetos personalizados, tipos, etc., enquanto $ state.params iria "converter objetos personalizados em objetos simples".
Amy.js
2
$stateParamsfunciona em resolução, enquanto $state.paramsestá incorreto (não mostrando parâmetros para estado que ainda não foi resolvido)
karaxuna
1
Descobri que o escopo pode $ assistir $ state.params, mas não $ stateParams. Eu não tenho ideia do porquê.
weltschmerz
19

Outra razão para usar $state.paramsé para o estado não baseado em URL, que (na minha opinião) é lamentavelmente mal documentado e muito poderoso.

Acabei de descobrir isso enquanto pesquisava no Google sobre como passar o estado sem precisar expô-lo na URL e respondi a uma pergunta em outro lugar no SO.

Basicamente, ele permite este tipo de sintaxe:

<a ui-sref="toState(thingy)" class="list-group-item" ng-repeat="thingy in thingies">{{ thingy.referer }}</a>
bbrown
fonte
Oi bbrown, de alguma forma não consigo fazer funcionar. Você tem um exemplo funcional?
storm_buster de
14

EDIT: Esta resposta está correta para a versão 0.2.10. Como @Alexander Vasilyev apontou, não funciona na versão 0.2.14.

Outro motivo para usar $state.paramsé quando você precisa extrair parâmetros de consulta como este:

$stateProvider.state('a', {
  url: 'path/:id/:anotherParam/?yetAnotherParam',
  controller: 'ACtrl',
});

module.controller('ACtrl', function($stateParams, $state) {
  $state.params; // has id, anotherParam, and yetAnotherParam
  $stateParams;  // has id and anotherParam
}
Daerin
fonte
2
Não é mais aplicável. Consulte Plunker: plnkr.co/edit/r2JhV4PcYpKJdBCwHIWS?p=preview
Alexander Vasilyev
4

Existem muitas diferenças entre os dois. Mas enquanto trabalho praticamente descobri que usar $state.paramsmelhor. Quando você usa mais e mais parâmetros, pode ser confuso mantê-los $stateParams. onde se usarmos vários parâmetros que não são parâmetros de URL $stateé muito útil

 .state('shopping-request', {
      url: '/shopping-request/{cartId}',
      data: {requireLogin: true},
      params : {role: null},
      views: {
        '': {templateUrl: 'views/templates/main.tpl.html', controller: "ShoppingRequestCtrl"},
        'body@shopping-request': {templateUrl: 'views/shops/shopping-request.html'},
        'footer@shopping-request': {templateUrl: 'views/templates/footer.tpl.html'},
        'header@shopping-request': {templateUrl: 'views/templates/header.tpl.html'}
      }
    })
Abdullah Al Noman
fonte
3

Eu tenho um estado de raiz que resolve o sth. Passar $statecomo um parâmetro de resolução não garante a disponibilidade de $state.params. Mas usando $stateParamsvontade.

var rootState = {
    name: 'root',
    url: '/:stubCompanyId',
    abstract: true,
    ...
};

// case 1:
rootState.resolve = {
    authInit: ['AuthenticationService', '$state', function (AuthenticationService, $state) {
        console.log('rootState.resolve', $state.params);
        return AuthenticationService.init($state.params);
    }]
};
// output:
// rootState.resolve Object {}

// case 2:
rootState.resolve = {
    authInit: ['AuthenticationService', '$stateParams', function (AuthenticationService, $stateParams) {
        console.log('rootState.resolve', $stateParams);
        return AuthenticationService.init($stateParams);
    }]
};
// output:
// rootState.resolve Object {stubCompanyId:...}

Usando "angular": "~ 1.4.0", "angular-ui-roteador": "~ 0.2.15"

jchnxu
fonte
2

Uma observação interessante que fiz ao passar parâmetros de estado anteriores de uma rota para outra é que $ stateParams é içado e sobrescreve os parâmetros de estado da rota anterior que foram passados ​​com os parâmetros de estado atuais, mas o uso de $state.params não.

Ao usar $stateParams:

var stateParams        = {};
stateParams.nextParams = $stateParams; //{item_id:123}
stateParams.next       = $state.current.name;

$state.go('app.login', stateParams);
//$stateParams.nextParams on app.login is now:
//{next:'app.details', nextParams:{next:'app.details'}}

Ao usar $ state.params:

var stateParams        = {};
stateParams.nextParams = $state.params; //{item_id:123}
stateParams.next       = $state.current.name;

$state.go('app.login', stateParams);
//$stateParams.nextParams on app.login is now:
//{next:'app.details', nextParams:{item_id:123}}
Shaun E. Tobias
fonte
1

Aqui neste artigo é explicado claramente: O $stateserviço fornece uma série de métodos úteis para manipular o estado, bem como dados pertinentes sobre o estado atual. Os parâmetros do estado atual são acessíveis no $stateserviço na chave params. O $stateParamsserviço retorna esse mesmo objeto. Portanto, o $stateParamsserviço é estritamente um serviço de conveniência para acessar rapidamente o objeto params no $stateserviço.

Dessa forma, nenhum controlador deve injetar o $stateserviço e seu serviço de conveniência $stateParams,. Se o $stateestiver sendo injetado apenas para acessar os parâmetros atuais, o controlador deve ser reescrito para injetar $stateParams.

pabloRN
fonte