简体   繁体   中英

How to store and retrieve search parameters in angularjs?

I have an Angular app where I sometimes want to:

  • Store the currently entered form variables for the currently shown page. This can be search terms or other user input.
  • If the user goes back in history, make sure the same search parameters are used again

I have looked at ngRoute, stateProvider and html history and not found a good way to do it. It looks like something which should be fairly common, so I guess I just haven't understood how to do it.

To deal with this in our current project we declared a directive which watches the model and bounds this value to a variable in a data service. Moreover on render, this directive checks if the appropriate value is already set in the data service. If so, the model is being set to this value.

As requested in comments, my StorageService :

'use strict';

/**
 * storage wrapper for session- or local-storage operations
 */
app.factory('StorageService', function(
        $rootScope,
        $http,
        $location) {

    /**
     * get an item
     *
     * @param item - string - the item identifier
     * @return - mixed -
     */
    var get = function(item) {
        return JSON.parse(sessionStorage.getItem(item) ||localStorage.getItem(item));
    };

    /**
     * set an item
     *
     * @param item - string - the item identifier
     * @param value - mixed - the value to set
     * @param usePersistentStorage - boolean - the flag for session- or local-storage
     * @return void
     */
    var set = function(item, value, usePersistentStorage) {
        var obj = {
            value: value,
            ts: new Date().getTime()
        };
        window[usePersistentStorage ? 'localStorage' : 'sessionStorage'][value === null ? 'removeItem' : 'setItem'](item, JSON.stringify(obj));
    };

    /**
     * remove an item
     *
     * @param item - string - the item identifier
     * @return void
     */
    var remove = function(item) {
        set(item, null, true);
        set(item, null);
    };

    /**
     * clear the whole session- and local-storage
     *
     * @return void
     */
    var clear = function() {
        sessionStorage.clear();
        localStorage.clear();
    };

    /**
     * check if item has expired
     *
     * @return boolean
     */
    var checkExpiration = function(str, minutes) {
        var now = new Date(),
            nowts = now.getTime(),
            item = get(str);
        if(item && typeof item.ts != 'undefined' && (new Date(nowts) - new Date(item.ts) < minutes * 60 * 1000)) {
            return true;
        } else {
            remove(str);
            return false;
        }
    };

    return {
        get: get,
        set: set,
        remove: remove,
        clear: clear,
        checkExpiration: checkExpiration
    };
}

);

I've saved view state in the past by having a service that held it in an object.

Any reference to a field I wanted to maintain (like the sort column of a table or the value of a specific field) would be saved in the service's view state object instead of in the controller/scope.

The controller would check the 'source' or 'type' of page transfer when it initialized by looking for a specific url parameter or state (eg. #/search?type=new). If it was considered a new search, it would reset the values. Otherwise it would display and use the previously used values.

An application reload would wipe out the data in the Service, giving a brand new form.


The approach I described above is simple because angular handles the saving for you. Services are singletons, so by binding directly to the service's fields, it will survive route changes automatically.

In your view:

<input ng-model="criteria.firstName">    

In your controller initialization:

$scope.criteria = ViewStateService.criteria;

If you'd prefer saving view state only at specific points, you can set up an event handler on a page change/route change event and do a copy of the data at that point.

$scope.$on('$locationChangeStart', function(next, current) {
    //code to copy/save fields you want to the Service.
});

how to watch for a route change in angularjs

Based on what you are describing I see a few ways that this could be done.

There's LocalStorage which is supported in most all browsers, and even so there's polyfills for it. This would mean that you store using a name/value pair.

Here's a quick example:

var searchParameters = {
  filters: [
    'recent'
  ],
  searchString: 'bacon'
}

// Store the value(s)
localStorage.setItem('searchParameters', JSON.stringify(searchParameters));

// Retrieve the value(s)
searchParameters = JSON.parse(localStorage.getItem('searchParameters'));

// Delete the value(s)
localStorage.removeItem('searchParameters');

Depending on your flow you could make use of the browser history stack. So if someone searches for bacon then you could send them to the page with ?query=bacon appended to it. This would allow you to easily maintain the history and allow easy use of the back button. At the end of the day, it all really comes down to how your application is setup as to what is the best choice. There's also other ways that this could be accomplished depending on requirements. For example, if you need to synchronize across devices a server side component would need to be implemented to store the values and retrieve them.

There are several methods and two seemed to be quite reliable. I ended up choosing the first approach for my app since my search parameters needed to propagate to other controllers.

Storing them in cookies

Angular.js offers $cookies that allows to get/set parameters on the browser. I use this as true source of search params.

For example:

search.service.js

angular
    .module('app')
    .service('SearchService', SearchService);


    SearchService.$inject = [
        '$cookies'
    ];

    function SearchService(
        $cookies
    ) {

    var searchCookieKey = 'searchHistoryCookieKey';
    var searchCookieMaxSize = 10;

    return {
        search: search,
        getSearchHistory: getSearchHistory
    };

    function search(arg1, arg2) {
        storeSearchHistory({arg1: arg1, arg2: arg2});

        // do your search here
        // also, you should cache your search so 
        // when you use the 'most recent' params
        // then it won't create another network request 
    }

    // Store search params in cookies
    function storeSearchHistory(params) {
        var history = getSearchHistory();
        history.unshift(params); // first one is most recent
        if(history.length > searchCookieMaxSize) {
            history.pop();
        }
        $cookies.putObject(searchCookieKey, history);
    }

    // Get recent history from cookies
    function getSearchHistory() {
        return $cookies.getObject(searchCookieKey) || [];
    }

}

app.states.js

   .state('search', {
        url: "/search",
        templateUrl: "/dashboard/search/templates/index.html",
        controller: 'SearchController',
        resolve: {
            searchResults: ['SearchService', '$stateParams', function(SearchService, $stateParams) {
                if(!$stateParams.arg1 || !$stateParams.arg2) {
                    var history = SearchService.getSearchHistory();
                    var mostRecent = history.length ? history[0] : null;
                    if(mostRecent) {
                        return SearchService.search(mostRecent.arg1, mostRecent.arg2);
                    }
                }
                return SearchService.search($stateParams.arg1, $stateParams.arg2);
            }]
        }
    })

If you are not caching these search network calls, then your app will need to wait for the request to return, thus slowing down your app.

Passing them around in states

You can create a parent controller which holds your $stateParams and your child controllers will inherit the parameters. The parameters are not overwritten when going back/forward or accessing between child states. However, they are overwritten when you move states with specific params. So, you will just need to explicity specify those parent's $stateParams when moving between states.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM