By Peter Bell

Generic Admin Screens - Who knows what?

I generally start my projects using things like generic list screens, only adding object specific admin lists if a specific object has a unique requirement. So I'll typically have an admin-list.cfm screen template for listing pages, articles, products, users, etc.

The question is how do you pass meta-information that the generic admin screen requires in an elegant way? I have a solution, but I don't love it. Better ideas appreciated . . .

I always pass an IBO (although you could pass a query) to my admin display list. But there is some information that isn't record specific. For instance, what is the name of the type of object I'm listing? What are the titles and the names of the properties I'm displaying, and what kind of things can I do to them (add, edit, delete, approve, send, publish, push-to-staging, implement, transform, import, export, etc.). The question is where that information should go.

For now I'm using the idea of "class properties". So, if you have a list of three products with a title and price for each one, you have three titles and three prices, but you also have a single PropertyNameList (Title,Price) and ObjectName (Product).

For now I've added a classGet() and classSet() method (including optional oMM support) so you can classGet("PropertyNameList") or classGetPropertyNameList() depending on your preference. I don't really like this approach, but I just dislike all the other approaches I've considered to date equally or more.

I don't particularly want to pass separate data and metadata beans, and I've thought seriously about having a metadata bean composed within the IBO so I could just Object.getMetadataBean().getPropertyNameList(). That seems marginally more elegant but I've not yet decided whether the marginal extra elegance warrants the extra complexity (it probably does, but I'm still a little on the fence).

Of course, I could just add the "class" properties to the regular properties and use a simple getPropertyNameList() that's smart enough to realize it's metadata, but then I run into the potential for naming conflicts in the general case - if I ever had an object that actually have a property called PropertyNameList I'd be SOL. I could also gen a separate list screen for each object, hard coding property name lists and the like. This is something I am seriously considering, although the amount of redundant display information seems a little un-DRY.

I could use some kind of strategy pattern for building up the displays, sharing common implementation details and then subclassing or composing different objects for the parts of the template that are object specific, but honestly objects can be a little inelegant as a solution for some kind of display templating problems and getting a sense of what an overall list screen looked like would require jumping between different CFC's.

So, how do you handle this when you want to add enough richness to your generic screens that they aren't just a scaffolding asset but are actually the final code while staying DRY and elegant? Any input appreciated!

Hmm, was just wondering if I should just use annotations on cfcomponent and maybe cfproperty tags and then use getMetadata() to access the annotations. Maybe that'd be a cleaner approach - what do you think? I'm starting to like the sound of that - anyone think of any downsides?

Comments
@Peter,

I think we've butted heads on this before, but as I don't know enough to follow everything you are talking about, so I'll mention it again (feel free to ignore).... but, why should you have to pass the property list to the view? Shouldn't the view know what it is going to be using? Plus, isn't the View's decision as to what to implement. For example, just cause you pass a property named "Price", what's to stop the developer from calling that column, "Cost" and referencing "price" only behind the scenes?

I know that the unwritten rule here is that you are ultimately not worried about MVC, but rather about automation and de-skilling displays.... at least I think that's what your goal is? I guess maybe I am not seeing the goal of what you are trying to accomplish. LIke, I'm trying to do XYZ and this is how I am thinking about doing it?

