In the last month, I've experimented with many frameworks and multiple languages to find a fitting tool for a new project.
I've played around with KotlinJS, a transpiler that compiles Kotlin to JavaScript, KotlinJS however currently compiles one-to-one to JavaScript, doesn't currently remove dead code, doesn't do any minification (which I easily resolved by adding a Google Clojure Compiler maven plugin to automatically minify everything) and it doesn't do much polyfilling, it's one-to-one. The KotlinJS experience was great, but it's not ready for prime-time yet.
ScalaJS, another transpiler which compiles to JS, my Scala skills are fairly limited and it didn't seem like a great idea learning another JVM language just for the sake of compiling to JS considering the steep learning curve with Scala. (I received a message asking why I went through all the trouble of learning KotlinJS, but not ScalaJS - answer: I already knew Kotlin and picked it up in a day, Scala feels very foreign whereas I picked up Kotlin in a day)
JavaScript with React and Angular2 ... with React, there were so many things I had to manually do to get up and running and the same goes for Angular2, although slightly less on the Angular side since the router and many other useful things just came included, but I still had to manually run my JS via grunt and hope that the minification process doesn't destroy my JS (especially on the Angular side where it's doing dependency injection using a very strange syntax), still had to manually compile my less / sass files or setup build scripts and I had little assurance that my code was type-safe in the critical parts. (I recently discovered the licensing terms for React, if at any point you are competing with facebook, your license is revoked, if at any point two companies are suing each other and both are using React, their license is also revoked - giving facebook the power over when you can and can't use the framework is definitely a no-go for me)
I've decided to give TypeScript a go with Angular2 TS, the experience was somewhat better than JS, but yet again, a lot of manual stuff that had to be done to get up and running, a package.json
file, tsconfig.json
, typings.json
, ... ugh, so much config just to get up and running with a simple hello word and docs that tell you
Scary error messages in red may appear during install. The install typically recovers from these errors and finishes successfully.
Not re-assuring at all, why are they called error messages if you are supposed to ignore them, horrible!!! :-| (somebody on Reddit linking to the article mentioned that the Angular2 CLI fixes the boilerplate problem, I haven't tried that, maybe that makes the Typescript version more bearable)
I've re-visited GWT for a bit and it was as clunky and slow as always and it seems like it's hardly maintained and used by a very small number of people. (I've revisited GWT again last month testing out Angular2-GWT and I was getting much better speeds this time, the syntax is still clunky as hell, the improvements Java8 brought along is not enough to make me switch back to it - give me KotlinGWT, then we're in business)
I've taken TeaVM for a ride, the TeaVM experience was a lot better than KotlinJS and GWT since it had dead code removal, a proper transpiler, but it seemed like a pet-project of someone which made me a bit nervous including it into something that needs to run in the enterprise. (I had a conversation with the creator of TeaVM, he's now on the KotlinJS team)
I finally went full circle back to Dart, I haven't used Angular1 Dart, so had no idea how to get started with Angular2 Dart either. Skimmed through the docs and 5 minutes later I was up and running.
A simple pubspec.yaml
file containing the following (note, a single config file):
name: projectname
version: 0.0.1
description: Project Description Goes Here
environment:
sdk: '>=1.13.0 <2.0.0'
dependencies:
angular2: 2.0.0-beta.17
bootjack: any
bootjack_datepicker: any
browser: ^0.10.0
event_bus: any
http: any
less_dart: any
dart_to_js_script_rewriter: ^1.0.1
transformers:
- angular2:
entry_points: web/main.dart
- dart_to_js_script_rewriter
- less_dart:
entry_points: [web/styles/main.less]
build_mode: dart
And voila, I had a less compiler working out of the box, fantastic minification with full Angular2 support and stuff just works.
In my main.dart
I simply have the following code to bootstrap everything:
void main() {
// bootstrap angular2
bootstrap(AppComponent, [
ROUTER_PROVIDERS,
provide(APP_BASE_HREF, useValue: '/'),
provide(LocationStrategy, useClass: HashLocationStrategy)
]);
}
Then I have an AppComponent which takes care of the routing:
@Component(
selector: 'myapp',
template: '<router-outlet></router-outlet>',
directives: const [ROUTER_DIRECTIVES]
)
@RouteConfig(const [
const Route(path: '/dashboard', component: DashboardPage, name: 'DashboardPage', useAsDefault: true),
const Route(path: '/home', component: HomePage, name: 'HomePage'),
])
class AppComponent {
String name = "Sample App";
}
Since everything is a component, even pages are components, here's my dashboard component
@Component(
selector: 'dashboard-page',
templateUrl: '../templates/dashboard-page.html',
directives: const [Navigation]
)
class DashboardPage {}
Using components are as easy as annotating with @Component
and templates can be inlined if they are small enough (like react does):
@Component(
selector: 'navigation',
template: '''
<div class="btn-group br">
<button type="button" class="btn btn-primary goto-first">
<span class="glyphicon glyphicon-fast-backward"></span>
</button>
<button type="button" class="btn btn-primary goto-prev">
<span class="glyphicon glyphicon-step-backward"></span>
</button>
<button type="button" class="btn btn-primary clear-search">
<span class="glyphicon glyphicon-refresh"></span>
</button>
<button type="button" class="btn btn-primary goto-next">
<span class="glyphicon glyphicon-step-forward"></span>
</button>
<button type="button" class="btn btn-primary goto-last">
<span class="glyphicon glyphicon-fast-forward"></span>
</button>
</div>
''')
class Navigation {
}
Want to use components inside other components? Also simple!
Let's look at my Item
component:
@Component(
selector: 'item',
template: '')
class Item implements AfterContentInit {
@Input() String type = "missing-type";
@Input() String text = "missing-text";
@Input() String classes = "";
@Input() String position = "center";
@override
ngAfterContentInit() {
}
}
Now I can use that Item
component inside another component:
<button-search>
<item type="identifier" text="Identifier"></item>
<item type="title" text="Title"></item>
<item type="province" text="Province"></item>
<item type="city" text="City"></item>
<item type="suburb" text="Suburb"></item>
<item type="town" text="Town"></item>
<item type="address" text="Address"></item>
<item type="postal" text="Postal"></item>
<item type="divider"></item>
<item type="clear" text="Clear Search"></item>
</button-search>
... and inside my ButtonSearch
component I can now get those items to render the SearchButton
component.
@Component(
selector: 'button-search'
directives: const [NgFor, NgIf, NgClass],
template: \'''
<ng-content></ng-content>
<div class="btn-group search-bar br">
<div class="input-group">
<span class="input-group-btn">
<a href="#" class="btn btn-primary qr-code" disabled="disabled">
<span class="glyphicon glyphicon-qrcode"></span>
</a>
</span>
<input class="form-control search top focus" type="search" name="search" placeholder="Search">
<div class="input-group-btn">
<button type="button" class="btn btn-primary search top" tabindex="-1">
<span class="glyphicon glyphicon-search"></span>
Search
</button>
<button type="button" class="btn btn-primary dropdown-toggle top" data-toggle="dropdown" tabindex="-1">
<span class="caret"></span>
<span class="sr-only">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu dropdown-menu-right">
<li *ngFor="let item of items" ngClass="{{item.type}}">
<a ngClass="{{item.type}}" *ngIf="item.type != 'divider'" href="#">{{item.text}}</a>
</li>
</ul>
</div>
</div>
</div>\'''
)
class ButtonSearch implements AfterContentInit {
@ContentChildren(Item) QueryList<Item> items;
@override
ngAfterContentInit() {
}
}
Angular2 Dart - batteries included, Dart polyfilling features that are not completely supported in all modern browsers (so proper cross-browser compatibility out of the box), elegant code and it just works. As far as I'm aware, things are stabilizing now, the router component might get some further rework according to sources close to the team, but otherwise now is a good time to get into Angular2 Dart. The guys on Dart's slack channel also helped me tremendously and a day later, I'm building Angular2 Dart components like a rock star.