[coldbox-3.1.0] Structuring nested views with coldbox

My first attempt of structuring a coldbox handler for nested pages.
My “viewCategoryProductList” event calls other methods within the handler which generate HTML (first they call methods to load the required data into the event)
The HTML is then added to the event and finally at the end rendered into a layout.
I have tried to make each HTML generation method reusable so that multiple templates can use them in the future.

Please take a look at let me know if there is a better way of achieving this.

Hopefully I have not going down the completely wrong route!

Thanks very much

Alex

public function viewCategoryProductList(event,rc,prc,numeric intSelectedCategoryID){

//Announce interception
announceInterception(‘DisplayCategoryProducts’);

//Bread crumb
event.setValue(‘strBreadCrumbTrailHTML’, getBreadCrumbHTML(event,rc,prc));

//setProductSearchHTML
event.setValue(‘strProductSearchHTML’, getProductSearchHTML(event,rc,prc));

//Get the sub category HTML
event.setValue(‘strSubCategoryHTML’, getSubCategoryHTML(event,rc,prc));

//Set the product list to be the main view
setProductListData(event,rc,prc);
event.setView(‘coldbox/productlist/productlist’);

//Bread crumb
event.setLayout(‘category/categorylist’);
}

public function setProductSearchData(event,rc,prc){
//Get root node
event.setValue(‘rsActiveSuppliers’, objSupplierService.getActiveUnBlockedSuppliers());
//Get the root parent sub categories
event.paramValue(‘intSelectedCategoryID’, variables.objCategoryService.getRootNode());
local.intSelectedCategoryID = event.getValue(‘intSelectedCategoryID’);
event.paramValue(‘blnGetSubCategoryChildren’, 0);
local.blnGetSubCategoryChildren = event.getValue(‘blnGetSubCategoryChildren’);
event.setValue(‘objCategory’, variables.objCategoryService.getCategoryWithImmediateSubCategories(
intParentCategoryID = local.intSelectedCategoryID
, blnGetSubCategoryChildren = local.blnGetSubCategoryChildren
));

}

public string function getProductSearchHTML(event,rc,prc){
setProductSearchData();
return renderView(‘coldbox/productlist/search’);
}

public function setBreadCrumbData(event,rc,prc){
event.paramValue(‘intSelectedCategoryID’, variables.objCategoryService.getRootNode());
local.intSelectedCategoryID = event.getValue(‘intSelectedCategoryID’);
local.objProductCategeoryLegacyCFC = new epsys.legacy.cfcs.product_cats();
local.stuBreadCrumbTrail = objProductCategeoryLegacyCFC.getBreadCrumb(local.intSelectedCategoryID);
event.setValue(‘stuBreadCrumbTrail’, local.stuBreadCrumbTrail );
}

public string function getBreadCrumbHTML(event,rc,prc){
setBreadCrumbData();
return renderView(‘coldbox/productlist/breadcrumbtrail’);
}

public string function setSubCategoryData(event,rc,prc){
local.intSelectedCategoryID = event.getValue(‘intSelectedCategoryID’, variables.objCategoryService.getRootNode());
//Get the sub categories based on the selected category id
local.stuCategoryHolder = variables.objCategoryService.getParentSubCategoriesForOutput(
intParentID=local.intSelectedCategoryID
);
event.setValue(‘stuCategoryHolder’, local.stuCategoryHolder);
event.setValue(‘strSubCategoryHTML’, renderView(‘coldbox/category/subcategories’));
}

public string function getSubCategoryHTML(event,rc,prc){
setSubCategoryData();
return renderView(‘coldbox/category/subcategories’);
}

public string function setProductListData(event,rc,prc){

}

The term viewlet is often used for a “nested” view that is self-contained and reusable. What you have will work fine, but I usually push more of the logic down into the view. This keeps more markup logic in the view layer (I consider layouts a type of “view”) and clears up the request collection so it doesn’t get filled with HTML strings.

It usually goes like this:

  1. If you have a “dumb” view that needs no data, just call #renderView( ‘viewName’ )# wherever you need it in any other view or layout
  2. If you have a dynamic view that needs data, create an event (like you have done) but instead of dumping the data for the view into rc, pass it directly in via the args parameter of renderView. This keeps the request collection clear
  3. Return the HTML from the renderView directly.
  4. In your view, access incoming data via the same args param
  5. In any view or layout that needs the viewlet, just do #runEvent( ‘event.name’ )# to spit out the HTML
  6. If you want to pass local info to runEvent() use the eventArguments param

Event
function news( event, rc, prc ) {
local.dataForView = service.getData();
return renderView(“news”, args={data = local.dataForView });
}

View


Other view or layout
#runEvent( ‘handlerName.news’ )#

Thanks!

~Brad

ColdBox Platform Evangelist
Ortus Solutions, Corp

E-mail: brad@coldbox.org
ColdBox Platform: http://www.coldbox.org
Blog: http://www.codersrevolution.com

Thanks for this Brad!

All of what you have said makes sense… My rational for separating the data setting from the HTML generation is that I may have multiple views for the same group of data.

So id have

public string function setTheData(event,rc,prc){
event.setValue(‘testData1’, variables.objMyService.getData1());
event.setValue(‘testData2’, variables.objMyService.getData2());
event.setValue(‘testData3’, variables.objMyService.getData3());
}

To set the data in the rc… Then multiple getHTML methods could use that data… Avoiding code repetition.

public string function getHTML1(event,rc,prc){
setTheData(event,rc,prc);

return renderView(‘testView1’);
}

public string function getHTML2(event,rc,prc){
setTheData(event,rc,prc);

return renderView(‘testView2’);
}

etc…

Apart from clarity are there any other reasons why I would want to keep my rc clean? Such as performance?

Thanks very much for taking the time to explain how you handle this.

Alex

Either way… i think putting your getMyHTML methods directly into views / layouts makes much more sense than putting them into the RC first!

I will be doing this from now on!

> Apart from clarity are there any other reasons why I would want to keep my rc clean? Such as performance?

If you use the ColdBox debugger, it dumps out the contents of your rc which can really bog down if there’s a lot of stuff.

But mostly, it’s just for organization. Throwing everything in the rc is bad practice since there’s no encapsulation. It’s kind of like the old anti-pattern of just putting all your data in the request scope.

Other times, you might find a bit of HTML that you want to include multiple times on the same page-- such as an attachment upload form with different data each time. If you’re dumping everything in the rc, the variables will all be overwriting each other. You also might have variable collisions between two views that different programmers wrote who reused the same variable names. It just makes for much cleaner app development to encapsulate the data instead of throwing it all in a shared space.

Thanks!

~Brad

ColdBox Platform Evangelist
Ortus Solutions, Corp

E-mail: brad@coldbox.org
ColdBox Platform: http://www.coldbox.org
Blog: http://www.codersrevolution.com

Thanks brad…

I think the solution to my problem here is going to AOP …

Using AOP should allow me to avoid populating the rc with my data for the views.

Will get my hands dirty and see how I get on

I think AOP is over kill for views, using an runEvent() inside your handler is much better.

What Brad means is that you don’t do something like this.

function myHandler(event, rc, prc) {
rc.results = someService.getData();

renderView()

}

Instead what he is suggesting is that you write it like this.

function myHandler(event, rc, prc) {
renderView(view=“myView”, args = someService.getData());

}

or

function myHandler(event, rc, prc) {
var results = someService.getData();

renderView(view=“myView”, args = results);

}

But in your view you could also do

runEvent(event=arguments.event, eventArguments=eventArguments, private=arguments.private);

That is the preffered way, if you want to encapsulate the variables into that view.

Yes, Andrew clarified well what I was trying to say. Thank you.

I also don’t want to deter you from a solution that works-- just providing food for thought since I’ve been down the road many times and have run into those sort of things.

Thanks!

~Brad

ColdBox Platform Evangelist
Ortus Solutions, Corp

E-mail: brad@coldbox.org
ColdBox Platform: http://www.coldbox.org
Blog: http://www.codersrevolution.com

Yes… Decided against aop for views… Not sure what i was thinking there! Thanks guys

I did mean in your views not your handler, so I hope I don’t get you further confused by that.