Tutorial 37: How to build a Calendar (Part II)

This tutorial is the second part of the series “How to build a Calendar” tutorials. It continues from the “calendar” application built in the first part of the series.

The first part of the series describes how to add a calendar component into a panel, how to use calendar‘s attributes and how to display activities on the calendar. This tutorial covers following topics;

  • how to add a new activity to calendar
  • how to edit activity details
  • how to reschedule an activity through the calendar

You may click here to run the application.

Editing – Creating an Activity

Create a datasource definition querying “ACTIVITIES” table created in the first part of the series. This datasource definition retrieves data for the activity with the given id and allows you to edit its details. It also allows you to create a new activities in the “ACTIVITIES” table.

To achieve this, expand the “Datasource Definitions” accordion, select the “Datasource Definitions” node and click the “+” button. Alternatively, you may right click the “Datasource Definitions” node and select “New” from the pop up menu. The “New Datasource Definition” dialog shows up. Enter “ACTIVITYBYID” as “Name”. Ensure that the “Table” radio button is selected and pick “ACTIVITIES” table from the combo box. Note that the Formspider IDE automatically picked the “ACTIVITYID” as the primary key of the datasource definition.

Creating “ACTIVITYBYID” datasource definition

Click the “Query” node in the navigation tree on the left. Enter the following “Where Clause” to the datasource definition:

ACTIVITYID = :activityid

This “Where Clause” assures that this datasource defition retrieves only the data belonging to the single activity with the specified activityid.

Entering Where Clause for “ACTIVITYBYID” datasource definition

Click the “Bind Variables” node to register “activityid” bind variable. Click “New BindVar” button, a new row appears in the grid. Enter “activityid” to the “Name” column and select “DefaultNumber″ as the “Domain” value.

Creating “activityid” bind variable

Click “OK” to save and close the “New Datasource Definition” dialog. This creates the “ACTIVITYBYID” datasource definition and the “ACTIVITYBYID1″ datasource.

Create a panel displaying activity details (id, title, start date, end date, background color, font color and editable state) using the newly created “ACTIVITYBYID1” datasource. This panel also allows you to set details of an existing or a new activity. Expand the “Containers” accordion, select the “Panels” node in the navigation tree and click the “+” button to create a new panel. Alternatively, you may right click the “Panels” node and select the “New” menu item from the pop-up menu. The “New Panel” dialog will show up. Enter “createEditActivityPanel” as the name of the panel. Click “OK” to create and open the panel in the editor.

Creating “createEditActivityPanel”

Edit the panel XML as the following;

<panel preferredHeightPolicy="Dynamic">
  <tableLayout cellSpacing="5">
    <row heightPolicy="Dynamic">
      <cell hAlign="Full" vAlign="Full">
        <textLabel label="Activity ID"/>
      </cell>
      <cell hAlign="Full" vAlign="Full">
        <textField name="tf_activityID" column="ACTIVITYID" dataSource="ACTIVITYBYID1"/>
      </cell>
    </row>
    <row heightPolicy="Dynamic">
      <cell hAlign="Full" vAlign="Full">
        <textLabel label="Title"/>
      </cell>
      <cell hAlign="Full" vAlign="Full">
        <textField column="TITLE" dataSource="ACTIVITYBYID1"/>
      </cell>
    </row>
    <row heightPolicy="Dynamic">
      <cell hAlign="Full" vAlign="Full">
        <textLabel label="Start Date"/>
      </cell>
      <cell hAlign="Full" vAlign="Full">
        <dateField column="STARTDATE" dataSource="ACTIVITYBYID1"/>
      </cell>
    </row>
    <row heightPolicy="Dynamic">
      <cell hAlign="Full" vAlign="Full">
        <textLabel label="End Date"/>
      </cell>
      <cell hAlign="Full" vAlign="Full">
        <dateField column="ENDDATE" dataSource="ACTIVITYBYID1"/>
      </cell>
    </row>
    <row heightPolicy="Dynamic">
      <cell hAlign="Full" vAlign="Full">
        <textLabel label="Background Color"/>
      </cell>
      <cell hAlign="Full" vAlign="Full">
        <textField column="BGCOLOR" dataSource="ACTIVITYBYID1"/>
      </cell>
    </row>
    <row heightPolicy="Dynamic">
      <cell hAlign="Full" vAlign="Full">
        <textLabel label="Font Color"/>
      </cell>
      <cell hAlign="Full" vAlign="Full">
        <textField column="FONTCOLOR" dataSource="ACTIVITYBYID1"/>
      </cell>
    </row>
    <row heightPolicy="Dynamic">
      <cell hAlign="Full" vAlign="Full">
        <textLabel label="Editable"/>
      </cell>
      <cell hAlign="Full" vAlign="Full">
        <checkBox column="EDITABLE" dataSource="ACTIVITYBYID1"/>
      </cell>
    </row>
    <row>
      <cell hAlign="Center" vAlign="Center">
        <button label="OK"/>
      </cell>
      <cell hAlign="Center" vAlign="Center">
        <button label="Cancel"/>
      </cell>
    </row>
  </tableLayout>
</panel>

Note that beside of the textFields displaying activity details, there is a button labeled “OK” to commit the changes made on activity details and a button labeled “Cancel” to dismiss them.

Create a dialog named “createEditActivityDialog” that displays the “createEditActivityPanel”. To achieve this, expand “Windows” node in the “Containers” nagivation tree and select the “Dialogs” node. Click the “+” button to create a new dialog. Alternatively, you may right click the “Dialogs” node and select “New” from the pop up menu. The “New Dialog” dialog will show up. Enter “createEditActivityDialog” as the name of the dialog. Specify a width of “300″, a height of “300″, untick the “Close” checkBox and click “OK”. Formspider IDE creates the dialog and opens it in the editor.

Creating “createEditActivityDialog”

Include the “createEditActivityPanel” panel to the “createEditActivityDialog” dialog;

<dialog width="300" height="300" close="N">
  <tableLayout>
    <row>
      <cell hAlign="Full" vAlign="Full">
        <include panelName="createEditActivityPanel"/>
      </cell>
    </row>
  </tableLayout>
</dialog>

When the user clicks the calendar you will show the “createEditActivityDialog” and display corresponding activity details if an activity box exists on the clicked point. If an activity box does not exist on the clicked point you will show the “createEditActivityDialog” with the purpose of creating a new activity. To achieve this, you should create an action which is fired when the calendar is clicked.

In your datasource schema, create a package called “calendar_pkg” and open your newly created “calendar_pkg” package in your favorite PL/SQL Editor. Add a procedure named “clickCalendar” and ensure that the procedure is exposed in the package specification. This procedure uses  api_calendar.getClickedActivity API to retrieve details of the activity existing on the clicked point of the calendar. This procedure checks whether an activity exists on the clicked point or not. If any activity exists, it executes “ACTIVITYBYID1” datasource and shows the “createEditActivityDialog”. If any activity box does not exist on the clicked point it creates a new row in the “ACTIVITYBYID1” datasource and shows the “createEditActivityDialog”.

procedure clickCalendar is
  v_activity_r      api_calendar.t_activity;
  v_row_id          number;
  v_clickedDate_dt  date;
begin
  -- get clicked activity
  v_activity_r := api_calendar.getclickedactivity('mainPanel.calendar');
  -- check whether an activity exists on the clicked point
  if v_activity_r.activityID_tx is null then -- new activity
    api_session.add('ActivityDialogAction', 'create');
    -- clear existing row in the datasource and create a new row
    api_datasource.clear('ACTIVITYBYID1');
    v_row_id := api_datasource.createrow('ACTIVITYBYID1');
    api_datasource.setcurrentrowbyid('ACTIVITYBYID1', v_row_id);
    -- default clicked date as default startdate and enddate values
    v_clickedDate_dt := api_calendar.getclickeddate('mainPanel.calendar');
    api_datasource.setcolumnvalue('ACTIVITYBYID1.startdate', v_clickedDate_dt);
    api_datasource.setcolumnvalue('ACTIVITYBYID1.enddate', v_clickedDate_dt);
    -- set activity as editable by default
    api_datasource.setcolumnvalue('ACTIVITYBYID1.editable', 'Y');
    -- enable Activity ID textField
    api_component.setenable('createEditActivityPanel.tf_activityID','Y');
    -- set dialog title and open it
    api_dialog.settitle('createEditActivityDialog', 'Create Activity');
    api_dialog.setvisible('createEditActivityDialog','Y');
  else -- existing activity
    api_session.add('ActivityDialogAction', 'edit');
    -- fetch clicked activity details
    api_datasource.setbindvar('ACTIVITYBYID1.activityid', to_number(v_activity_r.activityID_tx));
    api_datasource.executequery('ACTIVITYBYID1');
    -- disable Activity ID textField
    api_component.setenable('createEditActivityPanel.tf_activityID','N');
    -- set dialog title and open it
    api_dialog.settitle('createEditActivityDialog', 'Edit Activity');
    api_dialog.setvisible('createEditActivityDialog','Y');
  end if;
end;

In Formspider IDE expand the “Actions” accordion, select the “Actions” node, click the “+” button to create a new action. Alternatively you may right click the “Actions” node and select “New” from the pop up menu. The “New Action” dialog will show up. Enter “clickCalendar” as the action name and “calendar_pkg.clickCalendar” as the procedure. Click “OK” to save your action.

Creating “clickCalendar” action

Assign the newly created “clickCalendar” action to the “click” event of the calendar. To achieve this, open “mainPanel” and edit the panel XML as below;

<panel>
  <tableLayout>
    <row>
      <cell hAlign="Full" vAlign="Full">
        <calendar name="calendar" firstDayOfWeek="Monday" defaultView="Month" dataSource="ACTIVITIES1" idColumn="ACTIVITYID" startDateColumn="STARTDATE" endDateColumn="ENDDATE" titleColumn="TITLE" editableColumn="EDITABLE" fontColorColumn="FONTCOLOR" backgroundColorColumn="BGCOLOR">
          <events>
            <click action="clickCalendar"/>
          </events>
        </calendar>
      </cell>
    </row>
  </tableLayout>
</panel>

Open “calendar_pkg” and create a new procedure named “createEditActivityDialogOK” which fires when “OK” button existing on “createEditActivityPanel” is pressed. This procedure commits the changes made on activity details and reflects these changes to the calendar. If an existing activity is edited, it reflects the changes by updating the activity box displayed on the calendar using api_calendar.setActivity API. On the other hand, if a new activity is created, it adds a new activity box to the calendar using api_calendar.addActivity API.

procedure createEditActivityDialogOK is
  v_activity_r    api_calendar.t_activity;
  v_activityID_nr number;
begin

  v_activityID_nr := api_datasource.getcolumnvaluenr('ACTIVITYBYID1.activityid');

  -- reflect changes made to the calendar by adding a new activity box if a new activity is created
  -- or by setting the existing activity box belonging to the edited activity
  v_activity_r := api_calendar.createactivity(in_activityid_tx      => v_activityID_nr
                                             ,in_startdate_dt       => api_datasource.getcolumnvaluedt('ACTIVITYBYID1.startdate')
                                             ,in_enddate_dt         => api_datasource.getcolumnvaluedt('ACTIVITYBYID1.enddate')
                                             ,in_title_tx           => api_datasource.getcolumnvaluetx('ACTIVITYBYID1.title')
                                             ,in_editable_yn        => api_datasource.getcolumnvaluetx('ACTIVITYBYID1.editable')
                                             ,in_fontcolor_cd       => api_datasource.getcolumnvaluetx('ACTIVITYBYID1.fontColor')
                                             ,in_backgroundcolor_cd => api_datasource.getcolumnvaluetx('ACTIVITYBYID1.bgColor'));

  -- commit changes to db
  api_datasource.doCommit('ACTIVITYBYID1');  

  if api_session.getvaluetx('ActivityDialogAction') = 'edit' then
    api_calendar.setactivity('mainPanel.calendar', v_activity_r);
  else -- create
    api_calendar.addactivity('mainPanel.calendar', v_activity_r);
  end if;

  -- close the dialog
  api_dialog.setvisible('createEditActivityDialog','N');
