Pages

Let's start Dart with brilliant WebStorm10

Friday, April 3, 2015
WebStorm 10 has been released! This is the most important update for Dart users since Dart Analysis Server has been integrated into WebStorm.
Here is the quick start guide to set up WebStorm 10 and latest Dart ver 1.9.1.

Install Dart and Dartium

Assuming you are using Mac, the easiest way is to install using Homebrew.

Homebrew

If you have not installed Homebrew yet, go http://brew.sh/ and and install it.

Install Dart

$ brew tap dart-lang/dart
$ brew install dart
You will see latest Dart(1.9.1) installed in /usr/local/bin/, yay.

Install Dartium

$ brew install dartium
That's it.

Install WebStorm 10

Go to https://www.jetbrains.com/webstorm/ and download and install it.
Note, WebStorm is paid app with free 30-day trial. I'm pretty sure it's worth paying. If you don't want to pay, you can also try InteliJ IDEA community edition with Dart plugin for free.

Dart project setting.

Ensure Dart is correctly set in WebStorm as follows.

Workflow

Create a Dart project (File -> New Project -> Dart), I recommend "Web Application" template, then you will see initial project files.

pub get

Do "pub get" by right click on pubspec.yaml.

pub serve

Open "Terminal" by clicking "Terminal" button in WebStorm and You will see the root directory of the preject, then type
$ pub serve

Open Dartium

