AngularJS : How to pas multiple parameters in route - angularjs

I am building an application using AngularJs.
I suddenly stuck in a routing where I want to send multiple data in route but its not woking.
Her is my route :
.state('/filter-list', {
url: "/search/india/:data?",
views : {
"" : {
templateUrl:"/filter/filter-list.html",
controller: 'filterListCtrl',
controllerAs:'vm',
authenticated: true
}
},
resolve: {
loadMyCtrl: ['$ocLazyLoad', function($ocLazyLoad) {
return $ocLazyLoad.load('/filter-list'); // Resolve promise and load before view
}]
}
})
I want to send data like this:
/search/india/state/city/hotel-name
But if I try to send this, it is redirected my application to home page.
only '/search/india/new-delhi' is working.
Can someone help me to solve this problem.
Thanks in advance.

you can add the params in your route like this:
.state('/filter-list', {
url: "/search/india",
params: {
state: '',
city: '',
hotel-name: ''
}
...
}
and in your controller you can use $state.go() method like this:
$state.go("/filter-list", {
state: 'your-chosen-state',
city: 'your-chosen-city',
hotel-name: 'your-chosen-hotel'
});
and inside your controller you can capture the params from:
const state = $stateParams.state;
const city = $stateParams.city;
const hotel = $stateParams.hotel-name;

Related

ui-router's state params in data object

I am using ui-router's state data to decide what page title to display on the page. I have a specific scenario where one state might have 2 different page titles because it is shared.
Say I have page titles 'Directors' and 'HR'. These are parameters passed into the state by $state.go('main.employees', { pageType: 'HR' });
How can I possibly get data to return params.pageType like data: { pageTitle: this.params.pageType }. Is there a way to get params in data?
.state('main.employees', {
url: '/employees',
params: {
pageType: null
},
views: {
'content': {
templateUrl: 'app/employees/employees.html',
controller: 'employeesCtrl as vm'
}
},
data: {
pageTitle: this.params.pageType //<-- I have problem here
}
})
I have to use params.pageType only in this specific case because other states do not have this param. I am relying on $state.$current.data.pageTitle to display it.
I've also tried this but then when calling this with $state.$current.data.pageTitle() I get undefined...
data: {
pageTitle: function($state) {
return $state;
}
}
No need to use the data property. You can directly use the params in the controller using $stateParams service.
Ex-
$state.go('main.employees', { pageType: 'HR' });
$state.go('main.employees', { pageType: 'Directors' });
And in controller access the params as given below-
console.log($stateParams.pageType)

ui-router 1.x.x change $transition$.params() during resolve

Trying to migrate an angularjs application to use the new version of angular-ui-router 1.0.14 and stumbled upon a problem when trying to change $stateParams in the resolve of a state.
For example, previously (when using angular-ui-router 0.3.2) modifying $stateParams worked like this:
$stateProvider.state('myState', {
parent: 'baseState',
url: '/calendar?firstAvailableDate',
template: 'calendar.html',
controller: 'CalendarController',
controllerAs: 'calendarCtrl',
resolve: {
availableDates: ['CalendarService', '$stateParams', function(CalendarService, $stateParams) {
return CalendarService.getAvailableDates().then(function(response){
$stateParams.firstAvailableDate = response[0];
return response;
});
}]
}
})
The problem is firstAvailableDate is populated after a resolve and I do not know how to update $transition$.params() during a resolve when usign the new version of angular-ui-router 1.0.14.
I have tried, and managed to update the url parameter with
firing a $state.go('myState', {firstAvailableDate : response[0]}) but this reloads the state, so the screen flickers
modified $transition$.treeChanges().to[$transition$.treeChanges().length-1].paramValues.firstAvailableDate = response[0]; to actually override the parameters. I have done this after looking through the implementation on params() for $transition$.
Although both those options work, they seem to be hacks rather than by the book implementations.
What is the correct approach to use when trying to modify parameters inside a resolve?
Approach with dynamic parameter:
Take a look at this document: params.paramdeclaration#dynamic. Maybe thats what you are looking for: ...a transition still occurs....
When dynamic is true, changes to the parameter value will not cause the state to be entered/exited. The resolves will not be re-fetched, nor will views be reloaded.
Normally, if a parameter value changes, the state which declared that the parameter will be reloaded (entered/exited). When a parameter is dynamic, a transition still occurs, but it does not cause the state to exit/enter.
This can be useful to build UI where the component updates itself when the param values change. A common scenario where this is useful is searching/paging/sorting.
Note that you are not be able to put such logic into your resolve inside your $stateProvider.state. I would do this by using dynamic parameters to prevent the state reload. Unfortunally, the dynamic rules doesn't work when you try to update your state (e.g. by using $stage.go()) inside the resolve part. So I moved that logic into the controller to make it work nice - DEMO PLNKR.
Since userId is a dynamic param the view does not get entered/exited again when it was changed.
Define your dynamic param:
$stateProvider.state('userlist.detail', {
url: '/:userId',
controller: 'userDetail',
controllerAs: '$ctrl',
params: {
userId: {
value: '',
dynamic: true
}
},
template: `
<h3>User {{ $ctrl.user.id }}</h3>
<h2>{{ $ctrl.user.name }} {{ !$ctrl.user.active ? "(Deactivated)" : "" }}</h2>
<table>
<tr><td>Address</td><td>{{ $ctrl.user.address }}</td></tr>
<tr><td>Phone</td><td>{{ $ctrl.user.phone }}</td></tr>
<tr><td>Email</td><td>{{ $ctrl.user.email }}</td></tr>
<tr><td>Company</td><td>{{ $ctrl.user.company }}</td></tr>
<tr><td>Age</td><td>{{ $ctrl.user.age }}</td></tr>
</table>
`
});
Your controller:
app.controller('userDetail', function ($transition$, $state, UserService, users) {
let $ctrl = this;
this.uiOnParamsChanged = (newParams) => {
console.log(newParams);
if (newParams.userId !== '') {
$ctrl.user = users.find(user => user.id == newParams.userId);
}
};
this.$onInit = function () {
console.log($transition$.params());
if ($transition$.params().userId === '') {
UserService.list().then(function (result) {
$state.go('userlist.detail', {userId: result[0].id});
});
}
}
});
Handle new params by using $transition.on* hooks on route change start:
An other approach would be to setup the right state param before you change into your state. But you already said, this is something you don't want. If I would face the same problem: I would try to setup the right state param before changing the view.
app.run(function (
$transitions,
$state,
CalendarService
) {
$transitions.onStart({}, function(transition) {
if (transition.to().name === 'mySate' && transition.params().firstAvailableDate === '') {
// please check this, I don't know if a "abort" is necessary
transition.abort();
return CalendarService.getAvailableDates().then(function(response){
// Since firstAvailableDate is dynamic
// it should be handled as descript in the documents.
return $state.target('mySate', {firstAvailableDate : response[0]});
});
}
});
});
Handle new params by using $transition.on* hooks on route change start via redirectTo
Note: redirectTo is processed as an onStart hook, before LAZY resolves.
This does the same thing as provided above near the headline "Handle new params by using $transition.on* hooks on route change start" since redirectTo is also a onStart hook with automated handling.
$stateProvider.state('myState', {
parent: 'baseState',
url: '/calendar?firstAvailableDate',
template: 'calendar.html',
controller: 'CalendarController',
controllerAs: 'calendarCtrl',
redirectTo: (trans) => {
if (trans.params().firstAvailableDate === '') {
var CalendarService = trans.injector().get('CalendarService');
return CalendarService.getAvailableDates().then(function(response){
return { state: 'myState', params: { firstAvailableDate: response[0] }};
});
}
}
});

Getting a "Transition Rejection( .." error after using "$state.go('stateName', null, {'reload':true});"

I was trying to reload my controller using
$state.go('movies', null, {'reload':true});
But after using this, I am getting this error
Transition Rejection($id: 1 type: 2, message: The transition has been superseded by a different transition, detail: Transition#63( 'home'{} -> 'movies'{} ))
I am clueless why is this happening. Although the functionality is working fine.
Code:
$scope.filterMovies = function(genre){
var filteredMovies = [];
MoviesService.msGetData().then(function(dataparam){
$scope.movielist = dataparam;
for(var idx=0; idx<$scope.movielist.length; idx++){
if($scope.movielist[idx].genres.indexOf(genre) > -1){
filteredMovies.push($scope.movielist[idx]);
MoviesService.msSetFilteredMovies(filteredMovies);
//$state.go('movies');
$state.go('movies', null, {'reload':true});
}
}
});
}
Note: I am using AngularJS v1.6.5 and angular-ui-router v1.0.5
You can use:
$state.reload();
or
$state.transitionTo($state.current, $stateParams, {
reload: true,
inherit: false,
notify: true
});
The error is occurring because you're creating a new transition within an existing one. As the error says, the transition to home is being superseded by the transition to movies which you're creating with $state.go('movies'[, ...]);.
To stop the error occurring place $state.go('movies') within a $timeout. E.g.
$timeout(function() {
$state.go('movies', null, {'reload':true});
});
In my case, a routes file was angry about an empty permissions array:
import * as angular from 'angular';
import '#uirouter/angularjs';
import { StateProvider, UrlRouterProvider } from '#uirouter/angularjs';
import { Permissions } from '#appname/app.permissions';
angular.module('app').config([
'$stateProvider',
'$urlRouterProvider',
'PERMISSIONS',
function ($stateProvider: StateProvider, $urlRouterProvider: UrlRouterProvider, PERMISSIONS: Permissions) {
$urlRouterProvider.when('/batch', '/batch/abc');
$stateProvider
.state('batch', {
parent: 'root',
url: '/batch',
template: '<batch></batch>',
data: {
permissions: [PERMISSIONS.NavItemBatch],
menu: {
name: 'navigation.batch',
icon: 'file_upload',
showSubmenu: true
}
}
}).state('batch.abc', {
url: '/abc',
template: '<abc></abc>',
data: {
permissions: [],// <-- angry - remove entirely or add valid permission
menu: {
name: 'ABC'
}
}
});
}]);

Passing multiple parameters in $stateParams

In the ui-router tutorial https://plnkr.co/edit/jbZgIg?p=info, they are passing a single parameter personId to the state.
ui-sref="people.person({ personId: person.id })"
and the state is:
{
name: 'people.person',
url: '/{personId}',
component: 'person',
resolve: {
person: function(people, $stateParams) {
return people.find(function(person) {
return person.id === $stateParams.personId;
});
}
}
}
I am working on an example in which I want to pass 2 parameters (1 visible using the url, and the second hidden which will be the id that the user can't see in the url). Something similar to:
ui-sref="people.person({ personId: person.id, personUri : person.uri })"
and I want the state to become something like this (but it didn't work!):
{
name: 'people.person',
url: '/{personUri}',
component: 'person',
resolve: {
person: function(people, $stateParams) {
return people.find(function(person) {
return person.id === $stateParams.personId;
});
}
}
}
As the state shows, I want to use the personUri in the url, and I want the personId to be passed to the people service.
You can use params
.state('other', {
url: '/:personUri',
params: {
personUri:"",
// this param is not part of url
// it could be passed with $state.go or ui-sref
personId: "",
}
...
Instead of passing your object in url parameters you can simply call your controller's function by passing in your object and then do whatever you want to do with that object in your controller itself and then call your service inside it.
Example:
$scope.func = function info(person){
console.log(person);
PeopleService.xyz(person).then(function(response){
console.log('Service called')
})
}
In html call it like this :
<button ng-click="func(person)"> Click me</button>

How to implement filtering with Angular Ui-router

We want to implement pagination (or filtering) using ui-router. We don't want to reload controller each time next/prev parameters changed. We want to listen for an event and load data from API with new state parameters.
Code from our routes.js
$stateProvider.state('app.company.account.charges.index', {
url: '?before&after',
controller: 'ChargesCtrl',
templateUrl: 'app/charges.html',
reloadOnSearch: false,
params: {
before: { value: null, squash: true },
after: { value: null, squash: true },
}
});
our controller:
function ChargesCtrl() {
var loadCharges = function() {
Charge.query({before: $state.params.before, after: $state.params.after}).then(function(charges) {
$scope.charges = charges;
});
}
$scope.goNext = function() {
$state.go('app.company.account.charges.index', {before: $scope.pagination.before, after: null});
};
$scope.goPrevious = function() {
$state.go('app.company.account.charges.index', {after: $scope.pagination.after, before: null});
};
$scope.$on('state params were changed', function() {
loadCharges();
});
}
how can we track state params changes if $stateChangeSuccess is not raised because of reloadOnSearch is false.
P.S. now we just broadcast own event to solve this, but I'm looking for 'natural' solution :)
Ui Bootstrap has a really easy way to implement pagination. And it's also easy to integrate with Ui-router! This link will bring you to the pagination help :)
have fun coding
Thanks to #krish I have found the answer: Set URL query parameters without state change using Angular ui-router
And it's to use a hack with changing reloadOnSearch option to false before updating location params and then revert it back in $timeout.
However I think that using something like this is more explicit:
$scope.goNext = function() {
$state.go('app.company.account.charges.index', {before: $scope.pagination.before, after: null});
$scope.$emit('charges.paginated');
};
$scope.$on('charges.paginated', function(event) {
event.stopPropagation();
loadCharges();
});

Resources