exception
  when api_exception.e_invalidColor then -- raised from api_calendar.createactivity API
    api_application.showpopupmessage('Invalid font color or background color value.');
  when DUP_VAL_ON_INDEX then
    api_application.showpopupmessage('An activity with the ID "' || v_activityID_nr || '" already exists. Please specify another ID value.');
end;

Create a new action, enter “createEditActivityDialogOK” as the action name and “calendar_pkg.createEditActivityDialogOK” as the procedure. Click “OK” to save the action.

Open “calendar_pkg” again and create a new procedure named “createEditActivityDialogCancel” which fires when “Cancel” button existing on “createEditActivityPanel” is pressed. This procedure dismisses the changes made on activity details.

procedure createEditActivityDialogCancel is
begin
  -- close the dialog and clear the datasource to dismiss changes
  api_dialog.setvisible('createEditActivityDialog','N');
  api_datasource.clear('ACTIVITYBYID1');
end;

Create a new action, enter “createEditActivityDialogCancel” as the action name and “calendar_pkg.createEditActivityDialogCancel” as the procedure. Click “OK” to save the action.

Double-click and open the “createEditActivityPanel” in the editor. Add a “buttonPress” event to the “OK” and “Cancel” buttons  calling the “createEditActivityDialogOK” and the “createEditActivityDialogCancel” actions;

<panel preferredHeightPolicy="Dynamic">
  <tableLayout cellSpacing="5">
    <row heightPolicy="Dynamic">
      <cell hAlign="Full" vAlign="Full">
        <textLabel label="Activity ID"/>
      </cell>
      <cell hAlign="Full" vAlign="Full">
        <textField name="tf_activityID" column="ACTIVITYID" dataSource="ACTIVITYBYID1"/>
      </cell>
    </row>
    <row heightPolicy="Dynamic">
      <cell hAlign="Full" vAlign="Full">
        <textLabel label="Title"/>
      </cell>
      <cell hAlign="Full" vAlign="Full">
        <textField column="TITLE" dataSource="ACTIVITYBYID1"/>
      </cell>
    </row>
    <row heightPolicy="Dynamic">
      <cell hAlign="Full" vAlign="Full">
        <textLabel label="Start Date"/>
      </cell>
      <cell hAlign="Full" vAlign="Full">
        <dateField column="STARTDATE" dataSource="ACTIVITYBYID1"/>
      </cell>
    </row>
    <row heightPolicy="Dynamic">
      <cell hAlign="Full" vAlign="Full">
        <textLabel label="End Date"/>
      </cell>
      <cell hAlign="Full" vAlign="Full">
        <dateField column="ENDDATE" dataSource="ACTIVITYBYID1"/>
      </cell>
    </row>
    <row heightPolicy="Dynamic">
      <cell hAlign="Full" vAlign="Full">
        <textLabel label="Background Color"/>
      </cell>
      <cell hAlign="Full" vAlign="Full">
        <textField column="BGCOLOR" dataSource="ACTIVITYBYID1"/>
      </cell>
    </row>
    <row heightPolicy="Dynamic">
      <cell hAlign="Full" vAlign="Full">
        <textLabel label="Font Color"/>
      </cell>
      <cell hAlign="Full" vAlign="Full">
        <textField column="FONTCOLOR" dataSource="ACTIVITYBYID1"/>
      </cell>
    </row>
    <row heightPolicy="Dynamic">
      <cell hAlign="Full" vAlign="Full">
        <textLabel label="Editable"/>
      </cell>
      <cell hAlign="Full" vAlign="Full">
        <checkBox column="EDITABLE" dataSource="ACTIVITYBYID1"/>
      </cell>
    </row>
    <row>
      <cell hAlign="Center" vAlign="Center">
        <button label="OK">
          <events>
            <buttonPress action="createEditActivityDialogOK"/>
          </events>
        </button>
      </cell>
      <cell hAlign="Center" vAlign="Center">
        <button label="Cancel">
          <events>
            <buttonPress action="createEditActivityDialogCancel"/>
          </events>
        </button>
      </cell>
    </row>
  </tableLayout>
