Drag and Drop with LightSwitch HTML Client (Rohit Agrawal)

Drag and Drop with LightSwitch HTML Client (Rohit Agrawal)

Rate This
  • Comments 26

While the built-in controls and features provided by LightSwitch are optimized for smaller form factors, it is possible to take advantage of the mouse events available on desktop browsers to enable drag and drop when then situation calls for it. With HTML5 it is possible to grab an object with a mouse click from one location and drop it into another. This feature can be useful in LightSwitch applications where you display lists based on a property with some choice list values. A To-Do list or a scheduler type of application are good examples. The following post will demonstrate one way you can use HTML5 drag and drop in your apps. If you are not already familiar with LightSwitch, please see LightSwitch HTML Client Documentation and LightSwitch HTML Client Tutorial.

Create a Tasks (To-Do) Application

First, let’s start by creating a new LightSwitch HTML application and add a Table called Task with the following properties – ‘Name’ of type String and ‘State’ of type String. Add a choice list to the property ‘State’ with values – ‘Not Started’, ‘In Progress’ and ‘Completed’. Later in the application, we will be filtering Tasks based on their state.

1

Add 3 server queries – TasksNotStarted, TasksInProgress and TasksCompleted. The TasksNotStarted query will have the following filter – “Where State = <Literal> Not Started”. Likewise, TasksInProgress and TasksCompleted will have the following filters respectively - “Where State = <Literal> In Progress” and “Where State = <Literal> Completed”.

TasksNotStartedQuery

Add a Browse screen called ‘Home’ with Screen data as ‘None’ and change the control type of ‘Group’ Tab from ‘Rows Layout’ to ‘Columns Layout’. Now add all 3 queries to the Home screen by selecting “Add Data Item” button at the top of the screen designer and then drag them over into the content tree. Lastly add a command bar button on Home screen to add new Task. This is how the ‘Home’ screen will look like.

2

Now that we have 3 lists on our Home screen, it will be hard to distinguish between them if we do not show the headers for each list. So let’s now check off the ‘Show Header’ property for each list.

ShowHeader

Make List Items Draggable

On ‘Home’ screen, select ‘TasksNotStartedTemplate’ (Note: The display name of TasksNotStartedTemplate is ‘Task’) and click ‘Edit PostRender Code’ on the property sheet.

3

First, copy the following method and paste it on the Home.lsml.js file. ‘draggable’ is an HTML attribute which is by default set to false. For each list item, we will be setting this attribute to true. Moreover, we will also be passing the TaskId along with each list item that is being dragged so that later when we drop a task into another list we know which task’s state has to be changed.

function makeDraggable(element, taskId) {
    //Get the entire list item
    var listItem = $(element).closest("li");

    //Enable draggable feature
    listItem.attr("draggable", true);

    //Pass taskId along with the listItem when it is being dragged
    listItem[0].ondragstart = function (e) {
        e.dataTransfer.setData("Text", taskId.toString());
    };
}

 

Now, copy/paste the following code in TasksNotStartedTemplate_postRender() method.

myapp.Home.TasksNotStartedTemplate_postRender = function (element, contentItem) {
    // Write code here.
    makeDraggable(element, contentItem.value.Id);
};

 

Similarly, add following code to TasksInProgressTemplate_postRender() and TasksCompletedTemplate_postRender() methods.

myapp.Home.TasksInProgressTemplate_postRender = function (element, contentItem) {
    // Write code here.
    makeDraggable(element, contentItem.value.Id);
};
myapp.Home.TasksCompletedTemplate_postRender = function (element, contentItem) {
    // Write code here.
    makeDraggable(element, contentItem.value.Id);
};

On pressing F5, you will be able to drag a list item but will not be able to drop the list item yet.

4

Enable Dropping of List Items

To allow a list item to be dropped into another list we will have to prevent the default handling of all 3 lists which can be done by calling the event.preventDefault() method. The ondrop event will essentially get the taskId of the Task that is being dropped, change the state of task, save changes and eventually refresh all 3 lists. Open the Home.lsml.js file and copy/paste the following method at the bottom.

function enableDrop(element, stateType, screen) {
    var ulElement = $(element).find("ul").closest("div");
    ulElement[0].ondragover = function (e) {
        e.preventDefault();
    };
    ulElement[0].ondrop = function (e) {
        //Get TaskId
        var data = e.dataTransfer.getData("Text");
        var filter = "Id eq " + data;
        myapp.activeDataWorkspace.ApplicationData.Tasks.filter(filter).
            execute().then(function (result) {
            //Change state of Task
            result.results[0].State = stateType;
            return myapp.activeDataWorkspace.ApplicationData.saveChanges();
        }).then(function () {
            //Refresh all 3 lists
            screen.TasksNotStarted.refresh();
            screen.TasksInProgress.refresh();
            screen.TasksCompleted.refresh();
        });
    }
}

Now add postRender code to all 3 Lists – TasksNotStarted, TasksInProgress and TasksCompleted.

myapp.Home.TasksNotStarted_postRender = function (element, contentItem) {
    // Write code here.
    enableDrop(element, "Not Started", contentItem.screen);
};

myapp.Home.TasksInProgress_postRender = function (element, contentItem) {
    enableDrop(element, "In Progress", contentItem.screen);
};

myapp.Home.TasksCompleted_postRender = function (element, contentItem) {
    enableDrop(element, "Completed", contentItem.screen);
};

