For Pay: I Need A Few Hours Consulting On Modular JavaScript Application Architecture
I love JavaScript! I love it. Only, my understanding of JavaScript architecture is a bit old-school. For the last few months, I've been trying hard to wrap my head around taking that understanding into the modern era with modular, decoupled, asynchronously loaded code. Since I've discovered RequireJS, I've been trying hard to think about decoupled modules and how they can be instantiated on the fly. Only, I still can't quite see how this stuff all comes together.
I'm looking to hire a modular JavaScript consultant for an hour or two just to talk to me about how the following code should be organized. We don't need to write any code, necessarily - I'm struggling at the conceptual and philosophical levels. I figure something like a screen-share (Skype, GotoMeeting, etc.) and a phone call will be a good start. Maybe some sketching. Maybe some light code.
Here's the non-trivial, yet small-in-scope practice problem I started working on. I thought it would be a good place to start; perhaps I was completely misguided:
|
|
|
||
|
|
|||
|
|
|
I put the code up on GitHub so you could see what I was doing.
If you think you have the right advice to give me, shoot me an email: ben@bennadel.com. This is for-pay, so please let me know what your hourly consulting rate is, as well.
I want to understand this stuff so freakin' bad I can taste it! :D
UPDATE: December 22, 2011
After exchanging a few emails with Addy Osmani, I started to think through the event-driven communication a bit better. In this application, there are two view controllers: Agent and Bucket. The communication and interaction between these two elements is facilitated by a top-level controller, DragDrop:
- Agent announces dragStarted event.
- Controller tells all Buckets to start tracking said Agent so that they can tell when the Agent has hovered over or has been dropped on them, respectively.
- Buckets listen to "moved" event on given Agent.
- As Agent moves, Bucket determines if Agent is over drop-zone. If so, the drop-zone is activated.
- Bucket listens to "dragEnded" event.
- If Agent is over drop-zone when dragEnded event is fired, Bucket announces a "dropped" event.
- Controller responds to "dropped" event by telling said Bucket to take ownership (pushItem) of said agent.
Once an Agent becomes part of a given Bucket, the Bucket listens for reasons to remove the Agent.
- Bucket listens for dragStarted event.
- Bucket announces a "popped" event.
- Controller listens for popped event and tells given Bucket to release (popItem) the given Agent.
- Controller re-attaches agent to primary container.
If you want to see this in action, try my online demo here.
Right now, this does basically what I had envisioned. Other than that, though, I make no promise about the code (which can be seen on GitHub).
|
|
|
||
|
|
|||
|
|
|
As always, I'm looking for feedback. I'd be more than happy to pay for a Code Review / analysis if you think you have some good insights and advice to offer. Please feel free to shoot me an email.
UPDATE: December 23, 2011
Today, I wanted to try and add a second Bucket to the application surface. I didn't think this would add much complexity as the Buckets are fairly modular and the top level DragDrop controller already has a sense of a Bucket "collection." What I did have to do, however, was add a way for the DragDrop controller to set the position of the bucket when it gets attached to the rendered container. To accomplish this, I simply added a "position" argument to the attachContainer() method:
- Bucket.attachContainer( container, initialPosition )
For sake of simplicity, the initialPosition object just gets passed directly to the css() method of the root DOM (Document Object Model) node. This means that the calling object (DragDrop controller) needs to explicitly define the top/right/bottom/left CSS properties.
Also, in response to Randall's comment below, I added an "ingredients" widget to each bucket that will list out the labels of the contained agents that have been dropped over the dropzone. Since I was already keeping references to the controllers, all I needed to do was add a getLabel() method to the agent.
Making these changes to the code was actually quite simple. I guess this is one of the benefits of modular code - the focused responsibility of each module makes the code easier to maintain and extend.
|
|
|
||
|
|
|||
|
|
|
Meta to the code, I also added a Require.js build / optimization file to the project. Now, in the scripts directory, there is an app.build.js file that tells the Require.js optimizer (r.js) how to find and compile the named modules. Now, the live demo only loads a single JavaScript file, main-build.js. This file contains all of the JavaScript modules, including the inlined-text of the HTML templates.
Reader Comments
Hi - is your desire to understand more about jQuery Drag n' Drop or about "modular javascript" in general? The video seems to be focused on drag\drop while the text of the blog post is focused on modular js. Just wondering.
PS - love the site!
It was dark when I woke. This is a ray of sunihsne.
@Pat,
The drag and drop functionality is just incidental to the context in which I was trying to learn modularity. Under the hood, the code is still [trying] to be modular with communication all done via event systems and method invocation. If you look at the GitHub project, you'll see that the agent (square) and the bucket are being controlled by view modules. I'm currently working on creating a controller module to coordinate the communication between the two view instances (of which there may end up being N-instances).
Thanks for the kind words! I try to keep the site updated with new concepts and thought experiments :)
@Josey,
Ha ha, thanks :)
Dude, I wish I could help you as much as you have helped everyone else. But I am glad you found your help. Good luck!!!
(after 22 Dec update) Wow! PERFECT timing. I am dazzled by JQ's drag/drop and definitely want to use this in my code.
(Your demo) I drag-n'-dropped items 1, 2, 3, and 4 into the box. Then I dragged three and four back out. I then dragged #5 back in (thus the counter states "3").
Now what I need to see is upon a "submit", I want to know that #1, 2, and 5 are inside the drop zone. Have you made it that far yet? Or, what do I poll in order for the response to be, "1, 2, & 5"? Additionally, can I also know that 3, 4, and 6 are outside the drop zone?
@Randall,
I have updated the code / demo / post today to incorporate the concept of "ingredients". I couldn't think of a better word :) Basically, when you drag an agent onto the bucket, it now lists out the contained labels in addition to the simple count.
@All,
I've also added a Require.js build / optimization configuration which was kind of cool. The demo now only loads one JavaScript file.
@All,
Trying to continue the exploration of modular JavaScript application development - adding some "Model" stuff:
www.bennadel.com/blog/2304-Experimenting-With-A-JavaScript-Gateway-To-A-Remote-ColdFusion-Persistence-API.htm
Starting with a Gateway object to interface between local code and a remote ColdFusion persistence store.
Just wanted you to know I have added you to my bookmarks. I've seen your other blog topics too and I can say you've got great ideas. Keep it up!