</panel>

Press “Run on Web” button to run your application.

Click on the activity box titled “Title of activity 6”. The “createEditActivityDialog” opens and displays the activity details. Edit activity details, enter “New title of 6” as “Title”, “Black” as “Font Color” and press “OK”. Note that the title and the font color of the activity box are updated as expected.

Editing the activity titled “Title of activity 6”

The activity box is updated as expected

Click on a point where any activity does not exist yet, the “createEditActivityDialog” opens. Fill the activity details and click “OK”. Note that a new activity box is created on the calendar following the activity details that you have provided.

Creating a new activity

A new activity box is added to the calendar

Rescheduling an Activity Through the Calendar

An activity can be easily rescheduled by dragging or resizing activity boxes. In order to make the changes made on the calendar permanent, you should reflect them to the database through the “ACTIVITYBYID1” datasource.

Open “calendar_pkg” and add a new procedure named “rescheduleActivity”. This procedure uses api_calendar.getRescheduledActivity API to retrieve details of the dragged-resized activity. This procedure reflects and commits the changes by setting the “startdate” and the “enddate” columns of the “ACTIVITYBYID1” datasource.

procedure rescheduleActivity is
  v_activity_r api_calendar.t_activity;
begin
  -- get rescheduled activity
  v_activity_r := api_calendar.getrescheduledactivity('mainPanel.calendar');
   -- fetch details of rescheduled activity
  api_datasource.setbindvar('ACTIVITYBYID1.activityid', to_number(v_activity_r.activityID_tx));
  api_datasource.executequery('ACTIVITYBYID1');
  -- set activity enddate and startdate to the rescheduled vvalues
  api_datasource.setcolumnvalue('ACTIVITYBYID1.startdate', v_activity_r.startdate_dt);
  api_datasource.setcolumnvalue('ACTIVITYBYID1.enddate', v_activity_r.enddate_dt);
  -- commit changes to db
  api_datasource.doCommit('ACTIVITYBYID1');
end;

Create a new action, enter “rescheduleActivity” as the action name and “calendar_pkg.rescheduleActivity” as the procedure. Click “OK” to save the action.

Open the “mainPanel”, add a “reschedule” event to the calendar and call the “rescheduleActivity” action;

<panel>
  <tableLayout>
    <row>
      <cell hAlign="Full" vAlign="Full">
        <calendar name="calendar" firstDayOfWeek="Monday" defaultView="Month" dataSource="ACTIVITIES1" idColumn="ACTIVITYID" startDateColumn="STARTDATE" endDateColumn="ENDDATE" titleColumn="TITLE" editableColumn="EDITABLE" fontColorColumn="FONTCOLOR" backgroundColorColumn="BGCOLOR">
          <events>
            <click action="clickCalendar"/>
            <reschedule action="rescheduleActivity"/>
          </events>
        </calendar>
      </cell>
    </row>
  </tableLayout>
</panel>

Run the application again and drag-and-drop or resize the activity box titled “Title of activity 4”. Note that when you click this activity after rescheduling it, the start date and end date values retrieved by “ACTIVITYBYID1” datasource are identical with the rescheduled values as expected.

You may click here to run the application.

Rescheduling an activity by dragging the activity box

Rescheduling an activity by resizing the activity box

Finally, try to drag or resize the activity box titled “Title of activity 1”. Note that since this activity is set as uneditable, the activity box cannot be dragged or resized.