Monday, March 5, 2012

Simple PushState in Dart

‹prev | My Chain | next›

Tonight, I try to figure out push state in Dart. Any client-side MVC framework worth its salt needs push state routing and, dammit, Hipster MVC is worth salt.

So I start with an empty page that loads in a Dart script:
<html>
<head>
  <title>Hipster Push State</title>
  <script type="application/dart"
     src="main.dart"></script>

  <script type="text/javascript">
    // start Dart
    navigator.webkitStartDart();
  </script>
</head>

<body>
<h1>Push that state!</h1>

</body>
</html>
In that main.dart entry point, I define render() and template() functions:
render(num) {
  var el = document.query('body');
  el.innerHTML = template(num);
}

template(num) {
  return """
<h1>$num</h1>
<p>Now we're on page <b>$num</b>.</p>""";
}
The render() function draws the result of the template in the page so that render('One'); results in:


Next, I define a method that will navigate to a particular page after a given number of seconds:
navAfter(page_number, delay) {
  window.setTimeout(() {
    render(page_number);
  }, delay * 1000);
}
If I then define main() to be:
main() {
  navAfter('One', 1);
  navAfter('Two', 2);
  navAfter('Three', 3);
}
Then, I see page "One", page "Two" and finally page "Three". But the URL remains the same throughout and, if I click back, then I leave this sample app entirely.

So, instead of rendering the page when I nav-after, I route to the correct page:
navAfter(page_number, delay) {
  window.setTimeout(() {
    route(page_number);
  }, delay * 1000);
}
This route function then renders the appropriate HTML snippet, but it is also responsible for pushing the current state into History:
route(num) {
  window.history.pushState(null, num, window.location.pathname + "#$num");
  render(num);
}
With that, I have three different pages in history, but, when I click back to page "Two", I still see page three rendered:


To fix that, I need to start a history listener at the end of my main() entry point:
main() {
  navAfter('One', 1);
  navAfter('Two', 2);
  navAfter('Three', 3);

  startHistory();
}
That history listener is responsible for listening to the window's popState events:
startHistory() {
  window.
    on.
    popState.
    add((event) {
      var page_num = window.location.hash.replaceFirst('#', '');
      print(page_num);
      render(page_num);
    });
}
With that, I have the proper page displaying for the current route:


This is not quite ready to be popped into Hipster MVC, but it was a nice way to ease into it.


Day #316

No comments:

Post a Comment