Login Register

dnd: need help with source (moving items and manipulating source)

hello,

my goal is to move items from one source to the next. the destination-source should only contain a maximum of 3 items. so i have to change accept-property of the destination-source (in my case "test3"). additionally want to implement a undo function. i've tested some sourceode, but it doesn't work. after changing the accept-property of the source "test3" i got the following errormessages:
- nodes[0].parentNode has no properties

before it has worked. additionally the item doesn't drop in the destination-source. please give me a hint how to fix the issue and get the undo-functionality implemented.

here is my code:

<script type="text/javascript" src="dojo/dojo.js.uncompressed.js" djConfig="parseOnLoad: true, isDebug: false, usePlainJson: true">
    </script>
    <script type="text/javascript" src="dojo/dnd/Selector.js" djConfig="parseOnLoad: true, isDebug: false, usePlainJson: true">
    </script>
    <script type="text/javascript">
      dojo.require("dojo.dnd.Source");
      dojo.require("dijit.Dialog");
      dojo.require("dojo.parser");
      dojo.require("dijit.form.Button");
      dojo.require("dojo.dnd.Selector");
     
      var my_all_nodes, test1, test2, dataItems;
      var count = 0;
     
      var my_node_creator = function(data, hint){
          var types = [];
          var node = dojo.doc.createElement("div");
          types.push(data.type);
          node.innerHTML = "<img class='dojoDndItem' src='" + data.img_url + "' id='" + data.id + "' jsId='" + data.id + "'>";
          node.id = dojo.dnd.getUniqueId();
          return {
              node: node,
              data: data,
              type: types
          };
      };
     
      function myGetAllItems(){
          test1.selectAll();
          test1.deleteSelectedNodes();
          test1.clearItems();
          test1.insertNodes(false, [{
              "type": "blue",
              "id": "blue1",
              "img_url": "images/BLUE.png"
          }, {
              "type": "blue",
              "id": "blue2",
              "img_url": "images/BLUE.png"
          }, {
              "type": "blue",
              "id": "blue3",
              "img_url": "images/BLUE.png"
          }, {
              "type": "blue",
              "id": "blue4",
              "img_url": "images/BLUE.png"
          }, {
              "type": "blue",
              "id": "blue5",
              "img_url": "images/BLUE.png"
          }]);
          test2.selectAll();
          test2.deleteSelectedNodes();
          test2.clearItems();
          test2.insertNodes(false, [{
              "type": "red",
              "id": "red1",
              "img_url": "images/RED.png"
          }, {
              "type": "red",
              "id": "red2",
              "img_url": "images/RED.png"
          }, {
              "type": "red",
              "id": "red3",
              "img_url": "images/RED.png"
          }, {
              "type": "red",
              "id": "red4",
              "img_url": "images/RED.png"
          }]);
      }
     
      var init = function(){
          test1 = new dojo.dnd.Source("test1", {
              creator: my_node_creator,
              accept: ["red", "blue"]
          });
          test2 = new dojo.dnd.Source("test2", {
              creator: my_node_creator,
              accept: ["red", "blue"]
          });
          dojo.subscribe("/dnd/drop", function(source, nodes, iscopy){
              if (nodes[0].parentNode.id == "test3") {
                  count++;
              }
              if (source.node.id == "test3" && count == 3) {
                  dojo.byId("test3").attributes["accept"].value = ["blue", "red"];
                  dojo.parser.parse();
              }
              if (source.node.id == "test3") {
                  count--;
              }
              if (count == 3) {
                  dojo.byId("test3").attributes["accept"].value = [];
                  dojo.parser.parse();
              }
             
          });
          dojo.subscribe("/dnd/start", function(source, nodes, iscopy){
          });
         
         
          myGetAllItems();
         
          // doesn't work
     
          /*
           dataItems = dojo.map(test1.getSelectedNodes(), function(node){
           return test1.getItem(dojo.byId("blue1").parentNode.id);
           });
           test3.insertNodes(false, dataItems);
           test1.deleteSelectedNodes();
           */
      };
     
      function lastMoveAgain(){
          alert("should make laste move again");
      }
     
      function undoLastMove(){
          alert("should undo last move");
      }
     
     
      dojo.addOnLoad(init);
    </script>
  </head>
  <body style="font-size: 12px;" class="tundra" id="myBody">
    <table border="1">
      <tr>
        <td>
          <b>Source 1</b>
        </td>
        <td>
          <b>Source 2</b>
        </td>
        <td>
          <b>Source 3</b>
        </td>
      </tr>
      <tr>
        <td>
          id:test1
        </td>
        <td>
          id:test2
        </td>
        <td>
          id:test3
        </td>
      </tr>
      <tr>
        <td>
          unlimited
        </td>
        <td>
          unlimited
        </td>
        <td>
          max 3 items
        </td>
      </tr>
      <tr>
        <td>
          <div jsId="test1" id="test1" class="source" style ="background-color: green; width: 100px; height: 320px; float: left; margin: 5px;">
          </div>
        </td>
        <td>
          <div jsId="test2" id="test2" class="source" style ="background-color: gray; width: 100px; height: 320px; float: left; margin: 5px;">
          </div>
        </td>
        <td>
          <div jsId="test3" id="test3" class="source" style ="background-color: brown; width: 100px; height: 320px; float: left; margin: 5px;"" dojoType="dojo.dnd.Source" accept="red,blue">
          </div>
        </td>
      </tr>
      <tr>
        <td onclick="undoLastMove()" style="background-color:yellow; cursor:pointer; text-align:center;">
          UNDO
        </td>
        <td onclick="lastMoveAgain()" style="background-color:magenta; cursor:pointer; text-align:center">
          REDO
        </td>
        <td>
        </td>
      </tr>
    </table>
  </body>

