$ stateParams de `ui-router` frente a $ state.params

116

Con ui-router, es posible inyectar $stateo $stateParamsen un controlador para obtener acceso a los parámetros en la URL. Sin embargo, acceder a los parámetros a través de $stateParamssolo expone los parámetros que pertenecen al estado administrado por el controlador que accede a él, y sus estados principales, mientras que $state.paramstiene todos los parámetros, incluidos los de cualquier estado secundario.

Dado el siguiente código, si cargamos directamente la URL http://path/1/paramA/paramB, así es como funciona cuando se cargan los controladores:

$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
}

La pregunta es, ¿por qué la diferencia? ¿Y existen pautas de mejores prácticas sobre cuándo y por qué debe usar o evitar usar cualquiera de ellos?

Merott
fuente
Una pregunta tan excelentemente ilustrada: ¡gracias por decirme incluso lo que estaba tratando de preguntar!
Peter Nixey

Respuestas:

65

La documentación reitera sus hallazgos aquí: https://github.com/angular-ui/ui-router/wiki/URL-Routing#stateparams-service

Si no me falla la memoria, $stateParamsse introdujo más tarde que el original $state.params, y parece ser un simple ayudante inyector para evitar escribir continuamente $state.params.

Dudo que existan pautas de mejores prácticas, pero el contexto me gana. Si simplemente desea acceder a los parámetros recibidos en la URL, utilice $stateParams. Si desea saber algo más complejo sobre el estado en sí, utilice $state.

Matt Way
fuente
1
Me encontré usando $state.paramsin ACtrl, porque quería verificar si yetAnotherParamestá configurado. De modo que si no es así, puedo hacer algo. No voy a entrar en los detalles de ese algo, ya que podría justificar una pregunta propia. Sin embargo, creo que puedo estar haciendo un truco al verificar un parámetro introducido por un estado secundario y no reconocido por el estado actual a través de $stateParams. Desde entonces he encontrado un enfoque alternativo.
Merott
3
En realidad, la diferencia entre los dos es más que una cuestión de contexto. $ stateParams captura parámetros basados ​​en URL que $ state considera que se aplican a ese estado, incluso si su estado secundario contiene más parámetros . $ state.params parece capturar todo url + no url params base de la situación actual que se encuentra. Si se encuentra en el estado parent.child, a continuación, $stateParamsen parentControllerevaluará params basados en URL de parent, pero no los de parent.child. Vea este problema .
Amy.js
1
Por otro lado, $ stateParams puede preservar objetos personalizados, tipos, etc. mientras que $ state.params "convertiría objetos personalizados en objetos simples".
Amy.js
2
$stateParamsfunciona en resolución, mientras que $state.paramses incorrecta (no muestra parámetros para el estado que aún no se ha resuelto)
karaxuna
1
Descubrí que el alcance puede $ watch $ state.params, pero no $ stateParams. No tengo ni idea de porqué.
weltschmerz
19

Otra razón para usarlo $state.paramses para el estado no basado en URL, que (en mi opinión) está lamentablemente poco documentado y es muy poderoso.

Acabo de descubrir esto mientras buscaba en Google cómo pasar el estado sin tener que exponerlo en la URL y respondí una pregunta en otra parte de SO.

Básicamente, permite este tipo de sintaxis:

<a ui-sref="toState(thingy)" class="list-group-item" ng-repeat="thingy in thingies">{{ thingy.referer }}</a>
pardo
fuente
Hola bbrown, de alguna manera no puedo hacer que funcione, ¿tienes un ejemplo de trabajo?
storm_buster
14

EDITAR: Esta respuesta es correcta para la versión 0.2.10. Como señaló @Alexander Vasilyev, no funciona en la versión 0.2.14.

Otra razón para usar $state.paramses cuando necesita extraer 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
fuente
2
Ya no es aplicable. Ver Plunker: plnkr.co/edit/r2JhV4PcYpKJdBCwHIWS?p=preview
Alexander Vasilyev
4

Hay muchas diferencias entre estos dos. Pero mientras trabajaba prácticamente he descubierto que usar $state.paramsmejor. Cuando usa más y más parámetros, esto puede resultar confuso de mantener $stateParams. donde si usamos múltiples parámetros que no son parámetros de URL $statees muy ú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
fuente
3

Tengo un estado de raíz que resuelve algo. Pasar $statecomo parámetro de resolución no garantizará la disponibilidad de $state.params. Pero usando $stateParamswill.

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-router": "~ 0.2.15"

jchnxu
fuente
2

Una observación interesante que hice al pasar los parámetros de estado anteriores de una ruta a otra es que $ stateParams se eleva y sobrescribe los parámetros de estado de la ruta anterior que se pasaron con los parámetros de estado actual, pero el uso de $state.params no lo hace.

Al 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'}}

Al 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
fuente
1

Aquí en este artículo se explica claramente: El $stateservicio proporciona una serie de métodos útiles para manipular el estado, así como los datos pertinentes sobre el estado actual. Los parámetros del estado actual son accesibles en el $stateservicio en la tecla params. El $stateParamsservicio devuelve este mismo objeto. Por lo tanto, el $stateParamsservicio es estrictamente un servicio de conveniencia para acceder rápidamente al objeto params en el $stateservicio.

Como tal, ningún controlador debería inyectar tanto el $stateservicio como su servicio de conveniencia $stateParams. Si $statese está inyectando solo para acceder a los parámetros actuales, el controlador debe reescribirse para inyectar en su $stateParamslugar.

pabloRN
fuente