https://www.codementor.io/officehours/2913050604/angular-plunker
Geoff Goodman is the creator of Plunker, which is an entirely Angular-built way to support...you guessed it, Angular projects! The real-time previews and strong sense of feedback has made Plunker a very popular online community for creating, collaborating on and sharing web development ideas.
We'll be sitting down with Geoff to discuss the thought process and design decisions involved in building a low-level angular drag-and-drop library for Plunker.
Codementor Plunker AngularJS Office Hours - Making things draggable in plunker
1. Making stuff draggable in
Plunker (and other places)
Creating a drag and drop directive for
Angular.js
CodeMentor – 9 March 2015
Geoff Goodman (@filearts)
2. Who is this guy?
Hi, I’m Geoff Goodman
Twitter: @filearts
Github: @ggoodman
During the day, I value businesses at
EY (Ernst & Young)
At night, I build Plunker which is an in-browser
editor for front-end code.
3. • Like I said, Plunker is an in-browser code
editor
• A code editor is only half done if you can’t
drag things around…
7. Prior art
• https://github.com/codef0rmer/angular-
dragdrop - Wraps jQuery UI
• https://github.com/a5hik/ng-sortable -
Comprehensive, dependency-free list
reordering and transferral
• https://github.com/logicbomb/lvlDragDrop -
Also low-level, dependency-free drag and
drop based on html5 api
8. Figuring out what I needed
• I know that I have things that I want to drag
• I know that there are only certain places to
drop stuff
• I know that only certain things can be dropped
in certain places
9. Making this in Angular.js
• Angular lets me define how I want certain
things to behave straight in the markup
– These are called directives
– They are not the easiest thing to learn and
understand
– But they are crazy powerful
• HTML5 also defines a great API for drag and
drop (Spec)
10. Overcome API limitations
• The drag and drop pioneer is now a black
sheep
• IE only allows Text and URL as the data types
• Using our own layer on top, we can support
any data
11. Angular internals
• Three directives:
1. drag-container
2. drop-container
3. drop-area
• And one service to share drag state between
the directives: $dragging.
12. What I came up with:
<div drag-container="model" mime-type="text/whatever">
<!-- Container that is draggable -->
</div>
<div drop-container accepts="['text/x-plunker-file']">
<div drop-target="left" on-drop=“dropL(data)"></div>
<div drop-target="right" on-drop=“dropR(data)"></div>
<!-- You can put any markup here -->
</div>
13. drag-container
• Determines the data being dragged (drag-
container attribute) and the type of that data
(mime-type attribute)
• Call preventDefault() on the dragstart
event and the browser will start dragging! In
the event handler, I set the drag data.
<div drag-container="model" mime-type="text/whatever">
<!-- Container that is draggable -->
</div>
14. drop-container
• Defines a region that will accept any drop that
passes the ‘accept’ test.
• Accepts event handlers, figures out
appropriate target and delegates event.
<div drop-container accepts="['text/x-plunker-file']">
<div drop-target="left" on-drop=“dropL(data)"></div>
<div drop-target="right" on-drop=“dropR(data)"></div>
<!-- You can put any markup here -->
</div>
15. Delegating to targets
• Create a virtual point for each target
• Calculate minimum distance b/w mouse and
point
top top-right
right
bottom-rightbottombottom-left
left
top-left
center
16. drop-target
• Defines a potential region of the drop-
container that can respond to drag and
drop events
• Can by styled by css to give drop hints
<div drop-container accepts="['text/x-plunker-file']">
<div drop-target="left" on-drop=“dropL(data)"></div>
<div drop-target="right" on-drop=“dropR(data)"></div>
<!-- You can put any markup here -->
</div>
17. Linking the directives up
• drop-targets require an instance of a
drop-container in their DDO:
.directive("dropTarget", ["$parse", function ($parse) {
return {
restrict: "A",
require: ["^dropContainer", "dropTarget"],
controller: "DropTargetController",
controllerAs: "dropTarget",
link: function ($scope, $element, $attrs, ctrls) {
var dropContainer = ctrls[0];
var dropTarget = ctrls[1];
var anchor = $attrs.dropTarget || "center";
dropContainer.addDropTarget(anchor, dropTarget);
18. drop-targets are just attachment
points
• They are not designed to have content
• When active, an -active css class is added
so that they can: e.g. be shown or given
dashed borders
• Creating them gives you regions of the drop-
container that can respond to events that
relate only to that region
19. Event handling
• HTML5 gives us a bunch of events. I chose a
few to expose:
– on-drag-start, on-drag-end
– on-drag-enter, on-drag-leave
– on-drag-over
– on-drop
• Angular can inject special objects exposed to
those handlers, like $event and data
<div drop-container
accepts="['text/x-dragular-piece']"
on-drag-enter="game.swap($index, data)">
20. What this let’s you do
• Trello-style storyboards lists: DEMO
• Re-orderable lists by:
– Having a leading and trailing drop-target for each
list item
– Splicing before or after the target element on-
drag-enter
• Sliding puzzle: DEMO
• More complex behaviours: DEMO
22. But what about the code?
• The code is MIT-licensed on github:
https://github.com/ggoodman/angular-drag-
drop
• Feel free to ping me if you have questions on
Twitter: @filearts or @plnkrco
I’m not really good with Powerpoint. Bear with my crappy black and white presentation and gif animations
In case it wasn’t obvious, instead of dragging files, some useless selection range happened.
In a modern editor, you should be able to drag files around in a file tree. (Show video)
Also, wouldn’t it be handy if you could re-organize your workspace however you wanted? (Show video)
Things to drag: files, directories, panes
I also thought it would be great if you could drag files to and from the desktop
But you can’t drag a folder into a file, you can’t drag a folder into a child folder etc..
You need to be able to define what can be dragged where
Little segway into Angular (this won’t be a deep dive into Angular, sorry!)
The HTML5 api is a little unintuitive to begin with, but is really powerful and actually quite simple to use!
- Need to cancel events to opt-in
A service is an instance of an object that can be injected into other bits of an angular application. A service is a singleton instance so injecting the $dragging service will always give me the same instance. I use this to store a reference to the object identified as being dragged. I can also set the full mime-type of the data being dragged to work around a quirk of IE’s.
I also need to call some methods of the dataTransfer object to tell the browser what the mime-type of my data is and pass the browser the data itself. Even though IE pioneered this api, it has moved forward since then. In IE, you cannot set the type to anything but ‘text’ and ‘url’. Fortunately, by having a service instance
So for the workspace of the next version of Plunker, I wanted people to be able to reorganize their workspace in a simple and intuitive way. A tiling window manager is an appropriate example of what I wanted. You can drag a file or pane into the workspace, split an existing pane horizontally, vertically or simply replace the pane depending on where you drop within the overall pane’s surface.
For the plunker workspace, I have top, right, bottom and left targets that will trigger splits. I have a center target that will trigger a pane replace.
The dr
In Angular, you can use the $parse service to parse the value of your element’s attributes into a function that can be run on any scope-like object and another ‘locals’ object and will run the angular expression as if it was in the context defined by the $scope and locals.
The injectable $event and data object are made available via this ‘locals’ concept.
The library uses this mechanism for the events listed on this slide.
Note that ‘on-drag-start’ and ‘on-drag-end’ are only available to ‘drag-containers’ whereas all the other events are only available on ‘drop-container’ and ‘drop-target’s.