regards,
dura4cell

I think you approach is

I think you approach is wrong — you are modifying attributes through "attributes" (would it work in IE???) and reparsing stuff. Sounds brittle and heavy-weight. All you need to do is to override Source.checkAcceptance() method on your target that should be limited to 3 elements. Possible way to do it:

var oldCheckAcceptance = test3.checkAcceptance;
tests3.checkAcceptance = function(source, nodes){
    if(this == source){ return true; } // always accept your own nodes
    var counter = 0;
    this.forInItems(function(){ ++counter; }); // count your items
    if(counter >= 3){ return false; }  // reject extra items
    returns oldCheckAcceptance.call(this, source, nodes); // proceed normally
};

BTW, "jsId" attribute works only for elements with "dojoType" when parser instantiates objects.

The undo functionality is highly application dependent, so it is really up to you to implement it.

and how i set the attribute directly?

hallo eugene,

it works - many thanks! if i have a big grid, and i want to change the property after creating the item, how i do this? i think in this case, it's not the best way to override every checkAcceptance-method.

regards,
goran

If you have several similar

If you have several similar objects consider subclassing dojo.dnd.Source overriding proper methods — it is a simple as that. In fact dojo.dnd.Target was done this way. If you have a big grid of targets, consider changing the paradigm, and represent it with a specialized Source — this way you are saving on unnecessary event handlers, which can slow the application considerably even when not actively in use.

changing attribute accept possible?

hello eugene,

thanks for your answer. first i've changed the attribute as following:

dojo.byId("test3").attributes["accept"].value = ["blue", "red"];
dojo.parser.parse();

is it the right way, if i want to do this? or is it not possible? how i can change the attribute?

regards,
dura4cell

No. Just override the

No. Just override the method.

change without overriding the method

- i want to do this without overriding the method (if possible) ;-) :-) -

Not recommended. Override

Not recommended. Override the method. It is JavaScript not Java ;-), don't be afraid to do it.

additional question

hello eugene,

many thanks for your help and your patience. above in the code i've tried in the constructor the following first (meanwhile you don't see it, because it has not worked):

node.id = "myId";

why it doesn't work? can i give the new "item" my own id?

regards,
dura4cell

You can. It should work.

You can. It should work. Just make sure it is unique across the container, otherwise you will have a name clash in the internal dictionary.

