route.dart icon indicating copy to clipboard operation
route.dart copied to clipboard

`Route.parameters` should parse path and query string

Open antonmoiseev opened this issue 10 years ago • 6 comments

Not sure whether this feature should be discussed here or in angular.dart repository, but I would expect this implemented at this level.

I was thinking RouteProvider.parameters property behaves the same way as AngularJS $routeParams service does - it should return both search and path params. It is the case when we navigate to the page using Router.go() method:

_router.go('product.search', {
  'search.title': 'Product1',
  'search.category': 'online'
});

It results to the URL like this:

http://localhost:8080/products?search.title=Product1&search.category=online

Notice the unfortunate way parameters displayed in the address bar with prefixes, but this is filed as a separate issue.

And when we ask RouteProvider to return the parameters map we get all the parameters we passed above:

print(_routeProvider.parameters);
// {title: Product1, category: online}
// Notice, no prefixes here

But if we refresh the page with the same URL, _routeProvider.parameters returns an empty map.

Currently to handle both cases (opening page with Router.go() and directly typing URL in the address bar), we need to determine which source of parameters to use, parse query string params Uri.splitQueryString(window.location.search.substring(1), handle the case when search is empty, remove params prefixes, etc., and still we potentially have two sources of truth.

I would love to see this logic provided OOTB. Do you think this should be implemented for the Route, for the RouteProvider, as a separate service, or you think it shouldn't be implemented in a library at all?

antonmoiseev avatar Sep 23 '14 16:09 antonmoiseev

+1. It seems like query parameters are 2nd class citizens in route.dart. Here's a routing set up that I think is typical:

/widgets → list a collection of widgets
/widgets/:id → view a specific widget

Then one day we realize we have so many widgets that we can't display them all at once! We can't add a route /widgets/:page because that would conflict with /widgets/:id. IMO, the query string is conceptually a good place to add filters or optional arguments for a resource. Unfortunately, /widgets?page=2 doesn't "just work" in route.dart.

Angular apparently wants us to do this instead:

/widgets → list a collection of widgets
/widgets/page/:page → list a subset of all widgets
/widgets/:id → view a specific widget

Hopefully you never have a widget for which the id is page! If you did, you would need to restructure your widget view URLs and break any bookmarks to the old URLs.

(Edit: scratch what I said below. It goes haywire if you click back and I've spent hours fiddling with it and can't get it the forward and back buttons to work correctly.)

~~I decided to do as @antonmoiseev did: parse the query string myself and then call go() again. I wrapped it up in a preEnter delegate:~~

import 'dart:async';
import 'dart:html';

import 'package:angular/angular.dart';

/// Parse URL query parameters and tell the router about them.
class ParseQueryParams {
    Router _r;

    /// Constructor.
    ParseQueryParams(this._r);

    /// Implementation.
    void call(RouteRouteEnterEvent e) {
        if (window.location.search.length > 1 && e.queryParameters.length == 0) {
            Map<String,String> qp = Uri.parse(window.location.href)
                                       .queryParameters;

            e.allowEnter(new Future.value(false));

            this._r.go(e.route.name, e.parameters, queryParameters:qp);
        } else {
            e.allowEnter(new Future.value(true));
        }
    }
}

~~Hope this helps somebody else... I spent almost 2 days working on query arguments in route.dart! Any feedback/corrections/etc. is appreciated.~~

mehaase avatar Mar 16 '15 21:03 mehaase

Is there no way to work with query string parameters at all in AngularDart? If not, this seems like a pretty serious shortcoming, no?

ianfp avatar Mar 31 '15 22:03 ianfp

@ianfp There is extremely limited support for query parameters, but it's so broken that it's not practical for any real use. I have a PR that would fix it but Angular.dart projects all pretty much died soon after they released 1.0. I'm guessing there won't be any more merges for 1.x, and we'll all have to wait for Angular 2.0.

mehaase avatar Mar 31 '15 22:03 mehaase

Haven't had a chance to look at Mark's PR, but we have a helper service to work with route params. One of the method's returns query string params for a given Route. It combines Route.queryParameters with window.location.search:

@Injectable()
class RouteParamsService {

  Map<String, String> getQueryParamsFor(Route route) {
    final queryParams = route.queryParameters;
    final search = window.location.search;
    final params = search.isNotEmpty ? Uri.splitQueryString(search.substring(1))
        : {};

    return params.keys.fold(new Map.from(queryParams), (accum, paramName) {
      accum.putIfAbsent(paramName, () => params[paramName]);
      return accum;
    });
  }
}

antonmoiseev avatar Mar 31 '15 23:03 antonmoiseev

Actually, all I needed was route.queryParameters! Thanks, @antonmoiseev.

ianfp avatar Mar 31 '15 23:03 ianfp

@ianfp, np. The issue with route.queryParameters though is when you refresh the page they are empty even if there are parameters in the query string. This property returns parameters only if the page was opened with router.go() method and you explicitly passed parameters as an optional argument. That's why we have to use this helper function.

antonmoiseev avatar Apr 01 '15 16:04 antonmoiseev