You should now be able to drag a list item from one list and drop it into another. On dropping a Task to another list, the status of the dragged Task will automatically change and all 3 lists will refresh.

Add Background Color to Lists

Since the background color of all lists is white by default, it might be hard for the user to see a spot to drop a task. This problem can be solved by simply adding different background colors to all 3 lists via CSS. Here’s one way you can do this. Add the following highlighted line of code to the PostRender methods of all 3 lists. If we change the background color of entire list then it would change the background color of list header as well and since a list header is not a droppable region, we will first find the element on which a list item can be dropped and then change the background color of this element.

myapp.Home.TasksNotStarted_postRender = function (element, contentItem) {
    // Write code here.
    $(element).find("ul").closest("div").css("background", "#FFB6C1");
    enableDrop(element, "Not Started", contentItem.screen);
};

myapp.Home.TasksInProgress_postRender = function (element, contentItem) {
    $(element).find("ul").closest("div").css("background", "#FFE4B5");
    enableDrop(element, "In Progress", contentItem.screen);
};

myapp.Home.TasksCompleted_postRender = function (element, contentItem) {
    $(element).find("ul").closest("div").css("background", "#90EE90");
    enableDrop(element, "Completed", contentItem.screen);
};

This is what the screen will look like.

5

Increase Area of the Droppable Region

The droppable region on a list is currently limited to the number of list items within a list. You might want to increase it in order to make full utilization of the screen space. The droppable region’s area can be increased by making the lists taller so that each list grows in height leaving the height of a list item unaffected. On ‘Home’ screen, select ‘Columns Layout – Group (Tab)’ and on the property sheet set the height to ‘Stretch to Container’. Similarly set the height of all 3 Lists to ‘Stretch to container’.

6

This is what the screen looks like after making the above changes. The list item can now be dropped anywhere on the colored portion of the list.

7

Conclusion

With LightSwitch it is possible to add various HTML5 features and enhance the experience of your web application by writing custom code. Anything you can do in JavaScript & HTML5 you can do in the LightSwitch HTML client. The LightSwitch HTML client is available in Visual Studio 2012 Update 2 and higher but I encourage you to download Visual Studio 2013 RC and try out all of our new features!

And for a tutorial on HTML5 Drag & Drop, see HTML5 Drag and Drop.

- Rohit Agrawal, SDET, LightSwitch Team

Leave a Comment
  • Please add 2 and 6 and type the answer here:
  • Post
  • Great, even in VS 2013 RC.

    However I have one problem: When I add a task I get it in all three columns until the refresh commands in the drop function are used.

    How can I get the <query>.load / <query>.refresh get fired after adding a new task?

  • @Carlos - The stateType property in the enableDrop function is used to determine where an item is dropped.

    @Radareye - You can add a query.refresh() calls to a new button or 'pagechange' events in order to refresh the on screen lists. An example of the pageChange event would be like:

    $(document).on("pagechange", function (e) {

           if (e.currentTarget.nameProp === "Home") {

               contentItem.screen.TasksCompleted.refresh();

               contentItem.screen.TasksInProgress.refresh();

               contentItem.screen.TasksNotStarted.refresh();

           }

       });

  • @Rohit - Thank you for this snippet. But it is not clear to me where tot put pagechange event handler. I am a newbe on LightSwitch. I prever this over a button to be clicked after added one or more tasks.

  • @Radareye - You can put the pagechange event handler inside the postRender method for any one of the three lists.

  • @Rohit,

    Thanks again.

    At the moment I have this code in one of the postRender methods:

    myapp.Home.TaskNotStarted_postRender = function (element, contentItem) {

       $(element).find("ul").closest("div").addClass("todo");

       enableDrop(element, 1, contentItem.screen)

       $(element).on("pagechange", function (e) {

           if (e.currentTarget.nameProp === "Home") {

               contentItem.screen.TasksCompleted.refresh();

               contentItem.screen.TasksInProgress.refresh();

               contentItem.screen.TasksNotStarted.refresh();

           }

       });

    };

    But it does not do what I expecte.

  • @RADAREYE,  try to put pagechange event inside myapp.Home.Group_postRender:

    myapp.Home.Group_postRender = function (element, contentItem) {

       // Write code here.

       $(document).on("pagechange", function (e) {

           if (e.currentTarget.nameProp === "Home") {

               contentItem.screen.TasksCompleted.refresh();

               contentItem.screen.TasksInProgress.refresh();

               contentItem.screen.TasksNotStarted.refresh();

           }

       });

    };

  • @Ciro,

    Many thanks. This was the part on info I was missing.

    As I am a newby to LightSwith / JavaScript I do not know which events are availeble.

    The blog and tuturials I have seen sofar did not mention the Group_postRender.

  • When executing the function MakeDraggable shows 'runtime error in JavaScript: Can not get property' toString 'of undefined or null reference'

    You may postrender problem?, help me please

  • Great article!

    Unfortunately I have problem when I am trying to drag.

    I am getting error when executing the function MakeDraggable shows 'runtime error in JavaScript: Can not get property' toString 'of undefined or null reference'

    What I am doing wrong? I am not experienced with JavaScripts.

  • Thank You! this is the accurate way to do the drag & drop

  • For people having issues getting a null value for taskID, use contentItem.data.Id to get the value of the task ID, rather than the contentItem.value.Id, so the Template renders should be:

    makeDraggable(element, contentItem.data.Id);

Page 2 of 2 (26 items) 12