i must test - tomorrow. thanks eugene!!!!

;-) :-)

if works for initialization, but how to do for dnd-operations

hello eugene,

i've changed my "constructor" as following:

var my_node_creator = function(data, hint){
  var types = [];
  var node = dojo.doc.createElement("div");
  dojo.addClass(node, "dojoDndItem");
  types.push(data.type);
  node.innerHTML = "";
  node.id = data.id;		
  return {
    node: node,
    data: data,
    type: types
  };
};

now it works for the initialization, but not for dnd-operations.

//initialization start
...
test2.insertNodes(false, [{
    "type": "red",
    "id": "red1",
    "img_url": "images/RED.png"
  }, {
    "type": "red",
    "id": "red2",
    "img_url": "images/RED.png"
  }, {
    "type": "red",
    "id": "red3",
    "img_url": "images/RED.png"
  }, {
    "type": "red",
    "id": "red4",
    "img_url": "images/RED.png"
  }
]);
...
//initialization end

how i can give dojo for the dnd-operation a new id?

regards,
dura4cell

Need more info...

Is your source's creator function called on the drag? Is you target's creator function called on the drop?

"how i can give dojo for the dnd-operation a new id?" — what does it mean? In your creator function your create a node and assign an id to it. Do you mean it doesn't work?

here is the sourcecode ;-)


  
    Shopping Cart
    
      @import "dijit/themes/tundra/tundra.css";
      
      .target {
          border: 2px dotted gray;
          width: 200px;
          height: 350px;
          background-color: pink;
          -moz-border-radius: 8pt 8pt;
          radius: 8pt;
          overflow: auto
      }
      
      .source {
          height: 400px;
          width: 270px;
          background-color: ivory;
          -moz-border-radius: 8pt 8pt;
          radius: 8pt;
      }
      
      .itemtable {
          border: 1px solid gray;
          text-align: left;
          background-color: rgb( 153, 255, 255 );
          width: 250px;
          font-size: 12px;
          -moz-border-radius: 8pt 8pt;
          radius: 8pt;
      }
      
      .avatartable {
          border: 1px dotted gray;
          text-align: left;
          background-color: rgb( 153, 255, 255 );
          width: 150px;
          font-size: 10px;
          -moz-border-radius: 8pt 8pt;
          radius: 8pt;
      }
      
      .avatartable img {
          width: 25px;
          height: 25px
      }
      
      .targettable {
          border: 1px dotted gray;
          text-align: left;
          background-color: ivory;
          width: 150px;
          font-size: 10px;
          -moz-border-radius: 8pt 8pt;
          radius: 8pt;
      }
      
      .targettable img {
          width: 25px;
          height: 25px
      }
      
      .rating {
          color: green;
          font-family: Serif;
          font-size: 14px;
      }
      
      .dojoDndItemOver {
          background: skyblue;
          border: 1px dotted gray;
      }
      
      .dojoDndItemBefore {
          border-top: 2px dotted black;
      }
      
      .dojoDndItemAfter {
          border-bottom: 2px dotted black;
      }
      
      .target .dojoDndItemAnchor {
          border: 1px solid black;
      }
      
      .dojoDndAvatar {
          font-size: 75%;
          color: black;
      }
      
      .dojoDndAvatar td {
          padding-left: 20px;
          padding-right: 4px;
          height: 20px
      }
      
      .dojoDndAvatarHeader {
          background: #ccc;
          background-repeat: no-repeat;
      }
      
      .dojoDndAvatarItem {
          background: #eee;
      }
      
      .dojoDndMove .dojoDndAvatarHeader {
          background-image: url('dojo/resources/images/dndNoMove.png');
      }
      
      .dojoDndMove .dojoDndAvatarCanDrop .dojoDndAvatarHeader {
          background-image: url('dojo/resources/images/dndMove.png');
      }
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
      dojo.require("dojo.dnd.Source");
      dojo.require("dijit.Dialog");
      dojo.require("dojo.parser");
      dojo.require("dojo.back");
      dojo.require("dijit.form.Button");
      dojo.require("dojo.dnd.Selector");
      dojo.require("dojo.dnd.Manager");
      
      var my_all_nodes, test1, test2, test3, dataItems, oldCheckAcceptance;
	  var temp; //for different debugging
	  var movesPointer = 0;
	  var moves = new Array();
	  
      
      var state = {
          back: function(){
              alert("Back was clicked!");
          },
          forward: function(){
              alert("Forward was clicked!");
          }
      };
      
      var my_node_creator = function(data, hint){
          var types = [];
          var node = dojo.doc.createElement("div");
		  dojo.addClass(node, "dojoDndItem");
          types.push(data.type);
          node.innerHTML = "";

          //works, but if you look at the ids (on a simple dnd-operation
          //you see that the source (same item taken from the sourcecontainer) and 
          //target (same item drop in the targetcontainer) have different (!) ids
          //how can i resolve it in this constructor with my id. 
          //the id should not change!
          //node.id = dojo.dnd.getUniqueId();		

          node.id = data.id;				//doesn't work

          return {
              node: node,
              data: data,
              type: types
          };
      };
      
      function myGetAllItems(){
          test1.selectAll();
          test1.deleteSelectedNodes();
          test1.clearItems();
          test1.insertNodes(false, [{
              "type": "blue",
              "id": "blue1",
              "img_url": "images/BLUE.png"
          }, {
              "type": "blue",
              "id": "blue2",
              "img_url": "images/BLUE.png"
          }, {
              "type": "blue",
              "id": "blue3",
              "img_url": "images/BLUE.png"
          }, {
              "type": "blue",
              "id": "blue4",
              "img_url": "images/BLUE.png"
          }, {
              "type": "blue",
              "id": "blue5",
              "img_url": "images/BLUE.png"
          }]);
          test2.selectAll();
          test2.deleteSelectedNodes();
          test2.clearItems();
          test2.insertNodes(false, [{
              "type": "red",
              "id": "red1",
              "img_url": "images/RED.png"
          }, {
              "type": "red",
              "id": "red2",
              "img_url": "images/RED.png"
          }, {
              "type": "red",
              "id": "red3",
              "img_url": "images/RED.png"
          }, {
              "type": "red",
              "id": "red4",
              "img_url": "images/RED.png"
          }]);
          test3.selectAll();
          test3.deleteSelectedNodes();
          test3.clearItems();
      }
      
      var init = function(){
          test1 = new dojo.dnd.Source("test1", {
              creator: my_node_creator,
              accept: ["red", "blue"]
          });
          test2 = new dojo.dnd.Source("test2", {
              creator: my_node_creator,
              accept: ["red", "blue"]
          });
          test3 = new dojo.dnd.Source("test3", {
              creator: my_node_creator,
              accept: ["red", "blue"]
          });
          dojo.subscribe("/dnd/drop", function(source, nodes, iscopy, target){
			if(source.node.id != target.node.id){
				moves.push([source.node.id, target.node.id, nodes[0].id]);
				document.getElementById("moves").innerHTML = document.getElementById("moves").innerHTML + source.node.id + "," + target.node.id + "," + nodes[0].id + "";
			}
          });
          dojo.subscribe("/dnd/start", function(source, nodes, iscopy){
          });
          
          myGetAllItems();
          
          oldCheckAcceptance = test3.checkAcceptance;
          
          test3.checkAcceptance = function(source, nodes){
              if (this == source) {
                  return true;
              }; // always accept your own nodes
              var counter = 0;
              this.forInItems(function(){
                  counter++;
              }); // count your items
              if (counter >= 3) {
                  return false;
              } // reject extra items
              return oldCheckAcceptance.call(this, source, nodes); // proceed normally
          };
      };
      
	  function removeItem(source, id){
		var node = dojo.byId(id);
		temp = node;
		delete source.selection[id];
		source.delItem(id);
		dojo._destroyElement(node);	  	
	  }

	  function addItem(target, id){
		var matchReg = /blue/;
		var tempId = dojo.dnd.getUniqueId();
		if(matchReg.exec(id) == "blue")
			target.insertNodes(false, [{
			  "type": "blue",
			  "id": tempId,
			  "img_url": "images/BLUE.png"
			}]);
		var matchReg = /red/;
		if(matchReg.exec(id) == "red")
			target.insertNodes(false, [{
			  "type": "red",
			  "id": id,
			  "img_url": "images/RED.png"
			}]);
		//var tempNode = dojo.byId(tempID);
		//tempNode.id = id;
	  }
	  
      function undoLastMove(){
	  	alert(moves[moves.length-1][2]);
		var tempId = moves[moves.length-1][2];

	  	alert(moves[moves.length-1][1]);
		if(moves[moves.length-1][1] == "test1")
			removeItem(test1, tempId);
		else if(moves[moves.length-1][1] == "test2")
			removeItem(test2, tempId);
		else if(moves[moves.length-1][1] == "test3")
			removeItem(test3, tempId);
	  	
	  	alert(moves[moves.length-1][0]);
		if(moves[moves.length-1][0] == "test1")
			addItem(test1, tempId);
		else if(moves[moves.length-1][0] == "test2")
			addItem(test2, tempId);
		else if(moves[moves.length-1][0] == "test2")
			addItem(test3, tempId);
		moves.pop();
      }
      
      
      dojo.addOnLoad(init);
      
      function runCode(){
          eval(document.getElementById("Eingabe").value);
          document.getElementById("Eingabe").value = "";
      }
    
  
  
    
      

