Proof of Concept - Component based rendering
Here is an *extremely* rough proof of concept that I whipped up in about an hour. Will be playing with this tomorrow, but any thoughts much appreciated . . .
I'd really like to allow my designers to be able to create a page template that looks like this:
Header<br />
#Display.ContentArea("MainContent")#<br />
Footer<br />
</cfoutput>
They could define and name multiple content areas and just have them taken care of with separate templates for each content area (actually there would be object templates for listing, viewing, etc. and the appropriate object template for a given content area would be decided by the controller).
I also see how a similar approach would allow someone to put #Display.paging()# at the top of a table for listing records so they didn't have to put the ugly concatenation of the paging links directly into the template, and then they could create custom list.cfc's to extend baselist.cfc if they wanted different paging code for a given list or could just pass a different template if they wanted a different look and feel but the regular code.
So, starting with the idea of the page composed of content areas, I put together the following component. Trivial, but I just like the idiom it allows in the templates and I've got a feeling this could be kinda cool.
Obviously the content area code would call the appropriate content area controller which would return the screen name and the object data to provide to it - I'll drop that in shortly.
Thoughts anyone?
<cffunction name="init" output="false" returntype="any">
<cfargument name="ApplicationName" required="yes">
<cfscript>
Display = THIS;
variables.ApplicationName = arguments.ApplicationName;
</cfscript>
<cfreturn THIS>
</cffunction>
<cffunction name="Page" output="true" returntype="void">
<cfinclude template="/#ApplicationName#/layout/page/default-page.cfm">
</cffunction>
<cffunction name="ContentArea" output="true" returntype="void">
<cfargument name="ContentArea" required="true" type="string">
<cfscript>
Display.Screen("default-list");
</cfscript>
</cffunction>
<cffunction name="Screen" output="true" returntype="void">
<cfargument name="ScreenName" required="true" type="string">
<cfinclude template="/#ApplicationName#/layout/object/#ScreenName#.cfm">
</cffunction>
</cfcomponent>





1) I winced when I saw ApplicationName being passed into the component. I thought, "why should render need to know anything about the application, especially its name?" Of course, I see why you did it. At least I might want to change the name to "ApplicationRoot" or something more descriptive of what purpose it is serving. On the other hand, I don't really know of a better solution, but I'd still seek one (in fact, I'm passively looking for it!)
2) I don't understand why you decided to do Display = this. Is it just to make the rest of the code more easily understood?
3) Is the ContentArea function correct? You don't seem to ever be using the argument.
4) "I love the idea of using cfc's to abstract the calculations from templates while still using cfincludes for the pure templating." -- I do too.
Many thanks for the feedback!
LightBase is a central core of code, so it needs some way to find its way back to the application it is working with. I can live with that but am open to better approaches!
Yeah - Display = this is just pure syntactic sugar for graphic designers who'll be creating the templates. When I tell them to write #Display.ContentArea("RightSideBar")# that is fairly easy for them to get and read even without knowing anything about programming as it reads pretty much like English.
The ContentArea function is called by the template. Look at the template at top of page. Basically it calls content area every time there is a chunk of dynamic code to be inserted. Of course, insead of just including a standard screen, it would call the controller for that content are which would return (say) a product collection and a screen for displaying a list of product specials in the right hand sidebar.
Glad you like the core concept. I'll keep playing and let you know what I come up with. Any ideas/thoughts/musings/ponderings also gratefully accepted!
To clarify, I see how the contentArea function is intended to be used ... but, I don't understand how it works given its implementation. =)
Now, I don't know if you've done some pre-processing on the code or not, basically. But, the only line in that function is:
Display.Screen("default-list");
So, the argument appears to never get used. I had a feeling "default-list" was intended to be the default argument, and that it should be replaced with arguments.contentArea, but I wanted to ask to be sure. Of course, it may be I just can't see it.
var Response = ContentAreaFacade.getContent("RightHandSidebar");
Display.Screen(Response.ScreenName,Response.ObjectData);
Make any more sense?
If the function is written correctly, why not remove the argument, as it is never used? (or, when you said this is a mock function, did you mean that it is not yet complete and will use the argument in its final form?)
I think I understand the intent though, so I can leave it at that and be happy. =)
In any case, sorry for being so pedantic over this.
When I said it is a mock, I mean I have just thrown something into it to make it display something as proof of concept. It is kind of like when you write Object.Multiply(value1,value2) to always return 10 - clearly it should really do something smarter (multiple value 1 and value 2), but for initial test case (assuming values of 2 and 5), your test runs green. Then you add another test case (say 2 x 3), it goes red and then you refactor to get both tests green. Current contentarea funciton is equivaluent of multiple being coded as "return 10;".
Will show you something more complete tonight as mocking out whole framework today. I feel like I am falling off of the map a little as there are a bunch of things I'm doing where I'm breaking MVC, but there is a good reason in each case and I think the choices make sense . . . will post concepts and code tonight and see what you and the rest of the crew think!
Thing I did differently was to have component "CoreWidget" which is always extended by actual widget. Simple example would be "NewsList".
CoreWidget has methods "setTemplate" and "render" and when render is called in NewsList is has sufficient knowledge to get news items from database (trough DAO/Gateway) and include template speficied by setTemplate.
Whole thing is wired together and configured with ColdSpring.
What I'm trying to archive here is to have set of widgets that can be dropped into content regions of a site and customized with a simple template.
Hopefully this makes some sense, at least proof of concept code is working pretty much way I hoped.
Makes sense. I have some more layers of abstraction. To me a page is comprised of 0..n dynamic content areas. Each content area (main, right hard sidebar, etc.) has its own controller. Those controllers simply say what feature to use (PageDisplay, Catalog, etc.) for easy reuse of features. The feature (which is what would be a traditional controller in MG or M2) then calls object controllers in the appropriate manner so you can reuse standard controller functions like list, add, edit and delete while also extending or overloading for any given object.
The object controllers call the object service layer which then uses the DAO to get the data - phew!
Sounds like a lot of levels of indirection, but for my generator it gives a great deal of flexibility and reuse and is quite easy to work with once you "get" the concepts and makes it easy to create and manage pretty complex business rules for what content should go where for any given page request.
I have been given the task of coming up with a new framework (in the loosest sense of the word!) for our existing website.
The site has over 100 'skins' / partner sites that generally have their own headers, footers etc but the same content (with different css and small variations in content / multiple languages etc).
However, the content panel in the new design will consist of an x by y grid where 'widgets' or modules can be slotted in ... so on the Job List page for partner site 1 in the main content panel we will have the job listings widget then a comments widget and a poll widget, partner site 2 will have the job list and then just the poll.
At the moment there's a lot of business logic in the views / cfm pages that do lots of conditional checks ie <cfif request.partner neq "47">... show comments module ... </cfif>
You can imagine how difficult this has become with 100's of pages and over 100 versions of the site.
It's also a nightmare for the css guy to make changes to cfm pages where there is way too much CF code. We're also using the project to seperate our domain model from the views as much as possible with the available time.
My initial idea is to have DisplayPage object composed of several Widget objects. The url is used to work out which page we need and which 'partner' site we're using. Then the objects can be created. The display widgets will be simple cfm files that are cfincluded.
Now I can see your approach, I may 'borrow' the idea and run with it. Have you done much with this recently as I can see the post is from several months back?
Alan