Using Ext Scheduler with Angular 2

One of the most popular javascript frameworks nowadays is Angular. Angular gives you the ability to make HTML content dynamic by using templates, components and services. However Angular does not have a suite of high-level UI components as ExtJS does.

In this blog post we are going to create an Angular component for our Ext Scheduler, which can be used in Angular templates as <scheduler> tags. After following the steps in this blog post, you will have a basic example with interaction between Ext Scheduler and Angular. Feel free to extend the example to suite your needs.

angular2

Prerequisites

This guide uses Angular with Typescript, but you should be able to follow it for the most part even if you use Dart or JavaScript.

1. Please check https://angular.io/docs/ts/latest/quickstart.html for Angular prerequisites (at least node v5.x.x and npm 3.x.x).
2. Make sure you have typescript installed, tsc -v. Otherwise install it with npm install -g typescript.

Create Angular project

We are going to follow a few steps from Angular Quickstart to get started quickly 🙂 Please follow the steps below before continuing:

Step 1: Create and configure the project

Step 2: Our first Angular component

Step 3: Add main.ts

Step 4: Add index.html

If everything went well you should now have a project structure like this:

app
  app.component.ts
  main.ts
node_modules
  [many folders]
typings
  globals
  index.d.ts
index.html
package.json
systemjs.config.js
tsconfig.json
typings.json

If the typings folder is missing then you can run:
./node_modules/.bin/typings install

To automatically track changes in your project files you can run:
npm start
This command will start a webserver and a browser, compile changes on the fly and update the browser.

Create the Angular component

Angular components (a directive with a template) can be used as tags in templates to add functionality. We want to use Ext Scheduler in Angular and are thus going to create a component for it. It will be called SchedulerComponent and have a corresponding <scheduler> tag. Attributes in the tag will be used as settings for the scheduler and we will also enable event binding.

Start by adding a file named scheduler.component.ts to your app folder. Add the lines listed below:

import {Component} from '@angular/core';

@Component({
    selector: 'scheduler',
    template: '<div id="scheduler"></div>' 
});

export class SchedulerComponent { }

This gives us a component with the <scheduler> tag (line 4). In the end this will yield an empty div tag (line 5), to which we will render the scheduler. Now we modify app.component.ts to import SchedulerComponent:

import {Component} from '@angular/core';
import {SchedulerComponent} from './scheduler.component';

@Component({
    selector: 'my-app',
    template: <h1>{{title}}</h1>, 
    directives: [SchedulerComponent] 
}) 

export class AppComponent { title = 'Angular 2 demo'; }

An import-statement is added (line 2) to include the new component. The template is modified to include the component on page (line 8) and finally a directive is added to tell Angular which tags are valid (line 10).

Add the scheduler

Now we are going to include the required files for the scheduler and create an instance of it. Modify index.html and add the following to the <head> section to include Ext JS (you need your own license for it):

<link href="http://www.bryntum.com/examples/extjs-6.0.1/build/classic/theme-triton/resources/theme-triton-all.css" rel="stylesheet" type="text/css" />
<link href="http://www.bryntum.com/dist/scheduler-for-extjs/resources/css/sch-triton-all.css" rel="stylesheet" type="text/css" />

In scheduler.component.ts we create the scheduler, but not before the template is applied. We do not yet know where to render the scheduler since our container element <div id="scheduler"> is not yet added to the DOM. We need to hook into a suitable moment in our component life cycle. Change scheduler.component.ts to match the lines below:

import {Component, AfterContentInit} from '@angular/core';

@Component({
    selector: 'scheduler',
    template: '<div id="scheduler"></div>'
}) 

export class SchedulerComponent implements AfterContentInit {    
     ngAfterContentInit() { 
         // this is where we will create an instance of Ext Scheduler 
     } 
}

Because of TypeScript being more typesafe than JavaScript we need to declare a couple of global variables to handle the scheduler, or else we will get compilation errors. Add the following lines after the import statement:

// global variables for imported js libraries
declare var Ext:any;
declare var Sch:any;

The next step is to create an instance of the scheduler. Add the following lines to the ngAfterContentInit function:

var eventStore = Ext.create('Sch.data.EventStore', {
    model: 'Sch.model.Event',
    proxy: {
        type: 'memory'
    }
});

var resourceStore = Ext.create('Sch.data.ResourceStore')

var scheduler = Ext.create('Sch.panel.SchedulerGrid', {
    width: 1000,
    height: 600,
    renderTo: 'scheduler',
    columns: [
        {header: 'Name', sortable: true, width: 160, dataIndex: 'Name'}
    ],
    resourceStore: resourceStore,
    eventStore: eventStore
});

By now we have done most of the heavy lifting and have an empty scheduler displayed in your Angular project. But empty is kind of boring so let’s load some data.

Loading data

In a real world application we would load the data from a database, but in this sample we’ll just use dummy data from a JS file. Create a folder named data in the root of your project, add a file data.json with the following content:

{
    "events" : [
        {"Id" : "e10", "ResourceId" : "r1", "Name" : "Deal negotiation", "StartDate" : "2016-09-05", "EndDate" : "2016-09-08"},
        {"Id" : "e22", "ResourceId" : "r2", "Name" : "Attend software conference", "StartDate" : "2016-09-06", "EndDate" : "2016-09-09"},
        {"Id" : "e43", "ResourceId" : "r3", "Name" : "Visit customer", "StartDate" : "2016-09-05", "EndDate" : "2016-09-06"},
        {"Id" : "e210", "ResourceId" : "r4", "Name" : "Exhibition", "StartDate" : "2016-09-07", "EndDate" : "2016-09-11"},
        {"Id" : "e222", "ResourceId" : "r5", "Name" : "Meet customer X", "StartDate" : "2016-09-05", "EndDate" : "2016-09-06"},
        {"Id" : "e243", "ResourceId" : "r6", "Name" : "Prepare case studies", "StartDate" : "2016-09-06", "EndDate" : "2016-09-08"}
    ],

    "resources" : [
        {"Id" : "r1", "Name" : "Mike Anderson" },
        {"Id" : "r2", "Name" : "Kevin Larson" },
        {"Id" : "r3", "Name" : "Brett Hornbach" },
        {"Id" : "r4", "Name" : "Patrick Davis" },
        {"Id" : "r5", "Name" : "Jack Larson" },
        {"Id" : "r6", "Name" : "Dennis King" }
    ]
}

The file contains data for both the event-store and the resource-store. We can use Angulars http library to load it into the stores. Add two imports to schedule.component.ts below the other imports:

import {Http, HTTP_PROVIDERS} from '@angular/http';
import 'rxjs/add/operator/map';

Then add a provider to the @Component decorator:

@Component({
    selector: 'scheduler',
    template: '<div id="scheduler"></div>', 
    providers: [HTTP_PROVIDERS] 
})

Now inject the Http class into the SchedulerComponent class by adding a constructor:

export class SchedulerComponent implements AfterContentInit {

    constructor(private http:Http) { }

    ngAfterContentInit() {
        // existing code untouched...
    }
}

Finally, use http to load data.json and populate the stores by adding the following lines at the bottom of the ngAfterContentInit function:

this.http.get('data/data.json')
         .map(r => r.json())
         .subscribe(
             data => {
                 resourceStore.loadData(data.resources);
                 eventStore.proxy.data = data.events;
                 eventStore.load();
             },
             err => console.error(err)
          );

Add input property bindings

It would be nice to be able to configure the scheduler by using attributes in the <scheduler> tag, something that is called Property Binding. We will create an example binding for the title of the scheduler. This is done by adding fields to the SchedulerComponent class and decorating them with the @Input directive.

First, import the Input directive by changing the first import to:

import {Component, AfterContentInit, Input} from '@angular/core';

Secondly, add a field at the beginning of the SchedulerComponent class:

