Download Free Demos

Managing CRUD Operations

dhtmlxScheduler loads data via ajax and can send create/update/delete changes back to the specified backend handler. Since the component does not provide automated database binding, such backend has to be implemented manually.

Common Approach

Below we'll show a common CRUD template for ASP.NET MVC and ASP.NET Web Forms. The explanation is provided afterwards.

ASP.NET MVC

public class CalendarController : Controller
{
    public ActionResult Index()
    {
        var scheduler = new DHXScheduler(this);
 
        // ajax call for Data on loading
        scheduler.LoadData = true;
 
        // send changes to the Save action
        scheduler.EnableDataprocessor = true;
 
        return View(scheduler);
    }
 
    public ContentResult Data()
    {
        var data = new SchedulerAjaxData(
                new List<CalendarEvent>{
                    new CalendarEvent{
                        id = 1,
                        text = "Sample Event",
                        start_date = new DateTime(2017, 09, 03, 6, 00, 00),
                        end_date = new DateTime(2017, 09, 03, 8, 00, 00)
                    },
                    new CalendarEvent{
                        id = 2,
                        text = "New Event",
                        start_date = new DateTime(2017, 09, 05, 9, 00, 00),
                        end_date = new DateTime(2017, 09, 05, 12, 00, 00)
                    }
                }
            );
        return (ContentResult)data;
    }
 
    public ContentResult Save(int? id, FormCollection actionValues)
    {
        var action = new DataAction(actionValues);
 
        try
        {
            var changedEvent = DHXEventsHelper.Bind<CalendarEvent>(actionValues);
 
            switch (action.Type)
            {
                case DataActionTypes.Insert:
                    // do insert
                    // action.TargetId = changedEvent.id; // assign database id
                    break;
                case DataActionTypes.Delete:
                    // do delete
                    break;
                default: // "update"                          
                    // do update
                    break;
            }
        }
        catch
        {
            action.Type = DataActionTypes.Error;
        }
        return (ContentResult)new AjaxSaveResponse(action);
    }
}

ASP.NET Web Forms

public partial class _Default : System.Web.UI.Page
{
 
    public DHXScheduler Scheduler { get; set; }
    protected void Page_Load(object sender, EventArgs e)
    {
        this.Scheduler = new DHXScheduler();
 
        Scheduler.DataAction = this.ResolveUrl("~/Data.ashx");
        Scheduler.SaveAction = this.ResolveUrl("~/Save.ashx");
        Scheduler.LoadData = true;
        Scheduler.EnableDataprocessor = true;
    }
}

ASPX

<div style="height:600px; width: 100%;">
    <%= this.Scheduler.Render()%>
 </div>

Data.ashx.cs

public class Data : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        var data = new SchedulerAjaxData(
            new List<CalendarEvent>{
                new CalendarEvent{
                    id = 1,
                    text = "Sample Event",
                    start_date = new DateTime(2017, 09, 03, 6, 00, 00),
                    end_date = new DateTime(2017, 09, 03, 8, 00, 00)
                },
                new CalendarEvent{
                    id = 2,
                    text = "New Event",
                    start_date = new DateTime(2017, 09, 05, 9, 00, 00),
                    end_date = new DateTime(2017, 09, 05, 12, 00, 00)
                }
            }
        );
 
        context.Response.ContentType = "text/json";
        context.Response.Write(data.ToString());
    }
 
    public bool IsReusable { get { return false; } }
}

Save.ashx.cs

public class Save : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        var action = new DataAction(context.Request.Form);
        try
        {
            var changedEvent = DHXEventsHelper.Bind<CalendarEvent>(context.Request.Form);
 
            switch (action.Type)
            {
                case DataActionTypes.Insert:
                    //do insert
                    //action.TargetId = changedEvent.id; // assign database id
                    break;
                case DataActionTypes.Delete:
                    //do delete
                    break;
                default:// "update"                          
                    //do update
                    break;
            }
        }
        catch
        {
            action.Type = DataActionTypes.Error;
        }
 
        context.Response.ContentType = "text/xml";
        context.Response.Write(new AjaxSaveResponse(action).ToString());
    }
 
    public bool IsReusable { get { return false; } }
}

The Data handler is covered in a separate article. Let's consider the Save handler.

Specifying the update handler source

As you can see, inside the Index action we create scheduler and tell it to load data:

var scheduler = new DHXScheduler(this);
scheduler.EnableDataprocessor = true;

And the Save action serves as a handler for changes made in the calendar.

What do we need to know now:

  • If you create scheduler instance using DHXScheduler(Controller parent) constructor, scheduler will use the "Save" action of the controller as a data source. So you don't have to specify paths manually.
  • Thus, if you use scheduler in ASP.NET MVC, usually, you don't need to specify URL manually, just define the Data method.
  • Alternatively, you can specify a different action or an URL using the DHXScheduler.SaveAction property

Specifying a different action:

var scheduler = new DHXScheduler(this);
scheduler.SaveAction = "SaveChanges";
scheduler.EnableDataprocessor = true;

Specifying a complete URL explicitly:

var scheduler = new DHXScheduler(); // !! use the default constructor
scheduler.SaveAction = Url.Content("~/data.ashx");
scheduler.EnableDataprocessor = true;

Handling updates

1) To parse a request, you should use the DHTMLX.Common.DataAction class.

DataAction properties are:

  • Type - the type of an action. Possible values are:
    • Inserted;
    • Deleted;
    • Updated;
    • Error.
  • SourceId - the ID assigned to an item at the beginning of the action.
  • TargetId - the ID assigned to an item after the action is finished.

The last two parameters are different just in the case of the insert operation. In this case SourceID is the id generated on the client side and TargetId is the id of an event in the database.

2) To render the response, you should use the DHTMLX.Common.AjaxSaveResponse class. The constructor of AjaxSaveResponse takes a DataAction object as an argument.

By default, the DHTMLX.Common.DataAction class expects the item id to have the Integer type. But if you need, you can use other types, such as String or Guid.

To set a class different from Integer for the ID field, you should use generic of the DataAction class:

var action = new DataAction<string>(actionValues);

Helpers

The library provides special helpers (the DHTMLX.Common.DHXEventsHelper class) that especially can come in use when you develop a usual .NET project (but for MVC projects they are also helpful).

So, these helpers are:

  • Bind() - builds an object by taking values from the related request and converting them to the appropriate data type (analog to MVC model binder). In the CRUD logic it can be used to get the object of the changed event at once, not 'constructing' it from the values of individual fields.
  • Update() - updates one object with the values of properties from another object. It helps you to update the changed event by one command.

One more member of the class is the GetOccurrences() method. It's used for recurring events, and you can find details on it in the related article - Recurring events.

Bind() method

The Bind() method has 4 overloads:

  • public static object Bind(Type obj, NameValueCollection values);
  • public static T Bind(NameValueCollection values);
  • public static object Bind(Type obj, NameValueCollection values, CultureInfo culture);
  • public static T Bind(NameValueCollection values, CultureInfo culture);

As parameters the method takes:

  • obj - specifies the type the object should have (note, that the object's class must have the default constructor);
  • values - the name-value collection;
  • culture - defines the locale settings (e.g. new CultureInfo("en-US")).

Update() method

The Update() method has 2 overloads:

  • public static void Update(object target, object source);
  • public static void Update(object target, object source, List<string> except);

As parameters the method takes:

  • target - the object which should be updated;
  • source - the object to copy the values of properties from;
  • except - the list of properties that should be excluded from the updating procedure.

With the helpers, the function implementing CRUD logic may look as follows:

Using LinqToSQL Data context

public ContentResult Save(int? id, FormCollection actionValues)
{
  var action = new DataAction(actionValues);
 
  var data = new DHXSchedulerDataContext();
  try
   {
       var changedEvent = DHXEventsHelper.Bind<Event>(actionValues);
       switch (action.Type)
       {
         case DataActionTypes.Insert:
                data.Events.InsertOnSubmit(changedEvent);
                break;
         case DataActionTypes.Delete:
                changedEvent = data.Events.SingleOrDefault(ev => ev.id == action.SourceId);
                data.Events.DeleteOnSubmit(changedEvent);
                break;
         default:// "update"                          
                var eventToUpdate = data.Events.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));
}

Using Entity Framework context

public ContentResult Save(int? id, FormCollection actionValues)
{
    var action = new DataAction(actionValues);
    var changedEvent = DHXEventsHelper.Bind<Event>(actionValues);
    var db = new CalendarContext();
    try
    {
        switch (action.Type)
        {
            case DataActionTypes.Insert:
                db.Events.Add(changedEvent);
                break;
            case DataActionTypes.Delete:
                changedEvent = db.Events.FirstOrDefault(ev => ev.id == action.SourceId);
                db.Events.Remove(changedEvent);
                break;
            default:// "update"
                var target = db.Events.Single(e => e.id == changedEvent.id);
                DHXEventsHelper.Update(target, changedEvent, new List<string> { "id" });
            break;
        }
        db.SaveChanges();
        action.TargetId = changedEvent.id;
    }
    catch (Exception a)
    {
        action.Type = DataActionTypes.Error;
    }
 
    return (new AjaxSaveResponse(action));
}

Common CRUD Implementation

Helpers is a preferable approach, but if you don't want to use them, you can implement the CRUD logic by using MVC Model Binding:

// Using Linq To Sql context
public ContentResult Save(Event changedEvent, FormCollection actionValues)
{
    // initializes a DataAction object from request parameters
    var action = new DataAction(actionValues);
 
    var data = new DHXSchedulerDataContext();
 
    // Implementing create/update/delete operations. 
    // Note, we use the try-catch block to handle exceptions which may be thrown during operations. 
    // In this case, we must assign the error status to the DataAction object
    // in order to mark the appropriate action on the client side as failed.
    try
     {
        switch (action.Type)
          {
             case DataActionTypes.Insert:
                 data.Events.InsertOnSubmit(changedEvent);
                break;
             case DataActionTypes.Delete:
                changedEvent = data.Events.SingleOrDefault(ev => ev.id == action.SourceId);
                data.Events.DeleteOnSubmit(changedEvent);
                break;
             default:// "update"                          
                changedEvent = data.Events.SingleOrDefault(ev => ev.id == action.SourceId);
                UpdateModel(changedEvent);
                break;
          }
          data.SubmitChanges();
          action.TargetId = changedEvent.id;
     }
    catch (Exception a)
     {
         action.Type = DataActionTypes.Error;
     }
    // renders a response using an AjaxSaveResponse object. AjaxSaveResponse can be initialized by 
    // the DataActionResult object and can be implicitly converted
    // to the System.Web.Mvc.ContentResult type.
       return (new AjaxSaveResponse(action));
}

Custom Initial Values for Events

There is a possibility to set custom initial values for the event's properties. So, in cases when the user adds a new event, it will have values set by you.

To set custom initial values for new events, you should use the DHXScheduler.InitialValues.Add(string propertyName, object defaultValue) method. The DHXScheduler.InitialValues class contains a collection of default values for an event.

For example, if you want to change the text of new events and have "I'm your new event!" instead of "New event" you can call the Add() method as in:

var scheduler = new DHXScheduler()
scheduler.InitialValues.Add("text", "I'm your new event!");

 custom inital values for events

Post-processing Data Properties on the Server

In this part we'll consider how you can update a property or several properties on the client after saving a data item on the server.

First of all, to be able to update separate properties apart from the whole data item, you should activate a special mode (that provides such an ability), as in:

public ActionResult Index()
{
    var scheduler = new DHXScheduler(this);
    ...
    scheduler.UpdateFieldsAfterSave(); // this line activates the required mode
    ...
}

Then to update the desired property(-ies), you should use one of the methods below (the methods belong to the AjaxSaveResponse class):

  • UpdateField(string propertyName, object propertyValue) - updates a singular data property
public ContentResult Save(int? id, FormCollection actionValues)
{
...
   var response = new AjaxSaveResponse(action);
   response.UpdateField("propertyName", propertyValue);
   return (ContentResult)response;
}

After the client gets the response from the server, it will update the 'propertyName' property of the modified data item with the value of 'propertyValue'.

  • UpdateFields(Dictionary<string,object> properties) - updates multiple data properties at once
public ContentResult Save(int? id, FormCollection actionValues)
{
...
   var response = new AjaxSaveResponse(action);
   response.UpdateFields(new Dictionary<string,object>{
        {"propertyName", "propertyValue"},
        {"propertyName2", "propertyValue2"}
   };
   return (ContentResult)response;
}

Handling AJAX Errors

In case of network errors or invalid server response, the client side will fire the "onSaveError" event which can be captured with a JavaScript code:

scheduler.attachEvent("onSaveError", function(id, resp){
    dhtmlx.message("Failed to  update data");
})

See the event details here.

REST Mode

The component can work with the REST backend, which means that it can use an appropriate verb for each type of the operation - GET, POST, PUT, DELETE.

This mode can be enabled by the DHXScheduler.AjaxMode property:

var scheduler = new DHXScheduler();
scheduler.AjaxMode = TransactionModes.REST;
  • GET - load data - requests will be sent to the URL specified in the DHXScheduler.DataAction property.
  • POST, PUT, DELETE - add, update, delete an item - requests will be sent to the URL specified in the DHXScheduler.SaveAction property

Was this article helpful?

Yes No