Skip to content

One Time Dynamic Param #3047

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
calebeno opened this issue Sep 28, 2016 · 6 comments
Closed

One Time Dynamic Param #3047

calebeno opened this issue Sep 28, 2016 · 6 comments
Assignees

Comments

@calebeno
Copy link

calebeno commented Sep 28, 2016

Hello friends!

I have been working on integrating the 1.0.0-beta.3 version of ui-router into the application I'm working on. In particular, the dynamic param option is the feature I was hoping to take advantage of. Here's my use case:

I have a route defined with this url: /route/:key
This key has been set to be dynamic.

I am on page one of a list of results and my route url looks like this: /route/123
When navigating to a second page, I hit an intermediate route: /nav
Nav's job is to create a new key value. Once returned it navigates to the new page: route/456
Also, the nav route has location: 'replace' set so that it's url no longer shows in the history.

The key needs to be set to dynamic because sometimes on loading page one:
start page => nav => page 1
I will get an overwrite key which needs to replace the url's key without reloading the page: /route/orw123

However, the issue is that now, when I am on page 2 `route/456' when I click the browser back button, it modifies the route url without reloading the page's controllers as only the dynamic key value has changed. This is problematic.

What I see as a possible solution would be to pass a parameter into either a $state.go or a ui-sref options object which would indicate that the key was dynamic for this use only but then act as non-dynamic in all other circumstances.

Is this a viable possibility or is there another alternative I could use for my case?

This is another case associated with #2796 where dynamic params don't seem to be a complete solution to the problem.

@christopherthielen
Copy link
Contributor

Why do you make the key parameter dynamic?
How do you fetch the data for (page one), (page two)? Via a resolve?

Have you considered updating the data in your component when the parameter changes? http://plnkr.co/edit/0ww2TBT2WNPkZauuvEjT?p=preview

@calebeno
Copy link
Author

calebeno commented Sep 28, 2016

@christopherthielen Thanks for replying. I'll try to clarify.

Here is my route definition:

.state('search', {
    url: '/search/:key',
    params: {
        key: ''
    },
    views: {
        'content@': {
            component: 'searchComponent'
        }
    },
    resolve: {
        navObject: function($stateParams, navStateService) {
            return navStateService.getNavState($stateParams.key)
                .then(response => {
                    ... returns response
                });
        },
        searchResult: function($stateParams, searchService) {
            return searchService.search($stateParams.key)
                .then(response => {
                    ... returns response
                });
        }
    }
})

When the resolves finish I have access to both the navObject and the searchResult in the searchComponent. Sometimes, the searchResult contains a property called overwriteKey. In this case, this function is called:

service.overwriteState = function(overwriteKey) {
    $state.go($state.current.name,
        {key: overwriteKey},
        {location: 'replace'});
};

The goal here is to modify the url such that the overwrite key is now set as the key (for bookmark purposes, among others). However, I do not want the resolve blocks to refire or for the state to be reloaded. Additionally, the previous url with the original key should not be available in the browser history.

To solve this, I was attempting to make key a dynamic param. However, in a list of search results I can have multiple pages. Each page has the same url but a different key. Since each page has only a different key, nothing is reloaded or changed since the key is dynamic.

So the question is: Is there a way to have key be a non-dynamic property unless I am doing the overwrite? Or, is there another way to modify the url directly without going through the dynamic param route?

@shyamal890
Copy link

@christopherthielen Any updates on this? Can we make a param dynamic just for a single operation?

@MartijnWelker
Copy link

For anyone who still needs this, i've made a workaround/hack for this. It works by temporarily setting the param's dynamic property to true, and restoring it afterwards.

http://plnkr.co/edit/hCmot6TK1PMZPW77bvrN?p=preview

In this example we want to update the id param in the url without reloading the resolves/controllers

updateUrlParam('id', 123);

function updateUrlParam (param, newValue) {
    // Set the given param to dynamic
    setDynamicParamState(param, true);

    // Construct a new stateParams object
    var newParams = {};
    newParams[param] = newValue;

    // Navigate to the current state and replace the history item
    $state.go(
        '.',
        newParams,
        {location: 'replace'}
    ).then(function () {
        // Reset the param to non-dynamic again
        setDynamicParamState(param, false);
    });
}

function setDynamicParamState (param, isDynamic) {
    // Get the current internal state object
    var current = $state.current.$$state();
			
    // Search for the given param on the current state, or it's parents
    while (current) {
        var paramDefinition = current.params[param];
				
       // Given param not found on current state params
        if (paramDefinition === undefined) {
            current = current.parent;
					
            continue;
        }
			
        paramDefinition.dynamic = isDynamic;
        paramDefinition.config.dynamic = isDynamic;
			
        return true;
    }
		
    return false;
}

@inad9300
Copy link

Here there is a somewhat improved version of @MartijnWelker's code, ported to TypeScript:

import {StateService} from '@uirouter/core';

export class UiRouterService {
    public static readonly $inject = ['$state'];

    constructor(private $state: StateService) {}

    // Update the URL without page reload!
    public updateUrl(params: {[paramName: string]: string}): void {
        const paramNames = Object.keys(params);
        paramNames.forEach(paramName => this.setDynamicParamState(paramName, true));

        this.$state
            .go('.', params, {location: 'replace'})
            .then(() => paramNames.forEach(paramName => this.setDynamicParamState(paramName, false)));
    }

    private setDynamicParamState(paramName: string, isDynamic: boolean): void {
        let current = this.$state.current.$$state();
        while (current) {
            const paramDefinition = current.params[paramName];
            if (paramDefinition) {
                paramDefinition.dynamic = paramDefinition.config.dynamic = isDynamic;
                break;
            }

            if (current.parent) {
                current = current.parent;
            } else {
                throw new Error(`Parameter "${paramName}" is not defined for the current state ("${this.$state.current.$$state().name}").`);
            }
        }
    }
}

@stale
Copy link

stale bot commented Jan 24, 2020

This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs.

This does not mean that the issue is invalid. Valid issues
may be reopened.

Thank you for your contributions.

@stale stale bot added the stale label Jan 24, 2020
@stale stale bot closed this as completed Feb 7, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants