BaseEditService

A base implementation of the edit service which persists data to traditional CRUD services such as OData.

To support custom models, the BaseEditService class requires a field map as a constructor parameter. Subclasses require you to
implement the read operation, which is not called directly by the base class, and the save method which persists the created,
updated, and deleted entities.

The events observable will publish the current data which is set upon subscription by using, for example, an async pipe
(more information).

Implementations which utilize dedicated services, such as Google Calendar and Microsoft Exchange, will typically implement the
EditService of the Scheduler directly.

import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { CreateFormGroupArgs } from '@progress/kendo-angular-scheduler';

import '@progress/kendo-date-math/tz/regions/Europe';
import '@progress/kendo-date-math/tz/regions/NorthAmerica';

import { EditService } from './edit.service';

@Component({
    selector: 'my-app',
    template: `
        <kendo-scheduler
            [kendoSchedulerBinding]="editService.events | async"
            [kendoSchedulerReactiveEditing]="createFormGroup"
            [editService]="editService"
            [modelFields]="editService.fields"
            [loading]="editService.loading"
            [selectedDate]="selectedDate"
            style="height: 600px;"
        >
            <kendo-scheduler-week-view startTime="07:00">
            </kendo-scheduler-week-view>
        </kendo-scheduler>
    `
})
export class AppComponent implements OnInit {
    public formGroup: FormGroup;
    public selectedDate: Date = new Date('2013-06-10T00:00:00');

    constructor(
        private formBuilder: FormBuilder,
        public editService: EditService
    ) {}

    public ngOnInit(): void {
        this.editService.read();
    }

    // Note the use of a lambda function in order to bind `this`
    public createFormGroup = (args: CreateFormGroupArgs): FormGroup => {
        const dataItem = args.dataItem;

        this.formGroup = this.formBuilder.group({
            'TaskID': args.isNew ? 0 : dataItem.TaskID,
            'Start': [dataItem.Start, Validators.required],
            'End': [dataItem.End, Validators.required],
            'StartTimezone': [dataItem.StartTimezone],
            'EndTimezone': [dataItem.EndTimezone],
            'IsAllDay': dataItem.IsAllDay,
            'Title': dataItem.Title,
            'Description': dataItem.Description,
            'RecurrenceRule': dataItem.RecurrenceRule,
            'RecurrenceID': dataItem.RecurrenceID
        });

        return this.formGroup;
    }
}
import { HttpClientJsonpModule, HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { ReactiveFormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { SchedulerModule } from '@progress/kendo-angular-scheduler';

import { AppComponent } from './app.component';
import { EditService } from './edit.service';

@NgModule({
    imports: [
        BrowserModule,
        BrowserAnimationsModule,
        ReactiveFormsModule,
        SchedulerModule,
        HttpClientModule,
        HttpClientJsonpModule
    ],
    declarations: [ AppComponent ],
    bootstrap: [ AppComponent ],
    providers: [ EditService ]
})

export class AppModule { }

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

import { Observable, zip } from 'rxjs';
import { map, tap } from 'rxjs/operators';

import { BaseEditService, SchedulerModelFields } from '@progress/kendo-angular-scheduler';
import { parseDate } from '@progress/kendo-angular-intl';

import { MyEvent } from './my-event.interface';

const CREATE_ACTION = 'create';
const UPDATE_ACTION = 'update';
const REMOVE_ACTION = 'destroy';

const fields: SchedulerModelFields = {
    id: 'TaskID',
    title: 'Title',
    description: 'Description',
    startTimezone: 'StartTimezone',
    start: 'Start',
    end: 'End',
    endTimezone: 'EndTimezone',
    isAllDay: 'IsAllDay',
    recurrenceRule: 'RecurrenceRule',
    recurrenceId: 'RecurrenceID',
    recurrenceExceptions: 'RecurrenceException'
};

@Injectable()
export class EditService extends BaseEditService<MyEvent> {
    public loading = false;

    constructor(private http: HttpClient) {
        super(fields);
    }

    public read(): void {
        if (this.data.length) {
            this.source.next(this.data);
            return;
        }

        this.fetch().subscribe(data => {
            this.data = data.map(item => this.readEvent(item));
            this.source.next(this.data);
        });
    }

    protected save(created: MyEvent[], updated: MyEvent[], deleted: MyEvent[]): void {
        const completed = [];
        if (deleted.length) {
            completed.push(this.fetch(REMOVE_ACTION, deleted));
        }

        if (updated.length) {
            completed.push(this.fetch(UPDATE_ACTION, updated));
        }

        if (created.length) {
            completed.push(this.fetch(CREATE_ACTION, created));
        }

        zip(...completed).subscribe(() => this.read());
    }

    protected fetch(action: string = '', data?: any): Observable<any[]> {
        this.loading = true;

        return this.http
            .jsonp(`https://demos.telerik.com/kendo-ui/service/tasks/${action}?${this.serializeModels(data)}`, 'callback')
            .pipe(
                map(res => <any[]>res),
                tap(() => this.loading = false)
            );
    }

    private readEvent(item: any): MyEvent {
        return {
            ...item,
            Start: parseDate(item.Start),
            End: parseDate(item.End),
            RecurrenceException: this.parseExceptions(item.RecurrenceException)
        };
    }

    private serializeModels(events: MyEvent[]): string {
        if (!events) {
            return '';
        }

        const data = events.map(event => ({
             ...event,
             RecurrenceException:
                this.serializeExceptions(event.RecurrenceException)
        }));

        return `&models=${JSON.stringify(data)}`;
    }
}

export interface MyEvent {
    TaskID?: number;
    OwnerID?: number;
    Title?: string;
    Description?: string;
    Start?: Date;
    End?: Date;
    StartTimezone?: string;
    EndTimezone?: string;
    IsAllDay?: boolean;
    RecurrenceException?: any;
    RecurrenceID?: number;
    RecurrenceRule?: string;
}
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';

const platform = platformBrowserDynamic();
platform.bootstrapModule(AppModule);

Selector

``

Fields

events Observable

An observable stream with the current events.

fields SchedulerModelFields

The model field map that will be used during the reading and updating of data items.

Methods

create

Creates a new event.

Parameters

event TEvent

The event that will be created.

createException

Creates an exception to an existing recurring event. The recurrenceId field of the recurrence exception
or the custom field that is set by the model map will point to the master recurring event.

Parameters

event TEvent

The instance of the occurrence that will be removed from the series.

value TEvent

An object which contains the updated field values, for example, a form group value.

findRecurrenceMaster

Returns the master recurring event for a specified recurring event.

Parameters

event TEvent

An event from the recurrence series.

Returns

any the master recurring event for the series.

isException

Checks if the event is a recurrence exception.

Parameters

event any

The event that will be checked.

Returns

boolean true if the event is a unique event which belongs to a recurrence series. Otherwise, returns false.

isRecurring

Checks if the event is part of the recurrence series.

Parameters

event any

The event that will be checked.

Returns

boolean true if the event is an occurrence, an exception, or a master event. Otherwise, returns false.

remove

Removes a non-recurring event.

Parameters

event TEvent

A reference to the event that will be removed.

removeOccurrence

Removes a single occurrence from a series of recurring events. The recurrenceId field of the occurrence
or the custom field which is set by a model map will point to the master recurring event.

Parameters

event TEvent

A reference to the occurrence.

removeSeries

Removes the recurrence series and exceptions, if any.

Parameters

event TEvent

Any event from the recurrence series.

update

Updates the specified event by copying the changed fields from the supplied value object.

Parameters

event TEvent

The event that will be updated.

value any

An object which contains the new field values, for example, a form group value.