Regardless of whether or not I can test it in Dart, I still need to be able to drop project files into ICE Code Editor. So as much as it pains me to do this… tonight I will add that feature to ICE without a test to guide me.
After last night's vain attempt to TDD the behavior into existence, I have the outline of how this will look:
Element.
dropEvent.
forTarget(document).
listen((event) {
event
..preventDefault()
..stopPropagation();
var file = event.dataTransfer.files.first;
});
Nothing too fancy there—in Dart or JavaScript. It establishes an event stream for drop events. When such an event occurs, the listener callback is invoked with the drop event. I prevent the default action (which would be to open the file in the browser) and then get started with reading the contents of the dropped file.As of last night, I had only gotten as far as getting a reference to the uploaded file. I was unable to populate the
dataTransfer
property in a test, but it ought to work just fine in real life. The API for file readers in Dart seems to follow the JavaScript exactly. So my next step is to create a FileReader
object that reads the dropped file as text:
// ...
listen((event) {
event
..preventDefault()
..stopPropagation();
var file = event.dataTransfer.files.first;
new FileReader()
..onLoad((read_event) {
print(read_event.target.result);
})
..readAsText(file);
});
If everything is working correctly, when I drop a file into ICE:I should now see the contents of the file printed to the console.
Instead, I see the contents of the file that I just uploaded:
Arrgh! Is drop just completely broken in Dart?!
Er, no. It's just me.
In the end, I have to boil this down into the simplest JavaScript version that is possible before I realize that it still does not work. It seems that it is not sufficient to prevent the drop event—I also have to prevent the “dragover” event:
<body> <h1>Hello</h1> </body> <script> document.addEventListener('dragover', function (e) { e.preventDefault(); }); document.addEventListener('drop', function(e) { e.preventDefault(); var file = e.dataTransfer.files[0]; console.log('file.name: ' + file.name); console.log('file.type: ' + file.type); var reader = new FileReader(); reader.onload = function (e) { console.log(e.target.result); }; reader.readAsText(file); }); </script>And, in fact, I need to prevent both the dragover and drop default event behavior so that the dropped file does not open in the browser:
DOM coding: catch the fever!
Comforted in the knowledge that my trouble is with DOM coding, not Dart, I switch back to Dart. There, I also prevent the default dragover event behavior:
document
..onDragOver.
listen((event) {
event.preventDefault();
})
..onDrop.
listen((event) {
event.preventDefault();
var file = event.dataTransfer.files.first;
print('file.name: ${file.name}');
print('file.type: ${file.type}');
new FileReader()
..onLoad.
listen((read_event) {
print(read_event.target.result);
})
..readAsText(file);
});
With that, I can successfully read the pertinent information from the dropped file:No doubt there is a very good reason for needing to prevent two different events. I do not really want to know what it is. Really. Mostly I would like Dart or one of its libraries to make this go away. Regardless, I believe that I have a sufficient understanding of how this works to enable me to hook it into ICE—even if I cannot test it. That seems a fine stopping point for tonight.
Day #873
Cheers bro, good job
ReplyDelete