Extending dojo.dnd with a creator function

Following the Dojo philosophy of “build with, not on”, dojo.dnd provides a terrific API for getting the right behaviour for your app, with plenty of extension points and monkeypatching opportunities. Making a creator function for dojo.dnd.Container is the most obvious way to customize dojo.dnd, because it gets called anytime a container or avatar wants to visualize a data item.

The creator is called for two events:

  1. When an item is added to a container (at startup, or when dropped).
  2. When an item is dragged out of its container, creating an avatar.

The avatar represents dragged items visually, and is typically a scaled-down version of the item.

The callers expect the creator function to return an object containing node, data, and type members, though the type is not strictly necessary. The node will be rendered on a page, while data is only used internally as the input to creator.

//  creator: Function: a creator function, which takes a data item, and
//  returns an object like that:
//      {node: newNode, data: usedData, type: arrayOfStrings}

The DnD system calls your creator function with a piece of data, and possibly a String hint, and it’s up to your function to return a new object. The interpretation of the data is particular to your app. In this example the input is either a String or an Object, so we’ll have to handle both cases.

creator: function(/*Object|String*/ item, /*String?*/ hint){

When an item starts getting dragged, the function will be called to make an avatar, so the hint parameter will equal "avatar". The first thing my creator checks is if the DnD system wants an avatar:

    if(hint == "avatar"){
        return {node: dojo.dnd._createSpan(item.domNode.innerHTML)};
    }

In this case, I know the item is an object with an attached DOM node, so I send the innerHTML to the _createSpan helper function. The DnD system takes care of the rest.

If we’re not being asked for an avatar, then we want to create a real node to insert into a container. The item is a string if it’s coming from a certain source in my app, but I want to turn it into an object before it goes into this container.

    else if(typeof item == "string"){
        item = turnIntoObject(item);
    }

The turnIntoObject function finds the right constructor, does a bit of setup, and returns a new object corresponding to the string.

Finally, we know we’re not creating an avatar and our item is an object, so we can return the object that the DnD system expects: {node: ..., data: ...}. To make life easy, I pass the current item object into the data slot, so next time we’ll drop straight through to this return.

    return {node: item.domNode, data: item}; // Object
}

And here’s the function in one piece:

//  creator: Function: a creator function, which takes a data item, and
//  returns an object like that:
//      {node: newNode, data: usedData, type: arrayOfStrings}
creator: function(/*Object|String*/ item, /*String?*/ hint){
    if(hint == "avatar"){
        return {node: dojo.dnd._createSpan(item.domNode.innerHTML)};
    }else if(typeof item == "string"){
        item = turnIntoObject(item);
    }
    return {node: item.domNode, data: item}; // Object
}

Tags: