Thursday, October 17, 2013

Teasing Angular.dart Directive Support


Last night I got the simplest AngularJS application working with Angular.dart, the Dart port of Angular. Tonight I would like to take the next step: building a simple, browser-only application.

Rather than follow along with the venerable TODO tutorial, I am going to recreate the calendar application that Nick and I used in much of Recipes with Backbone. I like calendars because their different views provide more grist for exploring.

That said, my initial implementation is not going to stray much from the TODO tutorial—I will list appointments for today and add the ability to create new appointments. At least that is the hope.

I start with the basic skeleton HTML:
<!doctype html>
<html ng-app>
  <head>
    <title>Angular Calendar</title>
    <script type="application/dart" src="calendar.dart"></script>
    <script src="packages/browser/dart.js"></script>
  </head>
  <body><!-- Awesome Angular stuff here --></body>
</html>
I do the usual Angular thing, declaring the entire page as an Angular app with ng-app. Then I pull in the application code, which will reside in calendar.dart, as well as Dart's usual VM bootstrap script.

As for “Awesome Angular stuff here” in the <body>, I borrow heavily from the TODO application:
    <div ng-controller="AppointmentCtrl">
      <ul class="unstyled">
        <li ng-repeat="appt in appointments">
          {{appt.time}} {{appt.title}}
        </li>
      </ul>
      <form ng-submit="addAppointment()" class="form-inline">
        <input type="text" ng-model="appointmentText" size="30"
               placeholder="15:00 Learn Dart">
        <input class="btn-primary" type="submit" value="add">
      </form>
    </div>
With the three ng directives, I declare that this section is controlled by the AppointmentCtrl controller, that the controller has an appointments getter that lists appointments (time and title), and that submitting the form will invoke the addAppointment() method. I am not 100% certain that I have those correct, but hopefully they will connect properly when I define the AppointmentCtrl class.

In fact, it does not work. Neither the ng-controller nor the ng-submit directives are implemented in Angular.dart yet. Bother. But workarounds exist...

In regular AngularJS, I would create an AppointmentCtrl function that accepts a single $scope parameter on which I can define properties that match the ng directives. That will not work in the Dart version. First, I need a main() entry point in the referenced calendar.dart file:
import 'package:angular/angular.dart';
main() {
  var module = new AngularModule()
    ..type(AppointmentCtrl);
  bootstrapAngular([module]);
}
As I found last night, I need to invoke Angular's bootstrapAngular() top-level directive (n.b. this has already changed the main repository to ngBootstrap()). To include my target AppointmentCtrl class, I call the type() method on the module (I am unsure of the semantics for that).

With that, it is time to declare the AppointmentCtrl class:
class AppointmentCtrl {
  AppointmentCtrl(Scope scope) {
    scope
      ..['appointments'] = [{'time': '08:00', 'title': 'Wake Up'}]
      ..['addAppointment'] = (){print('yo');};
  }
}
Similar to the JavaScript version, the main entry point (here a constructor instead of a function) receives the Angular scope as the single argument. On the scope, I define the list of appointments and a function to handle the addAppointments() ng-submit call (which is not yet implemented in Angular.dart).

But, when I load the page, I am not seeing the single appointment that I have created. I do not see any appointments. As I mentioned, that ng-controller directive is also not yet implemented. But there is a workaround. Angular.dart includes an NgDirective that can describe an element selector:
@NgDirective(
  selector: '[appt-controller]'
)
class AppointmentCtrl {
  AppointmentCtrl(Scope scope) {
    scope
      ..['appointments'] = [{'time': '08:00', 'title': 'Wake Up'}]
      ..['addAppointment'] = (){print('yo');};
  }
}
Back in the HTML, I remove the ng-controller directive and instead add the selector to the container <div>:
    <div appt-controller> <!-- ng-controller="AppointmentCtrl" -->
      <ul class="unstyled">
        <li ng-repeat="appt in appointments">
          {{appt.time}} {{appt.title}}
        </li>
      </ul>
      <!-- ... -->
    </div>
With that, I have my list of one calendar appointments added to today's calendar:



I think that I will take that one small victory and call it a night here. I still need to figure out how to support ng-submit without ng-submit. There do seem to be some hints in the Angular.dart demo code, so I will pick up with those tomorrow.


Day #907

4 comments:

  1. Actually, controllers were implemented, but then removed:

    Instead, we were thinking of something like:

    @NgDirective(
    selector: '[appt]',
    publishAs: 'ctrl'
    )
    class Appointment {
    List appointments = [{'time': '08:00', 'title': 'Wake Up'}];
    var addAppointment = () => print('yo');
    }


    and in your HTML say "appt in ctrl.appointments"

    ReplyDelete
    Replies
    1. Thanks! I think I like the annotation approach. I'll play with it in a day or two to get a better feel for it.

      Delete
    2. Added again on October 17? :) https://github.com/angular/angular.dart/commit/d9febcd9b2608dd290308913ce6fcd4a6a75b32d#diff-2edae11fa1c89c32c01361f4a163e2f1

      Delete
  2. This comment has been removed by the author.

    ReplyDelete