Test command

Source 1 Source 2 Source 3 moves id:test1 id:test2 id:test3 unlimited unlimited max 3 items unlimited
UNDO REDO

hello eugene,

this code doesn't work. i've placed the comment directly in the code above. you are not able to make a simple dnd-operation (take item from one container and drop it in another one). hope you will have a good weekend.

regards,
dura4cell

Don't include big chunks of

Don't include big chunks of code inline — it is hard to read, and frequently mangled to be pasted in the file and debugged. Please pair it down to bare bones making it really a minimalistic example, and send it to me by e-mail to eugene at this domain. In the future you may open a ticket for that, and then attach the minimalistic example to it, which demonstrates the problem clearly.

In general it is a useful exercise similar to explaining the problem in plain words to a fried — if you cannot remove the fluff yet demonstrate the problem you may have the problem in the fluff. ;-)

here the minimalistic errordescription and code

hello eugene,

hope it's not to much now, and i will paste in the future again minimalistic code ;-)

var my_node_creator = function(data, hint){
          var types = [];
          var node = dojo.doc.createElement("div");
          dojo.addClass(node, "dojoDndItem");
          types.push(data.type);
          node.innerHTML = "";
          //node.id = dojo.dnd.getUniqueId();		//works
          node.id = data.id; //doesn't work
          return {
              node: node,
              data: data,
              type: types
          };
      };
      
      function myGetAllItems(){
          test1.selectAll();
          test1.deleteSelectedNodes();
          test1.clearItems();
          test1.insertNodes(false, [{
              "type": "blue",
              "id": "blue1",
              "img_url": "images/BLUE.png"
          }]);
      }
      
      var init = function(){
          test1 = new dojo.dnd.Source("test1", {
              creator: my_node_creator,
              accept: ["red", "blue"]
          });
          test2 = new dojo.dnd.Source("test2", {
              creator: my_node_creator,
              accept: ["red", "blue"]
          });
          myGetAllItems();
      };

so - it's the same source and i've placed the comment directly in the code above. you are not able to make a simple dnd-operation (take item from one container and drop it in another one). how to write a constructor which works for init and for later. i don't want to change the id with each dnd-operation. or must i change the id with each dnd-operation?

regards,
dura4cell

Could you add a following

Could you add a following line after the comment and before the assignment and look at it in the firebug console?

console.debug(dojo.byId(data.id));

I suspect that there is an id clash, which prevents DnD from doing something important.

update with console.debug

hello eugene,

after loading the page you get only the following:
null

the result of the try of dnd-operation is:

if you try it with the working code, you see that the id is changed by dojo.

regards,
dura4cell