Some events that a user enters into the scheduler repeat regularly. For example, weekly meetings, driving lessons, annual fairs, anniversaries and birthdays. To handle similar cases, Scheduler provides support for recurring events.
However, this functionality isn't enabled by default, and you should extend your existing code a little bit to activate it:
Let's start from the model. You should add 3 extra fields to database (as with regular events, in addition to the mandatory fields you can retrieve any extra data from the database):
Also note, the start_date and end_date fields change their meaning a little bit:
Since the start_date and end_date fields store the start and end dates of an event series (not a specific occurrence), we need to have the event_length field to store the duration of a specific occurrence.
To get detailed information on presenting recurring events in the database, please, refer to the dhtmlxScheduler documentation - 'Server-side integration'.
A basic DB table for storing recurring events can be created as in:
CREATE TABLE [Events] (
[id] INT IDENTITY (1, 1) NOT NULL,
[text] TEXT NULL,
[start_date] DATETIME NOT NULL,
[end_date] DATETIME NOT NULL,
[event_length] BIGINT NULL,
[rec_type] NVARCHAR(50) NULL,
[event_pid] INT NULL,
PRIMARY KEY (id)
);
In the controller you must enable the corresponding extension:
scheduler.Extensions.Add(SchedulerExtensions.Extension.Recurring);
and provide the following logic:
public ActionResult Save(int? id, FormCollection actionValues)
{
var action = new DataAction(actionValues);
DHXSchedulerDataContext data = new DHXSchedulerDataContext();
try
{
var changedEvent = (Recurring)DHXEventsHelper.Bind(typeof(Recurring), actionValues); // takes the object of an event
bool isFinished = deleteRelated(action, changedEvent, data);
if (!isFinished)
{
switch (action.Type)
{
case DataActionTypes.Insert:
data.Recurrings.InsertOnSubmit(changedEvent);
if (changedEvent.rec_type == "none")//deletes one event from the series
action.Type = DataActionTypes.Delete;
break;
case DataActionTypes.Delete:
changedEvent = data.Recurrings.SingleOrDefault(ev => ev.id == action.SourceId);
data.Recurrings.DeleteOnSubmit(changedEvent);
break;
default:// "update"
var eventToUpdate = data.Recurrings.SingleOrDefault(ev =>
ev.id == action.SourceId);
DHXEventsHelper.Update(eventToUpdate, changedEvent, new List() { "id" });
break;
}
}
data.SubmitChanges();
action.TargetId = changedEvent.id;
}
catch (Exception a)
{
action.Type = DataActionTypes.Error;
}
return (new AjaxSaveResponse(action));
}
// when the series is changed or deleted, we need to remove(change) all related events (events with the same 'event_pid')
protected bool deleteRelated(DataAction action, Recurring changedEvent, DHXSchedulerDataContext context)
{
bool finished = false;
if ((action.Type == DataActionTypes.Delete || action.Type == DataActionTypes.Update)
&& !string.IsNullOrEmpty(changedEvent.rec_type))
{
context.Recurrings.DeleteAllOnSubmit(from ev in context.Recurrings
where ev.event_pid == changedEvent.id select ev);
}
if (action.Type == DataActionTypes.Delete
&& (changedEvent.event_pid != 0 && changedEvent.event_pid != null))
{
Recurring changed = (from ev in context.Recurrings where ev.id == action.TargetId select ev)
.Single() ;
changed.rec_type = "none";
finished = true;
}
return finished;
}
Recurring events aren't stored in the database as individual items. The DB contains the start date, the time length and repetition of the series. Based on this information events are rendered in scheduler.
In case if you need to get an array of single events within a specific interval, you should use the List<object> GetOccurrences(IEnumerable source, DateTime from, DateTime to) method of the helper DHTMLX.Common.DHXEventsHelper class. As parameters, the method takes the collection of items, start and end dates (set the time interval you want to get instances of single events from).
var helper = new DHXEventsHelper();
var items = helper.GetOccurrences(new DHXSchedulerDataContext().Recurrings, new DateTime(2012, 11, 10),
new DateTime(2012, 12, 10));
The client side gets data from the rec_type field as a string of the following format: [type][count][count2][day][days]#[extra]
where:
Examples of the rec_type data:
By default, an event with the 'weekly' recurrence contains occurrences starting from the first day of the week when it has been created. Such a behavior can result in including past days (that are already gone) into the recurrence.
For example, the user creates an event on Friday and specifies the 'weekly' repetition on Tuesday and Thursday. The stored event will contain the current week, i.e. past Tuesday and Thursday, even though it has been created on Friday.
The library provides DHXScheduler.Config.repeat_precise property that allows excluding past days from the recurrence. Set the property to true and the start date of a recurring event will be the date of the first real occurrence, which in our example is Tuesday of the next week.
var sched = new DHXScheduler();
...
sched.Config.repeat_precise = true;
You can redefine the layout of the recurring control.
To customize the recurring layout, you need to:
Let's start with a use-case. Imagine that you need to remove the 'monthly' and 'yearly' repeat types and set the 'no end date' option for all events (i.e. remove the block that specifies the end of recurrence).
Step 1. Define the markup of a custom form and put it somewhere on the page (start with copying the default template that can be found at 'dhtmlxScheduler\sources\locale\recurring\'):
<div style="display:none">
<div class="dhx_form_repeat" id="custom_form">
<form>
<div>
<select name="repeat">
<option value="day">Daily</option>
<option value="week">Weekly</option>
</select>
</div>
<div>
<div style="display:none;" id="dhx_repeat_day">
<input type="hidden" name="day_type" value="d"/>
<input type="hidden" name="day_count" value="1" />
</div>
<div style="display:none;" id="dhx_repeat_week">
Repeat every week next days:<br />
<label><input type="checkbox" name="week_day" value="1" />Monday</label>
<label><input type="checkbox" name="week_day" value="2" />Tuesday</label>
<label><input type="checkbox" name="week_day" value="3" />Wednesday</label>
<label><input type="checkbox" name="week_day" value="4" />Thursday</label>
<label><input type="checkbox" name="week_day" value="5" />Friday</label>
<label><input type="checkbox" name="week_day" value="6" />Saturday</label>
<label><input type="checkbox" name="week_day" value="0" />Sunday</label>
<input type="hidden" name="week_count" value="1" />
</div>
</div>
<input type="hidden" value="no" name="end">
</form>
</div>
</div>
Step 2. Set the Form property of the LightboxRecurringBlock control to the id of your custom form:
scheduler.Lightbox.Add(new LightboxRecurringBlock("recurring", "Repeat"){
Form = "custom_form"
});
We have provided the default structure of the lightbox's recurring block for different languages at the 'dhtmlxScheduler\sources\locale\recurring\' directory.
For example, see the 'dhtmlxScheduler\sources\locale \recurring\repeat_template_en.htm' file for the English locale. In general, the recurring block of the lightbox consists of 3 groups of controls:
1) Controls for choosing the recurrence type. These inputs have the name 'repeat' and one of the following values: 'daily', 'weekly', 'monthly', 'yearly'. The form must have at least one 'repeat' input with a value. You can use radio buttons, selects or set the default type in the hidden input. Consider the following examples, each of them is a valid way for selecting the type of recurrence in the form.
<label><input type="radio" name="repeat" value="day" />Daily</label><br />
<label><input type="radio" name="repeat" value="week"/>Weekly</label><br />
<label><input type="radio" name="repeat" value="month" />Monthly</label><br />
<label><input type="radio" name="repeat" value="year" />Yearly</label>
<select name="repeat">
<option value="day">Daily</option>
<option value="week">Weekly</option>
</select>
<input type="hidden" name="repeat" value="day" />
2) A block to configure the recurrence depending on the repeat type. For example, for the 'Daily' repeat type, the block will take the following structure:
<div class="dhx_repeat_center">
<div style="display:none;" id="dhx_repeat_day">
<label>
<input class="dhx_repeat_radio" type="radio" name="day_type" value="d"/>Every
</label>
<input class="dhx_repeat_text" type="text" name="day_count" value="1" />day<br />
<label>
<input class="dhx_repeat_radio" type="radio" name="day_type" checked value="w"/>Every workday
</label>
</div>
...
</div>
Note, that the markup related to the specific type of recurrence can be wrapped in a div with the id in the following format "dhx_repeat_<repeat type>", e.g. "dhx_repeat_day". In this case it will be displayed only when the appropriate repeat type is selected.
3) Controls for specifying the end of recurrence. The end of recurrence is defined by an input with the 'end' name. Possible values are 'no', 'date_of_end', 'occurences_count'. Similar to the 'repeat' controls, the form must have at least one input of this type.
<div class="dhx_repeat_right">
<label>
<input type="radio" name="end" value="no" checked/>No end date
</label><br />
<label>
<input type="radio" name="end" value="date_of_end" />After</label>
<input type="text" name="date_of_end" />
<br />
<label>
<input type="radio" name="end" value="occurences_count" />After</label>
<input type="text" name="occurences_count" value="1" />Occurrences
</div>
The date for the 'date_of_end' mode must be defined in an input named 'date_of_end'. The same works for the 'occurences_count' mode that takes the number of occurrences from an input named 'occurences_count'.
You can remove any type or predefine it in a hidden input:
<input type="hidden" name="end" value="date_of_end" />
<input type="hidden" name="date_of_end" value="01.01.2016" />
Please, before starting to apply a custom configuration to the lightbox's recurring block, consider the following things:
Beware, dhtmlxScheduler doesn't work with your original HTML form and just creates its copy in the lightbox's template.
For example:
<input onclick="handler()">
addEventListener(node, "click", function(){...})