Skip to content

Workaround to preserve query string parameters between state transitions #202

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
pholly opened this issue Jun 23, 2013 · 31 comments
Closed

Comments

@pholly
Copy link

pholly commented Jun 23, 2013

Hi all,

I have a workaround for those of us that want query string parameters to persist between state transitions even if they are not defined in the state definitions.

Somewhere global in your app, such as a service or module.run(), copy $location.search() to a global variable in an event listener for the $stateChangeStart event. Then in a listener for the $stateChangeSuccess event assign it back to $location.search(). This works if the query string parameters are NOT part of the urls of your state definitions.

Example:

var app = angular.module('MyApp', ['ngResource', 'ui.state']);
app.config( function ($stateProvider) {
    //configure states on the $stateProvider here
    //Do NOT add query string variables to the url definitions
});
app.run( function ($rootScope) {
    $rootScope.$on('$stateChangeStart',
    function (event, toState, toParams, fromState, fromParams) {
        //save location.search so we can add it back after transition is done
        this.locationSearch = $location.search();
    });

$rootScope.$on('$stateChangeSuccess',
    function (event, toState, toParams, fromState, fromParams) {
        //restore all query string parameters back to $location.search
        $location.search(this.locationSearch);
    });
});

I understand why query parameters should be considered part of a state definition, but for our needs we don't want them to be.

Background for those that want to know why this is needed:

Currently, angular query string variables ($location.search()) are treated exactly like path parts and only persist if included in a state definition.

For example, if you append an angular query string parameter to the sample app's url, such as http://angular-ui.github.io/ui-router/sample/#/contacts/1?variable1=test, then click on "Show Random Contact" which performs a $state.transitionTo(), the query string variable will be lost. If variable1 is included in the state parameters passed to $state.transitionTo() AND in the url of the contacts.detail state definition then it would persist. E.g.: $state.transitionTo('contacts.detail', { contactId: id, variable1: 'test' });

In the app I'm creating this is not the desired behavior. When the user enters a search term in the search box and hits enter, a search is performed, and the search term is appended to the $location query string, e.g. http://www.example.com/myapp#/home?searchTerm=test. We do not want the view to refresh because it thinks there's a state change. We want the user to move between our app and that search term needs to persist in the url because the search box and the list of search results persist in every state. We always want the state to be reflected in the url, and that includes the user's active search. We have a search service that houses the search term and search results data and the url should always be in sync with that search term. We use a couple $watches to ensure they stay in sync.

@ksperling
Copy link
Contributor

It sounds to me like we should be able to cover this scenario with the 'dynamic parameters' feature that's on the roadmap. In your case you would simply make 'searchTerm' a dynamic parameter in your root state.

@pholly
Copy link
Author

pholly commented Jun 24, 2013

Great, thanks. Looking forward to the next release.

@pholly pholly closed this as completed Jun 24, 2013
@timkindberg
Copy link
Contributor

@alexbeletsky
Copy link

I know it's 2 years old already, but I'm working in the exactly same scenario now. Is that possible to preserve query parameters on transition now?

@surfjedi
Copy link

+1

3 similar comments
@victor-duarte
Copy link

+1

@dtrillian
Copy link

+1

@michalsnik
Copy link

+1

@renanmzmendes
Copy link

Calling $state.go('stateName', {}, { location: false }) will keep the original query string when transitioning to another state.

@jmuharsky
Copy link

That doesn't solve the problem, renanmz... I want to be able to issue state transitions, and have my url parameters preserved. The suggestion you're making wouldn't preserve my state params to the url either.

@tamitutor
Copy link

+1

1 similar comment
@vicpon
Copy link

vicpon commented Oct 15, 2015

+1

@michalsnik
Copy link

Have you tried setting 'reloadOnSearch' to false in route? It preserves query string without reloading page.
Check the docs: https://docs.angularjs.org/api/ngRoute/provider/$routeProvider

@aoakeson
Copy link

@michalsnik this is for the built in ng-route and not Angular UI Router.

