Introducing the Angular 2 Explorer for Wijmo 5

by Alex Ivanenko, Program Manager of Wijmo

We've been working closely with Angular 2 and are excited to share our progress. We're hoping our new Explorer will help you learn Angular 2 concepts in general and understand how we plan to integrate. Before we dive in, here are our plans with Angular 2:

Why Angular 2?

Angular 1 has been wildly successful for Google and for Wijmo. So when Angular 2 came around, of course we took notice. Angular 2 is quite different from Angular 1. Here are some key reasons why you might want to consider Angular 2:

Our Angular 2 Proof of Concept

GrapeCity Wijmo Angular 2 Explorer

We're happy to launch our first set of components representing Wijmo controls in the Angular 2 environment. Our Explorer sample is a full-blown Angular 2 Single Page Application (SPA) with routing, custom services and pipes. As you probably already guessed, the Explorer uses Wijmo components for Angular 2.

The Explorer sample for Angular 2 resembles its Angular 1 counterpart (shipped as a part of the Wijmo library distribution). Comparing its implementation with its Angular 1 version answers some key questions:

Notes on the sample and versions:

Let's consider key parts of the Explorer for Angular 2 vs. its Angular 1 counterpart.

The Angular 2 Application

In Angular 1 version of Explorer, we added ng-app=app directive to the root element of the default.htm page, and registered the root app application module in the scripts/app.js file. For the navigation parts of default.htm, we also included ng-controller=navigationCtrl directive that references a controller implemented in the scripts/controllers/navigationCtrl.js file.

Let's look at how it's done in its Angular 2 counterpart.

The default.htm page's body looks like:

<body> 
       <app-cmp></app-cmp> 
</body>

The <app-cmp> tag represents a root application component; this is the whole application. All the sample life happens inside this component, and default.htm is used solely to add this component and bootstrap the application this way. In Angular 2, everything is a component, be it an application itself, or its parts (like a current SPA view or component representing a Wijmo control). Think of a component as of a single unit which is a model, controller and a view in one entity.

Think of a component as of a single unit which is a model, controller and a view in one entity.

The component comprises two core parts:

The component represented by the <app-cmp> tag is implemented in the src/app.ts file, and its view is in the sibling src/app.html file.

Let's learn some key parts of this implementation. The <app-cmp> differs from other components in the sample; it represents a whole application, so besides the standard component functionality to provide a view and its code-behind, it has to implement the following additional features:

The src/app.ts file defines the AppCmp TypeScript class. The @Component decorator has the selector: 'app-cmp' definition, which maps the<app-cmp> tag to the AppCmp class. When Angular runtime meets this tag, it will create an instance of this class.

Routing

The AppCmp class has also a @RouteConfig decorator. This is where we define the application routing rules, and it's an analogue of the $routeProvider.when(…).when(…)… code in the app.js file of the Angular 1 Explorer. In Angular 1, each route item defines a virtual path exposed in browser's address field, URL of HTML file (a view) that will be shown in the tag marked by ng-view directive, and a controller that will back this html file.

Let's look at route item definition example in the Angular 2 application:

 { path: '/input/listbox', component: ListBoxCmp, as: 'InputListBox' },

Each route also defines a virtual path and… a component! Yes, the target of Angular 2 navigation is a component (the ListBoxCmp component in this example), but not an arbitrary html file. When the user clicks a link associated with a certain route item, Angular 2:

Navigation UI

The view of the AppCmp component is defined in the src/app.html file. The AppCmp class has the @View decorator with the templateUrl: src/app.html property, which defines a path to the component's view file.

Let's look into this html file. As you see, its content looks similar to the content of the default.htm file from the Angular 1 Explorer.

At the bottom of the file you may find two interesting lines of markup:

<a [routerLink]="['/' + link.alias]">{{ link.text }}</a>

Its Angular 1 counterpart:

<a ng-href="{{ link.url }}">{{ link.text }}</a>

In Angular 2, we use [routerLink] attribute directive instead of the ng-href directive in Angular 1. This directive should be assigned to route name (alias) instead of the route virtual path as we did in in Angular 1. In the route definition example above, such an alias is specified using the as: InputListBox property.

The other important piece of markup is the tag where Angular will add current component's view. We marked this tag with ng-view directive in Angular 1 Explorer. In Angular 2, it's the <router-outlet></router-outlet> tag that you may find in the bottom of the app.html file.

Bootstrap

The last line of the app.ts file looks like:

bootstrap(explorer.AppCmp, [
  ROUTER_PROVIDERS,
  provide(LocationStrategy, { useClass: HashLocationStrategy }),
  MenuSvc,
  DataSvc,
  SparkSvc
]); 

This call to the Angular bootstrap method creates our AppCmp component instance and initializes it as an application root component. In other words: it creates and runs the application.

We also pass some additional parameters here:

To get an instance of a certain service in a component implementation, we should just add a parameter of the service type to the component constructor, and Angular will pass an instance of the wanted service to this parameter. We don't need to create it by ourselves.

For example, the constructor signature of the ListBoxCmp component class looks like this:

constructor( @Inject(DataSvc) dataSvc: DataSvc) 

It defines the parameter of the DataSvc service type that receives an instance of this service, which will be available for usage right in the constructor code. It's worth mentioning here that services are implemented as regular TypeScript classes.

Folder structure

The Explorer sample consists of the following folders:

GrapeCity Wijmo Angular 2 Explorer Folder Structure

Components for Wijmo controls

The components for Wijmo controls are implemented in the scripts/wijmo.angular2 folder. A set of components for each Wijmo module is implemented in a separate external TypeScript module. (For example, wijmo.angular2.input.ts and wijmo.angular2.grid.ts.)

For convenience, we also include the wijmo.angular2.all.ts module, which exports all the rest of the Wijmo component modules. If you plan to write Angular 2 components that use Wijmo components, we recommend you import wijmo.angular2.all.ts since it includes imports for all of the Wijmo components.

It exports a single symbol for each component module, and the symbol name is prefixed with "wjNg2" followed by a module name. For example: wjNg2Input and wjNg2Grid for modules containing Input and FlexGrid control components, respectively.

Here's an example of importing symbols from this module:

import { wjNg2Input, wjNg2Grid } from '../../../scripts/wijmo.angular2/wijmo.angular2.all';

And you can reference component classes using expressions like wjNg2Input.WjMenu and wjNg2Grid.WjFlexGrid.

The usage of Angular 2 components for Wijmo controls in HTML markup is virtually the same as for their Angular 1 counterparts. For example, this markup creates Wijmo Menu with items:

<wj-menu [(value)]="itemCount" 
       [header]="'Items'"
       (itemClicked)="menuItemClicked(menu3, $event)"> 
       <wj-menu-item [value]="5">5</wj-menu-item> 
       <wj-menu-item [value]="50">50</wj-menu-item> 
       <wj-menu-item [value]="500">500</wj-menu-item> 
</wj-menu> 

The only difference with Angular 1 is property binding syntax. For Angular 2, you'll need to wrap the property name in brackets as follows:

Components for Wijmo controls are derived directly from control classes they represent. The definition of component class representing InputNumber control looks like:

export class WjInputNumber extends wijmo.input.InputNumber

So if you obtained a reference to control component instance, you automatically have a reference to the control itself and can use its API. The reference to the component can be obtained in a usual way via the local template variable. For example, this markup will provide the "flex" variable that references FlexGrid instance:

Implementing a Specific Component

Let's consider some interesting details of implementation of a specific component.

Single controller for multiple views

In Angular 1, a developer has full freedom to define views and controllers separately and combine them in an arbitrary manner. The developer may proclaim any part of an HTML page as bound to a specific controller, and even use the same controller in multiple HTML pages. Angular 1 Explorer extensively utilizes this capability. For example, the sample defines the basicCtrl controller used in many FlexGrid example pages (like intro.htm, grouping.htm and paging.htm).

In Angular 2 this seems impossible, as a component defines both a model/controller logic and a view as a single unit. You can't define them separately and combine as you need. How do we manage this challenge?

The answer is very simple: utilize TypeScript class inheritance capability. We derive classes in order to implement specific views in them, while use shared model/controller logic implemented in the base class. So, multiple views provided by multiple derived classes inherit the same model/controller implementation from the base class.

Let's look at the class definitions of the following components representing FlexGrid examples:

export class GridIntroCmp extends GridBaseCmp
export class GridGroupingCmp extends GridBaseCmp

As you see, both component classes representing Grid Introduction and Grouping examples are derived from the single GridBaseCmp class.

The base GridBaseCmp class:

Once we have the base, we can derive a specific component class (like GridGroupingCmp), but all the controller behavior and API is automatically derived from the base GridBaseCmp class. This is essentially the same as a single controller in multiple views in Angular 1.

Getting a reference to control from a component

Sometimes you may need to refer to a control defined in the component's view. In Angular 1, you retrieved it by binding the Wijmo directive's control proptery to a controller property. In Angular 2, you'll need to add a "local template variable" to the control's component element in the view.

For example, in the GridIntroCMP component's view, you may define the "flex" template variable for FlexGrid:

In the component code, you may declare the following variable that automatically receives a reference to the FlexGrid instance with the #flex variable defined in markup:

// references FlexGrid named 'flex' in the view
@ViewChild('flex') flex: wijmo.grid.FlexGrid;

Note that we have this declaration in the GridBaseCmp class (a base class for GridIntroCmp), but it will still receive a reference defined in the GridIntroCmp view.

Respond to component property changes

In Angular 1, if you need to perform some actions on some controller property change, you subscribe a callback using the $scope.$watch function, which is called by Angular when the property value changes. In Angular 2, we simply declare a true ES5 property with getter and setter and perform necessary actions in the property setter.

For example, this declaration defines the dataMaps; property in the GridBaseCmp class, and calls the _updateDataMaps method on property change:

get dataMaps(): boolean {
	return this._dataMaps;
}
set dataMaps(value: boolean) {
	if (this._dataMaps != value) {
		this._dataMaps = value;
		this._updateDataMaps();
	}
}

Another way to act on property changes is to add a special onChanges method to the component class. Angular will automatically call the method on each property change.

Services

The service is just an arbitrary class that exposes some methods or properties. It should be marked with the @Injectable() decorator so that Angular is able to inject it in your components.

For example, the definition of the DataSvc service looks like this:

@Injectable()
export class DataSvc {

To inject it into a component, you specify it as the component constructor parameter with the @Inject decorator:

constructor( @Inject(DataSvc) dataSvc: DataSvc) {

Pipes

Pipe is an analogue of Angular 1 Filter. A pipe is implemented as a class with the @Pipe decorator that exposes special transform method.

For example, here's the implementation of the "glbz" pipe, an analog of Angular 1 Explorer's glbz filter:

@Pipe({
  name: 'glbz',
  // stateful pipe
  pure: false
})
export class GlbzPipe {
  transform(value: any, args: string[]): any {
  	return wijmo.Globalize.format(value, args[0]);
  }
}

And its usage:

{{passengers | glbz:'n0'}}

Next Steps

We're still moving quickly alongside the Angular 2 team to make sure we have components to ship when Angular 2 releases.

Our next release will be a stable Beta version for all of the Wijmo Angular 2 components. Look for this to release in early March 2016.

After that, we'll keep refining our components and have them ready for production when Angular 2 officially releases.

Most important, we need your help. Please take the time to look at our current Angular 2 Explorer and give feedback. Once we ship our components, we'll want you to use them in your Angular 2 applications and report issues/requests.

Download our Angular 2 Proof of Concept

Of course, if you want to use Wijmo in production today, you can download Wijmo 5 and use it in PureJS form or with Angular 1.

This article was originally published at Wijmo.Com.