export class SchedulerComponent implements AfterContentInit {
    @Input() title:string;
    // existing code untouched

Third, alter the code that creates the scheduler in the ngAfterContentInit function:

var scheduler = Ext.create('Sch.panel.SchedulerGrid', {
    title: this.title,
    // existing code untouched
});

Finally, add a property binding to the <scheduler> tag in the template in app.component.ts:

@Component({
    selector: 'my-app',
    template: 
        <scheduler
            title="Scheduler">
        </scheduler>
        ,
    directives: [SchedulerComponent]
})

Those are the basic steps for adding property bindings. If you download and look at the demo that belongs to this blog post you can see that we have added a few other useful properties. You can find the download link at the bottom of this post.

Add output property bindings

Angular supports output of values from a component to another components template, by decorating fields in the class with the @Output directive. We can use this to display information from the scheduler. For example we can display the number of loaded events in the main template.
First, import the Output directive by changing the first import to:

import {Component, AfterContentInit, Input, Output} from '@angular/core';

Secondly, add a field at the top of the SchedulerComponent class:

@Output() eventCount:number;

Third, assign a value when the data is loaded. Alter the http call in the ngAfterContentInit function:

resourceStore.loadData(data.resources);
eventStore.proxy.data = data.events;
eventStore.load();
//new line:
this.eventCount = eventStore.getCount();

Finally, modify the main template in app.component.ts to match the following lines:

@Component({
    selector: 'my-app',
    template: 
      <h1>{{title}}</h1>
      Number of events: {{scheduler.eventCount}}
      <scheduler
            #scheduler
            title="Scheduler">
        </scheduler>
    , 
    directives: [SchedulerComponent] 
})

By specifying #scheduler on line 7 we assign a variable, called a template reference variable. This variable can be used in the template to access the output values. It is then used on line 5 to display the eventCount.

Add event bindings

Event bindings are not that different from property output bindings. We are going to add an event for when the user selects an event in the scheduler. For this we need an EventEmitter in the ScheduleComponent and then do event binding in the main template.

First, import EventEmitter by changing the first import to:

import {Component, AfterContentInit, Input, Output, EventEmitter} from '@angular/core';

Secondly, add an EventEmitter to the top of the SchedulerComponent class:

@Output() onEventSelect = new EventEmitter();

Then add a listener to the scheduler to catch the event and emit it. This listener is set in the config for the scheduler:

// last line of existing code in config object
eventStore: this.eventStore,
// add below
listeners: {
    eventselect: function(em, eventRecord) {
        this.onEventSelect.emit(eventRecord);
    },
    scope: this
}

Lines 4-9 contains the listeners configuration. Line 6 emits the event, enabling us to catch it outside the SchedulerComponent.

Now bind to the event in the main template. Alter the scheduler tag (in app.component.ts) to match the following lines:

@Component({
    selector: 'my-app',
    template: 
      <h1>{{title}}</h1>
      Selected event: {{eventTitle||'None'}}, 
      <scheduler
            #scheduler
            title="Scheduler">
               (onEventSelect)="onEventSelect($event)">    
      </scheduler>
    directives: [SchedulerComponent] 
})

Line 5 will be used to display the title for the selected scheduler event. Line 8 is the event binding. When the scheduler emits an event the binding will call an onEventSelect function with the eventRecord passed as an argument. Add the onEventSelect function to the AppComponent class:

export class AppComponent {
    title      = 'Angular 2 demo';

    onEventSelect(eventRecord:any) {
        // event selected in scheduler, display its name
        this.eventTitle = eventRecord.get('Name');
    }
}

Accessing the scheduler from another class

In the section “Add output property bindings” above we created a template reference variable to access SchedulerComponent output in the template. Unfortunately we cannot use that variable to access the SchedulerComponent from the main class of the application (AppComponent). Instead we have to inject the SchedulerComponent as a ViewChild in AppComponent. Follow the steps below to modify app.component.ts:

First, import ViewChild by altering the first import to:

import {Component, ViewChild} from '@angular/core';

Secondly, add a @ViewChild field somewhere at the top of the AppComponent class:

@ViewChild(SchedulerComponent) schedulerComponent:SchedulerComponent;

Congratulations! If you reached this point you can access the scheduler by using this.schedulerComponent anywhere in AppComponent.

Download the example

View live example: http://www.bryntum.com/playpen/angular2
Download source: http://www.bryntum.com/playpen/angular2.zip

Code away!

Comments ( 6 )

  • Balamurugan

    will your angular js supports multiple functionalities to use in dispatcher board . For Example (Drag and drop , show the details using cursor directory).
    If So then kindly please mention the functionalities

  • Johan Isaksson

    Hi,
    this blog post describes the basics of how to wrap our Ext Scheduler component for Angular 2. It does not affect the functionality available in the product, please look at the rest of our examples to see what is possible. To expose that functionality to Angular you would have to extend the wrapper yourself.
    Regards,
    Johan Isaksson

  • balamurugan m

    Hi,
    I saw your angular2 scheduler component .it working fine, I saw lot of examples in your product for ex (external drag& drop, time resolution, vertical mode, integration). Whether We need to integrate those extra features in angular2 component .if I purchase, it will support or not. How to wrap your JS file in Angular2 component? How to support for future in this product?

  • jack

    I m stuck with Error Ext is undefined

Leave a Comment