@michalsnik
Copy link

yeah, but there is reloadOnSearch parameter available in ui-router as well. I belive so, i'm using ui-router with this parameter and everything works just fine. page does't reload but path changes.

@Jamie452
Copy link

Jamie452 commented Dec 1, 2015

I'm having the opposite issue, I keep having to clear the individual params by setting them to null when ever I navigate to a different url param (for example /post/1 to /post/2).

I'm having to call the below code everywhere that I'm trying to navigate to a new post:

$state.go('main.post', { queryParamOne: null, queryParamTwo: null})

I don't want the querystring to persist between these two, for this specific route.

Is there a way around this?

@mackelito
Copy link

+1

@nateabele
Copy link
Contributor

We just released the first alpha of 1.0. Any parameter can now be declared 'dynamic'. Please see if that works.

@carab
Copy link

carab commented Jan 6, 2016

Hello,

@nateabele I saw your previous comment about dynamic parameters, and I wanted to try it out but I didn't find any sample, can you explain what they are and how to use them ?

Thank you for your time !

@nateabele
Copy link
Contributor

$stateProvider.state('foo', { url: '/:bar', params: { bar: { dynamic: true } } }); — that's how you declare them. I'll try to poke around for a more extended example a little later on.

@carab
Copy link

carab commented Jan 7, 2016

Thank you !
What is the purpose of the dynamic type ? Allow for objects params, or multiple params to be used ?

@nateabele
Copy link
Contributor

To allow individual parameters (whether path or search) to arbitrarily respond to observable changes, without initiating a transition.

So in the above example, if you're already in the foo state, and you do $state.go('foo', { bar: 'newValue' }), you won't actually trigger a new transition, you'll just push an update to $stateParams.bar.

@carab
Copy link

carab commented Jan 8, 2016

Okay I see the use case indeed, I may have the need of this for a search component.
I'll come back to you when I'll implement this to give my feedback ;)

Thanks again.

@elynnaie
Copy link

@nateabele I'm trying to use the dynamic parameters with search parameters, and I'm wondering if you can give some guidance. I've got my state definitions like so:

$stateProvider.state('login', { url: '/login', params: { bar: { dynamic: true } } });
$stateProvider.state('logout', { url: '/logout', params: { bar: { dynamic: true } } });

and I'm using html5mode, if that matters. In either state, I can get the query parameter from $location.search().bar, but if I go to /logout?bar=foo and then the logout controller calls $state.go('login'), it doesn't preserve the search parameter during the transition. I also attempted $state.go('login', { bar: $location.search().bar }) but no dice. I am using 1.0.0-beta.1.

Do you have any suggestions to get this to work? Thanks!

@elynnaie
Copy link

Update: I figured out the problem I was having. This works perfectly as long as you specify the search param in the state definition:

$stateProvider.state('login?bar', { url: '/login', params: { bar: { dynamic: true } } });
$stateProvider.state('logout?bar', { url: '/logout', params: { bar: { dynamic: true } } });

Then I can access the param using $stateParams

@jindalrohit
Copy link

+1

2 similar comments
@tanansatpal
Copy link

+1

@alxvallejo
Copy link

+1

@teone
Copy link

teone commented Jan 24, 2017

Same behavior with '$transitions`, in case someone needs it:

    $transitions.onStart({ to: '**' }, (transtion) => {
      // save location.search so we can add it back after transition is done
      this.locationSearch = $location.search();
    });

    $transitions.onSuccess({ to: '**' }, (transtion) => {
      // restore all query string parameters back to $location.search
        $location.search(this.locationSearch);
    });

@NitsanBaleli
Copy link

NitsanBaleli commented Jun 11, 2019

A somewhat shorter version

$transitions.onStart({}, trans => {
    const locationSearch = $location.search();
    trans.promise.finally(() => {
      $timeout(() => {
        $location.search(locationSearch);
      });
    });
});

@PandeleFlorin
Copy link

It seems the problem comes from Angular itself, not ui-router.
angular/angular#36688

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests