XPagesでDrag and Drop

XPagesでドラッグ and ドロップ を行うサンプルがあったのでそれを解析してみる。これを理解すれば自分でもブラウザ上でDrag & Dropをできるようになるかも。

元ネタは、Pipalia Software HouseImplementing drag and drop in XPages using Dojo という文書。 サンプルDBもあるので、ダウンロードして試すことができる。

文書の一覧から削除したい対象をゴミ箱にドラッグアンドドロップすると、対象文書が消えるというもの。

DragandDropデモ

このサンプルで使われているテクニックはこんな感じ。

サンプルコードの解析

ドラッグ元とドロップ先のコード

  01:     <xp:div dojoType="dojo.dnd.Target" style="width:94px;height:94px" id="divTrash">
  02:       <xp:image url="/trashicon.gif" id="trashIcon"></xp:image>
  03:     </xp:div>
  04:
  05:     <xp:dataTable id="dataTable" rows="10" value="#{view1}" var="dtData">
  06:       <xp:this.attrs>
  07:         <xp:attr name="dojotype" value="dojo.dnd.Source"></xp:attr>
  08:       </xp:this.attrs>
  09:       <xp:this.rowAttrs>
  10:         <xp:attr name="class" value="dojoDndItem"></xp:attr>
  11:         <xp:attr name="noteid" value="#{javascript:dtData.getDocument().getNoteID()}"></xp:attr>
  12:       </xp:this.rowAttrs>
  13:       <xp:column id="column1">
  14:         <xp:this.facets>
  15:           <xp:span xp:key="header">Name</xp:span>
  16:         </xp:this.facets>
  17:         <xp:text escape="true" value="#{dtData.$22}" disableTheme="true"></xp:text>
  18:       </xp:column>
  19:       <xp:column id="column2">
  20:         <xp:this.facets>
  21:           <xp:span xp:key="header">Address</xp:span>
  22:         </xp:this.facets>
  23:         <xp:text escape="true" value="#{dtData.Address}" disableTheme="true"></xp:text>
  24:       </xp:column>
  25:     </xp:dataTable>  
  

要は、ドラッグ元の要素にdojotype="dojo.dnd.Source"を設定し、ドラッグしたい対象にclass="dojoDndItem"を付けてやるというのが大事なんだろうと思う。

これでドラッグ中の表示もやってくれる。

ドラッグ中の表示

前準備部

必要なライブラリを読み込んでるところ。

  01:   <xp:view xmlns:xp="http://www.ibm.com/xsp/core" dojoTheme="true">
  02:     <xp:this.resources>
  03:       <xp:dojoModule name="dojo.dnd.Source"></xp:dojoModule>
  04:     </xp:this.resources>  
  

ドロップされたときの処理(クライアントサイド)

ページ内の、<xp:scriptBlock>に以下のコードが書かれている。要素をゴミ箱アイコンにドロップしたときに走る処理が記述されている。 ここはClient Sideのスクリプト。

  01: XSP.addOnLoad(init);
  02: var divTrash;
  03: function init() {
  04:     console.log("init");
  05:     divTrash = new dojo.dnd.Target("#{id:divTrash}");
  06:     dojo.connect(divTrash, "onDndDrop", targetDrop); // Connect the onDndDrop event to target
  07: }
  08:
  09: function targetDrop(source, nodes, copy, target) {
  10:     console.log("targetDrop");
  11:     // To verify that this is the right handler for drop event
  12:     if (dojo.dnd.manager().target !== this) {
  13:             console.log("targetDrop terminating...");
  14:             return;
  15:     }
  16:
  17:     // Remove the dropped elements
  18:     divTrash.selectAll();
  19:     divTrash.deleteSelectedNodes();
  20:
  21:     // Animate the trash icon shrink-enlarge
  22:     var propsShrink = {
  23:             width: {start: "94", end: "80", unit: "px"},
  24:             height: {start: "94", end: "80", unit: "px"}
  25:     };
  26:     var propsEnlarge = {
  27:             width: {start: "80", end: "94", unit: "px"},
  28:             height: {start: "80", end: "94", unit: "px"}
  29:     };
  30:     dojo.anim("#{id:trashIcon}", propsShrink, 250);
  31:     dojo.anim("#{id:trashIcon}", propsEnlarge, 250);
  32:
  33:     // Get all the note IDs and put them in an array
  34:     var notesIDs = new Array();
  35:     dojo.forEach(nodes, function (node, i) {
  36:             notesIDs.push(dojo.attr(node, "noteid"));
  37:     });
  38:
  39:     XSP.executeOnServer("#{id:ehDeleteDocs}", // Event handler to be executed
  40:             "#{id:dataTable}",  // Data table to be partially refreshed
  41:             {}, // onStart, onError and onComplete events
  42:             notesIDs.join(",") // Value to be submitted so that SSJS code can process it
  43:     );
  44: }
  45:
  46: XSP.executeOnServer = function () {
  47:     // Source: http://publib.boulder.ibm.com/infocenter/domhelp/v8r0/index.jsp?topic=%2Fcom.ibm.designer.domino.ui.doc%2Fwpd_controls_events.html
  48:
  49:     // the event handler id to be executed is the first argument, and is required
  50:     if (!arguments[0])
  51:             return false;
  52:     var functionName = arguments[0];
  53:
  54:     // OPTIONAL - The Client Side ID that is partially refreshed after executing the event handler
  55:     var refreshId = (arguments[1]) ? arguments[1] : "@none";
  56:     var form = (arguments[1]) ? this.findForm(arguments[1]) : dojo.query('form')[0];
  57:
  58:     // catch all in case dojo element has moved object outside of form...
  59:     if (!form)
  60:             form = dojo.query('form')[0];
  61:
  62:     // OPTIONAL - Options object containing onStart, onComplete and onError functions for the call to the
  63:     // handler and subsequent partial refresh
  64:     var options = (arguments[2]) ? arguments[2] : {};
  65:
  66:     // OPTIONAL - Value to submit in $$xspsubmitvalue. can be retrieved using context.getSubmittedValue()
  67:     var submitValue = (arguments[3]) ? arguments[3] : '';
  68:
  69:     // Set the ID in $$xspsubmitid of the event handler to execute
  70:     dojo.query('[name="$$xspsubmitid"]')[0].value = functionName;
  71:     dojo.query('[name="$$xspsubmitvalue"]')[0].value = submitValue;
  72:     this._partialRefresh("post", form, refreshId, options);
  73: }
  

ドロップされたときの処理(サーバサイド)

先ほど、要素がドロップされて、ブラウザ側で XSP.executeOnServer()が実行された。 これを受けてサーバ側で走る処理が記述されている。

  01:     <xp:eventHandler event="deleteDocs" id="ehDeleteDocs" refreshMode="partial" refreshId="dataTable" submit="false">
  02:             <xp:this.action><![CDATA[#{javascript:
  03:               var doc:NotesDocument;
  04:               var noteIDs = context.getSubmittedValue().split(",");
  05:               print("noteIDs = " + noteIDs);
  06:               for (var i=0 ; i<noteIDs.length ; i++) {
  07:                 doc = database.getDocumentByID(noteIDs[i]);
  08:             doc.removePermanently(true);
  09:             doc.recycle();
  10:             print("deleted");
  11:           }
  12:         }]]></xp:this.action>
  13:     </xp:eventHandler>  
  

うーん、よくできてるなぁ。