ColdBox + jQuery Mobile

I am just thinking out loud here but I am working on plans to overhaul an ecommerce application. The client would like a mobile version and I plan to use jQuery Mobile to build it out. Would this be nothing more than a MobileController with a custom layout? I am not sure the best way to handle this and I would love all you smart people to chime in.

Thanks!

Until the smart people get here, I’ll see what I can do… :slight_smile:

I’m doing the same thing as you are, jQuery Mobile front-ends in addition to a full web version for many applications. There are a couple of ways to handle it, below are some thoughts about the options I’ve considered.

The best method is going to depend on how you structure the application and what kind of data you want to return.

Maybe you want to create a responsive interface by pre-loading the entire client-side / UI by putting it all into one HTML file containing the interface elements. It would load external data via AJAX (JSON or HTML fragments) and even possibly store that data in local storage for caching, offline usage, or other purposes.

Maybe you would rather have individual HTML pages loaded with the jQuery Mobile transitions as they are clicked on instead of having a responsive, pre-loaded UI built in to the client.

Depending on what you do, you could create a separate endpoint route for mobile requests (such as http://www.foo.com/mobile/handler/action) that would always return JSON or a code fragment (you could use an interceptor to further decorate the response), since the mobile application / site would always be making the requests to that endpoint.

Or, you could do an Event.isProxyRequest() in your existing handlers to determine if the request is coming through the ColdBox Proxy, and then return JSON or your code fragment that way. This would allow the handlers to run normally in web requests, but return data to mobile data requests made through the proxy. Then just make calls to your proxy (or even directly to the handlers) from the mobile app. For prettiness, you could make a rewrite rule for http://www.foo.com/mobile to point to your ColdboxProxy.cfc and make the request to the pretty URL from your mobile app, which would send the request through the proxy to your normal handlers, which would see the proxy and return JSON or fragment, as you desire.

If you just wanted page-to-page transitions, then you’d simply make a default layout to load for the mobile site, then load views containing the data using the jQuery Mobile page / load functionality.

You could create a mobile handler, specifically to handle requests, but it’s really not needed if you implement some of what’s already there for you in ColdBox.

Hope this helps, feel free to hit me up about any questions.

Jim

Thanks for the thoughts Jim.

I ended up creating 2 new modules in my project…

/phone
/tablet

Now most people wont have to do this and can probably just create a mobile version. I like having this pulled out into its own module. This you can have your own settings, layouts, handlers, views,etc… My question is where should I handle the detection. In a normal application I would just thrown this in the onRequestStart method but I suspect there is a better way to do it here. I want to detect if they are coming from a smart phone and push them into /phone and from a table into /tablet. I also registered 2 new routes

addModuleRoutes(pattern="/phone",module=“Phone”);

addModuleRoutes(pattern="/tablet",module=“Tablet”);

Dan,

You shouldn’t need to add those module routes. The modules automatically get a route that matches your module name.

Anyway, that aside, you could probably create an interceptor that detects the user agent and adjusts the route appropriately according to the user agent that was detected.

Stephen

Well the best thing to do is think about it some more!!!

Don’t be frightened to look at the things that are at your disposal, for example what you could do is create another directory structure that is /mobile or /tablet or /shitdevice and then use an interceptor with the open source WURFL that could detect the device and modify the conventions on the fly.

Most people don’t know that the user conventions override the normal conventions, that means you could create a directory and detect the skin system, and change all this based on the device in question if need be.

I did something similar for an Application that was to reuse the core application, and modules and then based on the domain name would then change the conventions to the user based depending on skin. If the user convention did not exist by convention it would then default to the standard convention, all of which I have posted in this mailing list once before.

Sorry I need to rephrase, when I meant use conventions I actually meant external conventions.

Now I am picking up what your laying down ;D I will give this a try tonight.

COOL!!

I love event driven programming!! NERD ALERT!

Luis F. Majano
CEO
Ortus Solutions, Corp
www.ortussolutions.com

ColdBox Platform: http://www.coldbox.org
Linked In: http://www.linkedin.com/pub/3/731/483
Blog: http://www.luismajano.com
IECFUG Manager: http://www.iecfug.com

Social: twitter.com/lmajano facebook.com/lmajano

here is another quick question… someone mentioned that I didn’t need to add a route for the mobile module if I wanted the route to be /mobile.

This is what my module config looks like

routes = [
{pattern="/", handler=“main”,action=“index”}
];

If I don’t put in a route in the main ColdBox application and go to http://localhost/cbmobile/mobile then my onInvalidEvent exception is thrown in my main app. However if I go to /mobile/main then it works fine, what gives?

If I add this module route everything works as expected ?
addModuleRoutes(pattern="/mobile",module=“Mobile”);

Have you setup the entry point in the moduleConfig.cfc?

I took that out… I could of sworn Luis said he was removing the need for that

lol, I am not using CB3.5 as yet… But I am sure it is required for the system to know the entry point of the module, for the routes defined in moduleConfig to work. Someone else might correct me if this has changed in 3.5 though.

OK, Almost got this working. In my moduleConfig I have a preProcess method that calls a isMobile() function. For testing purposes I am forcing the agent but I am stuck in an endless loop. I need a way to say if the device isMobile() and we are not already in the mobile module, next the next event as mobile home. Any idea how to do that?

public function preProcess(event,interceptData) {

if( isMobile() && “we are not already in the /mobile module” ){
setNextEvent(event=‘mobile’);
}

}

private Boolean function isMobile(){
var isMobile = false;
//var userAgent = cgi.http_user_agent;

// force agent for testing
var userAgent = “android”;

if (reFindNoCase(“android|avantgo|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)/|plucker|pocket|psp|symbian|treo|up.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino”,userAgent) GT 0 OR reFindNoCase(“1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|)|g1 u|g560|gene|gf-5|g-mo|go(.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| ||a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|/(k|l|u)|50|54|e-|e/|-[a-w])|libw|lynx|m1-w|m3ga|m50/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|xda(-|2|g)|yas-|your|zeto|zte-”,Left(userAgent,4)) GT 0) {
isMobile = true;
}

return isMobile;
}

never mind, we have a winner folks!

public function preProcess(event,interceptData) {
var module = arguments.event.getCurrentModule();

if( isMobile() && module != ‘Mobile’ ){
controller.setNextEvent(event=‘mobile’);
}

}

private Boolean function isMobile(){
var isMobile = false;
//var userAgent = cgi.http_user_agent;

// force agent for testing
var userAgent = “android”;

if (reFindNoCase(“android|avantgo|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)/|plucker|pocket|psp|symbian|treo|up.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino”,userAgent) GT 0 OR reFindNoCase(“1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|)|g1 u|g560|gene|gf-5|g-mo|go(.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| ||a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|/(k|l|u)|50|54|e-|e/|-[a-w])|libw|lynx|m1-w|m3ga|m50/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|xda(-|2|g)|yas-|your|zeto|zte-”,Left(userAgent,4)) GT 0) {
isMobile = true;
}

return isMobile;
}

If you don’t have an entry point in your moduleConfig.cfc then you will need to go the addModuleRoutes() route and manually add in your modules.

If you have an entry point in your moduleConfig.cfc then a route will automagically be added for that module.

This is something I’ve recently learnt myself… (see my thread about ses routes)

Stephen