1

Use case:
For an administration UI, I want an interactive table widget with a number of behaviors:

  • Collapse / expand (yes, this means the rows are a hierarchy)
  • Update of data in a child cell based on a parent cell.
  • Hiding or revealing of a row or an entire sub-tree based on a value update.
  • Two or more form elements in each row, that may depend on each other. E.g. a checkbox that will mess with the value of a text field in the same row.
  • DOM manipulation on newly created rows. E.g. the checkbox might be turned into a more visual kind of widget. Or

In another use case, I have the following additional behaviors:

  • Adding and removing rows, manually or automatically.
  • Drag and drop of rows. (probably not in combination with the collapse/expand, but who knows)

The problem:

  • Ideally, we want each of these behaviors to be implemented as a separate and independent module / plugin. BUT
  • If one behavior changes the DOM, then other behaviors need to know about it. E.g. if you insert a row, then the collapse/expand needs to know, to decide whether the new row should be shown or hidden. And the newly added row needs new event listeners from various other behaviors.
  • There might be behaviors that are triggered by the state of other behaviors. E.g. each row might contain a text field, and depending on the value, the row must be moved up or down (drag/drop).

The question:

I had to deal with this kind of problem a few times and I generally managed to get it work. However, I always hated the architecture that came out of it.

What I'd like is an approach that allows to create the different behaviors as independent modules, and then somehow plug them together.

Some more specific questions:

  • Where should each of the behaviors keep its state information
  • What about state information for a specific table row? Should it be stored as a variable on the DOM element? (e.g. $('tr')[5].isExpanded = true) or rather in a data container in the module itself?
  • How can modules know about DOM updates?
  • How should modules talk to each other?
  • How should the code of each module be structured? What kind of API should it expose?
  • How to handle multiple instances of this kind of widget on one page? (In this case, global instances don't work)

Example: https://drupal.org/project/multicrud (sorry, all I have atm is Drupal modules)

migrated from stackoverflow.com Jan 2 '14 at 21:55

This question came from our site for professional and enthusiast programmers.

  • you can try and use jQuery datatables for drupal. drupal.org/project/datatables – Arvind Sridharan Jan 7 '14 at 5:23
  • @Arvind: Thanks, this looks interesting, but not exactly what I need. And the question was more about patterns to organize my code, than existing solutions to a specific problem. – donquixote Jan 7 '14 at 15:52
1

I recently had to develop something vaguely similar. I ended up using jQuery UI's widget factory, as in, I developed widgets.

You can read a lot about how jQuery UI widgets work. If you want a really complicated example, look at http://silk.codeplex.com/. This is all about a very modern single page interface built on jQuery (and an ASP.NET MVC server backend) and they answer all kinds of architectural questions. Despite of it being ASP.NET MVC, there are lots of cool JavaScript to learn.

Here it is in a few words of my own. A widget is a jQuery plugin, but it has some added jQuery UI functionality. A widget instance cannot be created out of "thin air", it is added on top of a pre-existing DOM element. The widget adds a certain behavior on top of that DOM element. This is a nice two-layer architecture actually: the DOM element is the presentation, the widget instance is the logic behind it.

jQuery UI widgets provide the separation you need. Each widget is a different module, a different class, with its own constructor/destructor lifecycle, custom events, properties, options, etc.

The fact that you want to listen for DOM updates sounds bad. The DOM shouldn't drive your widgets, it should be the other way around. You shouldn't just add new DOM elements, instead you should create a DOM element, add a widget on top of it, and then let the widget do the work: put its element somewhere inside the document, notify other widgets, etc.

jQuery UI widgets support event triggering, DOM events and your custom events as well. It allows you to loosely couple your widgets. For example, when you create widget A, you might want to notify widget B about it, this is tight coupling. Instead, widget A could trigger an event, and if there is a widget B, it will get the message, this is loose coupling. This makes life a lot easier, and it also makes testing easier. There is a similar approach in Silk, they created a very cool event notification system, but I found that it's way too complex for my needs when I have very few events and subscriptions.

A widget instance is also a perfect place to store its state.

I guess that's most if not all of your questions. Please feel free to ask more specific questions. I spent a lot of time thinking about the architecture of my rich application, and I really love what I ended up with.

  • Thanks for the answer! The project this is for is taking a break atm, I will probably have more questions when I'm back on it. – donquixote Jan 27 '14 at 17:51
  • "The fact that you want to listen for DOM updates sounds bad." - a simple example would be if I have one behavior that adds new table rows, and another behavior that needs to attach itself to each table row. – donquixote Jan 27 '14 at 17:54
  • In your example, the entity that generates the rows, could publish a row-created event, nice and simple. If you listen for DOM events, you need to find out what tagnames you want to find, where you want to find it, etc, it could get ugly if you change some stuff in the HTML layout. JS code reading the DOM can get ugly, but JS writing DOM is cool, and JS entities communicating with each other is also cool. – fejesjoco Jan 27 '14 at 19:57
  • How do you implement the event publishing? So far usually use an array of listeners/observers (which can be either simple callbacks or objects). This works, but it means I need to add an addObserver() method to each component. Is this the recommended way to do stuff? – donquixote Jan 27 '14 at 20:09
  • and for per-row widgets, I suppose I would have one component that is instantiated per-row, and another component that is instantiated per-table and will create and manage the per-row widgets? – donquixote Jan 27 '14 at 20:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.