Friday, October 25, 2013

Really Stupid Routing Parameters in Angular.dart


Whenever something is really, really hard to do in a library, I am almost certainly doing something really stupid. I love when that happens.

I love it because there is almost certainly something fundamental that I am missing. Something that the library wants me to do in a certain way. And if I can figure out that way, then learning starts. More importantly, the library starts teaching me why things are done that way. It's the why that I really love. The day that I learn why a framework has me do something a particular way is always a good day. Today is not one of those days. Today, I struggle through a really dumb solution—to Angular.dart routing.

I have routing working with the master branch of Angular.dart (which is the same as version 0.0.5). The default route in my calendar application points to a controller-backed list view of all appointments. A separate route points to a controller-backed individual appointment detail view. The routing works fine. The controllers attached to the two views work fine.

The problem is that I cannot figure out how to get the dayId parameter in the route sent to the appointment detail controller. From last night, routing in in Angular.dart is currently implemented by the route_hierarchical package. In my calendar application, this looks like:
class CalendarRouter implements RouteInitializer {
  void init(Router router, ViewFactory view) {
    router.root
      ..addRoute(
          defaultRoute: true,
          name: 'day-list',
          enter: view('partials/day_list.html')
        )
      ..addRoute(
          name: 'day-view',
          path: '/days/:dayId',
          enter: view('partials/day_view.html')
        );
  }
}
By virtue of the section route, I ought to be able to access /days/42 URLs and the detail view controller should know to lookup record ID=42 from the backend via the dayId parameter.

Now, I know that there must be a very easy way to do this in Angular.dart, but after some time of searching and injecting and searching and injecting some more, I have come up empty. I know that, ultimately, I need a Route object from the route_hierarchical package so that I can get access to the parameters property. But I do not see how to get that object in the controller.

From the route_hierarchical documentation, I know that the enter named parameter to the addRoute() is a function that receives a RouteEvent object. The RouteEvent object has a route property. Perhaps that can help...

The view() that is currently being assigned to the enter named parameter is returning a function that takes RouteEvent object as an argument. I still need to call that to get my Angular.dart routing working properly, but I also need to get that route. This winds up fitting the bill:
class CalendarRouter implements RouteInitializer {
  void init(Router router, ViewFactory view) {
    router.root
      // ...
      ..addRoute(
          name: 'day-view',
          path: '/days/:dayId',
          enter: (RouteEvent e) {
            print(e.route.parameters);
            return view('partials/day_view.html')(e);
          }
        );
  }
}
Now when I access the day view route, I get the parameters dumped to the console:
{dayId: a7380ec0-17e0-11e3-a775-774b90ad8e75}
That is only part of the battle. I have the dayId paramter in the route, but I need to get it to the controller somehow.

And the best that I can think to do in this case is to inject scope into my router and then dump the parameters into scope:
class CalendarRouter implements RouteInitializer {
  Scope _scope;
  CalendarRouter(this._scope);

  void init(Router router, ViewFactory view) {
    router.root
      // ...
      ..addRoute(
          name: 'day-view',
          path: '/days/:dayId',
          enter: (RouteEvent e) {
            e.route.parameters.forEach((k,v) { _scope[k] = v; });
            return view('partials/day_view.html')(e);
          }
        );
  }
}
After injecting scope into the day view controller, I finally have access to that parameter:
@NgDirective(
  selector: '[day-view-controller]',
  publishAs: 'appt'
)
class DayViewController {
  // ...
  DayViewController(Scope scope) {
    print('dayId: ${scope["dayId"]}');
  }
}
And, when I load the page, I see my UUID dayId value logged to the console:
dayId: a7380ec0-17e0-11e3-a775-774b90ad8e75
So I have routing parameters working. But communicating through scope feels wrong. In plain-old AngularJS, it is possible to inject route parameters into objects. Angular.dart must have something similar—or some alternate approach that I am missing. Or perhaps it is still a feature in need of development?

Regardless, I have a really dumb routing parameters solution. And the promise of something better in the near future.

Day #915

2 comments:

  1. Looks like not so stupid but not so good. Did you see https://github.com/angular/angular.dart.tutorial/tree/master/Chapter_05?
    It seems you need:
    1. bind route to view: https://github.com/angular/angular.dart.tutorial/blob/master/Chapter_05/view/view.html#L1
    2. inject RouteProvider: https://github.com/angular/angular.dart.tutorial/blob/master/Chapter_05/view/view_recipe_component.dart#L21
    Then you can use routeProvider.route.parameters.

    Don't like the need to bind route to a view in html. Such binding was already defined in *RouteInitializer. It looks like a bad responsibility for a template.

    ReplyDelete
    Replies
    1. Thank you so much! I had not seen that and will give it a try today.

      I think I agree with the dislike of binding routes in the view. Based on the discussion in the [RouteProvider](http://ci.angularjs.org/view/Dart/job/angular.dart-master/javadoc/angular.routing/RouteProvider.html), I thought something like this was needed. I was ultimately unable to figure out how to connect everything (the tutorial explains it nicely though), but even as I was getting close, it just felt like too much work.

      Anyhow, I'll give it a try tonight and see how I like it. Much thanks for the pointers!

      Delete