Sorry, I know I always get confused on your stuff :( I'm just trying to keep up :)
# Posted By Ben Nadel | 7/22/08 11:48 AM
Hey Ben,

Her's what I'm saying. I'm saying I have all of these really similar screens and I'd like to make the code DRYer. Imagine I'm building an admin system for managing Pages, Users, Articles, Products, Events, etc. Most of the admins will probably have some kind of admin list. So the initial solution might be to code page-admin-list.cfm, user-admin-list.cfm, article-admin-list.cfm, etc. Basically one admin list per object. But (a) that takes a while, (b) it doesn't seem very DRY, and (c) if I decide to change my table implementation (say from a spry dataset to a jQuery one) if I have 15 different object types to admin, I have to change fifteen different screens. Now with code generation and templates I could live with this, but wouldn't it be nicer just to have a single admin-list.cfm?

The problem is that to have that, I need to pass the information that varies. Stuff like the name of the object being managed, the TITLES of the properties to display (e.g. "Your Price") and the NAMES of the properties to display (e.g. Cost). I think it is perfectly appropriate for the model to know that the title for the Cost property is "Your Price" as if I have lots of screens that should display "Your Price", it needs a single definitive reference. I'm OK with putting that into the model. I'd also be fairly easily persuadable that if it was a pure view concern it should go into a view decorator (so Page.cfc might be decorated by PageView.cfc that'd have the getTitle() methods and other similar methods). Either way, I need some way to put that metadata and that's the problem I'm trying to solve.

Making any sense?
# Posted By Peter Bell | 7/22/08 12:00 PM
@Peter,

Right, I figured as much. I guess since I am trying to learn more about OOP, I am trying to see more of a divide in my head.

Since you are essentially replacing the "developer" of the page, would it be beneficial to think in that third-party mindset? By that I mean, rather than worry about composing meta data in the IBO or anything, can you pass a secondary object to the view as well that kind of glues them all together.... just as a Developer would have done.

For example:

PageRequest.Products = [your IBO]
PageRequest.Renderer = [your "Developer"]

Then have the Renderer have maybe a key-indexed property table for things onthe page to display.. ex:

PageRequest.Renderer.Modules[ "main_table" ] = [your IBO meta data]

Maybe that seems absurd, but I'm just trying to model the "Real world".
# Posted By Ben Nadel | 7/22/08 12:09 PM
@Ben, Interesting approach. As usual, there are lots of different ways of thinkiing about a problem and each one will suggest a different object model. One way is to think I'm replacing a developer, another is just to look at this information as metadata of the object itself. I certainly can pass a separate bean with the metadata and that's one of the options I'm looking at. Still not sure which approach I like the most though :-)
# Posted By Peter Bell | 7/22/08 12:20 PM
@Peter,

You don't have to be shy about it - you can tell me I'm a few bricks shy of a load ;)
# Posted By Ben Nadel | 7/22/08 12:25 PM
@Ben, not at all. You're describing one of the approaches I was considering (although describing it from a different perspective and a different implementation). If I do ever think you're a few bricks shy, I'll be sure to mention it though :-)
# Posted By Peter Bell | 7/22/08 12:29 PM
Ha ha, fair enough.
# Posted By Ben Nadel | 7/22/08 12:48 PM
For what it's worth, I think having a separate object to handle the metadata is the better way to go. That way, if you ever find yourself in a situation where the metadata needs to be different on different pages (but the underlying model object is the same), you can inject the appropriate metadata object rather than having to have all of the possible metadata options in the main model object.
# Posted By Brian Swartzfager | 7/22/08 1:13 PM
@Brian, I think you're probably right.
# Posted By Peter Bell | 7/22/08 1:39 PM
How about having a display method as part of the object, i.e. product.displayList(), product.dispayDetail() etc.

True that you'd have to have separate methods for each object, so there will be some RY, but I find that I have to have some custom code in the display for each object anyway, since they have different properties, so there is very little actual repetition in the code. And you don't have to pass anything in since the properties are right there for you already -- you can just do #get('price')#, #get('name')# or whatever in your display method.
# Posted By Josh Nathanson | 7/22/08 3:13 PM
@Josh, Interesting. I wouldn't include it as part of the (say) product object, but as part of a ProductView.cfc that was a decorator for the product, that might make some more sense and I did play with that. Definitely something else to play with although funnily enough right now I'm veering towards just genning a list/form per object as it makes the runtime code simpler for more developers to understand . . .
# Posted By Peter Bell | 7/22/08 3:16 PM
Yeah -- since I'm always extending my baseclass which does a lot of the heavy lifting (get/set methods, IBO methods etc.) my actual classes start out empty other than init, so I just drop displayList and displayDetail in each one if needed and they're still pretty lightweight.

I guess that violates MVC, since the view becomes part of the model, but I feel that if you look at from an overall OOP perspective (an object *has* a view or views in the same way that it *has* a certain property or dependency) then it makes sense. Using a decorator like ProductView.cfc would keep things clean and separated if desired.

The views that *should* be separated are what I'd call Layouts which contain the common sitewide views, such as headers/footers and the like. Perhaps this could be part of a Site object, I haven't messed with that concept too much.
# Posted By Josh Nathanson | 7/22/08 3:36 PM
BlogCFC was created by Raymond Camden. This blog is running version 5.005.