In another terminal window, type
$ dartium --checked
Go the site (http://localhost:8080 by default) to see the content.
See https://www.dartlang.org/tools/dartium/

Debugging

To debug Dart project on WebStorm IDE you have to set up a few things as follows.

Install JetBrains IDE Support Chrome Extension

Open https://chrome.google.com/webstore/detail/jetbrains-ide-support/hmhgeddbohgjknpmjagkdomcpobmllji?hl=en in Dartium, and install the extension.

Set Up Debugger for Dartium

Select "Run -> Edit Configurations...", then set new "JavaScript Debug" as follows.

Open Dartium window with WebStorm debugging.

Select "Run -> Debug"
Then you should be able to use the debugger. You will see "JetBrains IDE Support is debuggeng this tab" message above the Dartium window. You can see stack trace and logs on the console, set break points, step over, step into, etc...

Live Analysis

Dart finally releases Dart Analysis Server and WebStorm 10 has already integrated it. Click "Dart Analysis" button and you will see on-the-fly code analysis result automagically. This is huge improvement!

Dash plugin

WebStorm has Dash plugin to open definition in Dash instantly. Install the plugin via Preferences -> Plugins. Then Tools -> Search in Dash to open definiton in Dash. You can also set shortcut. Default: cmd+shift+D.
https://itunes.apple.com/us/app/dash-api-docs-snippets/id458034879?mt=12
Note, you can also open docs in https://api.dartlang.org/ by shift+F1.



WebStorm becomes the best Dart development IDE. It's good time to start Dart development with latest Dart(1.9.1) and WebStorm 10. Let's have fun!

Ref

http://news.dartlang.org/2015/03/live-analysis-results-with-webstorm-10.html
https://www.dartlang.org/tools/download.html
https://www.jetbrains.com/webstorm/help/running-and-debugging-dart.html#d154619e283
Read more ...

Exploring Latest PolymerDart SPA using Custom Elements

Thursday, April 2, 2015

Recently, PolymerDart team has released its version 0.1.6, and they have published another sample: the SPA example. It is based on PolymerJS SPA Example, and it is pretty nice starting point to build practical SPA in Dart. So I have been exploring the sample to understand the architecture and found some parts of what I wanted to add and modify to make it a bit more practical for me. I am not sure it is the right way, but try to share what I have done here.

Add custom page element for every page.

The original sample only shows content of every page name by <div>{{page.name}}</div> in the main pane. For real app I want to use custom element to handle the entire page content.

There are five pages in the sample(#one, #two, #three, #four, #five), so I also create five corresponding custom elements(one-page, two-page, three-page, four-page, five-page).

lib/src/elements.dart

// Page elements
export 'package:polymer_spa_example/one_page.dart';
export 'package:polymer_spa_example/two_page.dart';
export 'package:polymer_spa_example/three_page.dart';
export 'package:polymer_spa_example/four_page.dart';
export 'package:polymer_spa_example/five_page.dart';

lib/one_page.dart

@HtmlImport('src/one_page.html')
library one_page;

import 'package:polymer/polymer.dart';

@CustomTag('one-page')
class OnePage extends PolymerElement {

  OnePage.created() : super.created();

}

lib/src/one_page.html

<polymer-element name="one-page" layout vertical center-center fit>
  <template>
    <link rel="stylesheet" href="one_page.css">
    <div>Single</div>
  </template>
</polymer-element>

Of course we will need to consider directory structure for custom elements when a project code grows.

Insert custom element at route change.

Then I try to insert a page element whenever user enters a new route. And make sure contents are cleared before the insertion.

lib/example_app.dart

/// Updates [route] whenever we enter a new route.
void enterRoute(RouteEvent e) {
  route = e.path;
  /// Ensure to clear page element, and add the page element corresponding to route.
  if (route != null && route != "") {
      corePages.querySelector('section[hash="$route"]').children
          ..clear()
          ..add(new Element.tag("${route}-page"));
  }
}

I create a convention that the custom element name must be ${route}-page (new Element.tag("${route}-page")). This is a quick hack and looks a little dirty. I modify this part in the later section.

Handling custom page elements between page transition animation.

I think there might be some edge cases, but usually in SPA, on moving to new page, the previous page content is expected to be deleted from DOM tree to lighten its load.

But simply deleting page content immediately after the page transition start works but disrupts the smooth transition animation between pages. This is bad for UX. To fix it, it needs to keep leaving page's content until the page transition animation finishes showing a new page. So I add var _previousRoute; to keep leaving page's route, and also add the code to handle corePage's onTransitionEnd event for the timing to delete. (Borrowing the idea from erikringsmuth/app-router)

void handlePageElementsOnRouteTransition() {
  // Clear previous route's content on the transition end.
  // Following app-router's idea.
  // https://github.com/erikringsmuth/app-router/blob/master/src/app-router.js
  // TODO: This doesn't work well when another transition starts before a transition ends. Needs another tweak.
  corePages.onTransitionEnd.listen((TransitionEvent e) {
    if (_previousRoute != null && _previousRoute != route) {
      corePages.querySelector('section[hash="$_previousRoute"]').children.clear();
    }
  });
}

It works.

demo

Make it more explicit.

As I said in previous section, "${route}-page" looks a little hacky. I prefer more explicit way so I add customTag attribute and Element create() => new Element.tag("$customTag"); in Page class to handle corresponding custom page element with page instance itself.

example_app.dart

/// Simple class which maps page names and custom tags to paths.
class Page {
  final String name;
  final String path;
  final String customTag;
  final bool isDefault;

  const Page(this.name, this.path, this.customTag, {this.isDefault: false});

  // Consider some conventions. For example, custom tag name is expected to be same as the name...
  Element create() => new Element.tag("$customTag");

  String toString() => '$name';
}

/// The list of pages in our app.
final List<Page> pages = const [
  const Page('Single', 'one-page', 'one-page', isDefault: true),
  const Page('page', 'two-page', 'two-page'),
  const Page('app', 'three-page', 'three-page'),
  const Page('using', 'four-page', 'four-page'),
  const Page('Polymer', 'five-page', 'five-page'),
];

/// Updates [route] whenever we enter a new route.
void enterRoute(RouteEvent e) {
  route = e.path;
  if (selectedPage == null) selectedPage = pages.firstWhere((page) => page.path == route);
  // Ensure to clear page element, and add the page element corresponding to route.
  if (route != null && route != "") {
    corePages.querySelector('section[hash="$route"]').children
      ..clear()
      ..add(selectedPage.create());
  }
}

Non hash fragment mode (HTML5-mode by Angular)

If you prefer to delete # in the url (means to change to non hash fragment mode, or 'HTML5-mode' by Angular), you can simply change the Router's useFragment option to false (new Router(useFragment: false);). Note, it needs some reverse proxy server (like Nginx) settings to handle routes.

References

You can see the whole source code on Github

Read more ...